s3-libads: Add a function to retrieve the SPNs of a computer account.
[Samba.git] / source3 / libads / ldap.c
blobc683e2c530a98c739ccc6dbb5f1394086003b72f
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;
542 const char *dns_hosts_file;
544 if (!realm)
545 realm = lp_realm();
547 if ((sitename = sitename_fetch(frame, realm)) == NULL) {
548 ads_lookup_site();
549 sitename = sitename_fetch(frame, realm);
552 dns_hosts_file = lp_parm_const_string(-1, "resolv", "host file", NULL);
553 do {
554 /* We try once with a sitename and once without
555 (unless we don't have a sitename and then we're
556 done */
558 if (sitename == NULL)
559 done = true;
561 nt_status = ads_dns_query_gcs(frame, dns_hosts_file,
562 realm, sitename,
563 &gcs_list, &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 const void **invals = (const void **)_invals;
1440 int curmod;
1441 LDAPMod **modlist = (LDAPMod **) *mods;
1442 struct berval **ber_values = NULL;
1443 char **char_values = NULL;
1445 if (!invals) {
1446 mod_op = LDAP_MOD_DELETE;
1447 } else {
1448 if (mod_op & LDAP_MOD_BVALUES)
1449 ber_values = ads_dup_values(ctx,
1450 (const struct berval **)invals);
1451 else
1452 char_values = ads_push_strvals(ctx,
1453 (const char **) invals);
1456 /* find the first empty slot */
1457 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1458 curmod++);
1459 if (modlist[curmod] == (LDAPMod *) -1) {
1460 if (!(modlist = talloc_realloc(ctx, modlist, LDAPMod *,
1461 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1462 return ADS_ERROR(LDAP_NO_MEMORY);
1463 memset(&modlist[curmod], 0,
1464 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1465 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1466 *mods = (ADS_MODLIST)modlist;
1469 if (!(modlist[curmod] = talloc_zero(ctx, LDAPMod)))
1470 return ADS_ERROR(LDAP_NO_MEMORY);
1471 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1472 if (mod_op & LDAP_MOD_BVALUES) {
1473 modlist[curmod]->mod_bvalues = ber_values;
1474 } else if (mod_op & LDAP_MOD_DELETE) {
1475 modlist[curmod]->mod_values = NULL;
1476 } else {
1477 modlist[curmod]->mod_values = char_values;
1480 modlist[curmod]->mod_op = mod_op;
1481 return ADS_ERROR(LDAP_SUCCESS);
1485 * Add a single string value to a mod list
1486 * @param ctx An initialized TALLOC_CTX
1487 * @param mods An initialized ADS_MODLIST
1488 * @param name The attribute name to add
1489 * @param val The value to add - NULL means DELETE
1490 * @return ADS STATUS indicating success of add
1492 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1493 const char *name, const char *val)
1495 const char *values[2];
1497 values[0] = val;
1498 values[1] = NULL;
1500 if (!val)
1501 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1502 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1506 * Add an array of string values to a mod list
1507 * @param ctx An initialized TALLOC_CTX
1508 * @param mods An initialized ADS_MODLIST
1509 * @param name The attribute name to add
1510 * @param vals The array of string values to add - NULL means DELETE
1511 * @return ADS STATUS indicating success of add
1513 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1514 const char *name, const char **vals)
1516 if (!vals)
1517 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1518 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1519 name, (const void **) vals);
1522 #if 0
1524 * Add a single ber-encoded value to a mod list
1525 * @param ctx An initialized TALLOC_CTX
1526 * @param mods An initialized ADS_MODLIST
1527 * @param name The attribute name to add
1528 * @param val The value to add - NULL means DELETE
1529 * @return ADS STATUS indicating success of add
1531 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1532 const char *name, const struct berval *val)
1534 const struct berval *values[2];
1536 values[0] = val;
1537 values[1] = NULL;
1538 if (!val)
1539 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1540 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1541 name, (const void **) values);
1543 #endif
1546 * Perform an ldap modify
1547 * @param ads connection to ads server
1548 * @param mod_dn DistinguishedName to modify
1549 * @param mods list of modifications to perform
1550 * @return status of modify
1552 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1554 int ret,i;
1555 char *utf8_dn = NULL;
1556 size_t converted_size;
1558 this control is needed to modify that contains a currently
1559 non-existent attribute (but allowable for the object) to run
1561 LDAPControl PermitModify = {
1562 discard_const_p(char, ADS_PERMIT_MODIFY_OID),
1563 {0, NULL},
1564 (char) 1};
1565 LDAPControl *controls[2];
1567 controls[0] = &PermitModify;
1568 controls[1] = NULL;
1570 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, mod_dn, &converted_size)) {
1571 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1574 /* find the end of the list, marked by NULL or -1 */
1575 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1576 /* make sure the end of the list is NULL */
1577 mods[i] = NULL;
1578 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1579 (LDAPMod **) mods, controls, NULL);
1580 TALLOC_FREE(utf8_dn);
1581 return ADS_ERROR(ret);
1585 * Perform an ldap add
1586 * @param ads connection to ads server
1587 * @param new_dn DistinguishedName to add
1588 * @param mods list of attributes and values for DN
1589 * @return status of add
1591 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1593 int ret, i;
1594 char *utf8_dn = NULL;
1595 size_t converted_size;
1597 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, new_dn, &converted_size)) {
1598 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!"));
1599 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1602 /* find the end of the list, marked by NULL or -1 */
1603 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1604 /* make sure the end of the list is NULL */
1605 mods[i] = NULL;
1607 ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1608 TALLOC_FREE(utf8_dn);
1609 return ADS_ERROR(ret);
1613 * Delete a DistinguishedName
1614 * @param ads connection to ads server
1615 * @param new_dn DistinguishedName to delete
1616 * @return status of delete
1618 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1620 int ret;
1621 char *utf8_dn = NULL;
1622 size_t converted_size;
1623 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, del_dn, &converted_size)) {
1624 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!"));
1625 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1628 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1629 TALLOC_FREE(utf8_dn);
1630 return ADS_ERROR(ret);
1634 * Build an org unit string
1635 * if org unit is Computers or blank then assume a container, otherwise
1636 * assume a / separated list of organisational units.
1637 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1638 * @param ads connection to ads server
1639 * @param org_unit Organizational unit
1640 * @return org unit string - caller must free
1642 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1644 char *ret = NULL;
1646 if (!org_unit || !*org_unit) {
1648 ret = ads_default_ou_string(ads, DS_GUID_COMPUTERS_CONTAINER);
1650 /* samba4 might not yet respond to a wellknownobject-query */
1651 return ret ? ret : SMB_STRDUP("cn=Computers");
1654 if (strequal(org_unit, "Computers")) {
1655 return SMB_STRDUP("cn=Computers");
1658 /* jmcd: removed "\\" from the separation chars, because it is
1659 needed as an escape for chars like '#' which are valid in an
1660 OU name */
1661 return ads_build_path(org_unit, "/", "ou=", 1);
1665 * Get a org unit string for a well-known GUID
1666 * @param ads connection to ads server
1667 * @param wknguid Well known GUID
1668 * @return org unit string - caller must free
1670 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1672 ADS_STATUS status;
1673 LDAPMessage *res = NULL;
1674 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1675 **bind_dn_exp = NULL;
1676 const char *attrs[] = {"distinguishedName", NULL};
1677 int new_ln, wkn_ln, bind_ln, i;
1679 if (wknguid == NULL) {
1680 return NULL;
1683 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1684 DEBUG(1, ("asprintf failed!\n"));
1685 return NULL;
1688 status = ads_search_dn(ads, &res, base, attrs);
1689 if (!ADS_ERR_OK(status)) {
1690 DEBUG(1,("Failed while searching for: %s\n", base));
1691 goto out;
1694 if (ads_count_replies(ads, res) != 1) {
1695 goto out;
1698 /* substitute the bind-path from the well-known-guid-search result */
1699 wkn_dn = ads_get_dn(ads, talloc_tos(), res);
1700 if (!wkn_dn) {
1701 goto out;
1704 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1705 if (!wkn_dn_exp) {
1706 goto out;
1709 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1710 if (!bind_dn_exp) {
1711 goto out;
1714 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1716 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1719 new_ln = wkn_ln - bind_ln;
1721 ret = SMB_STRDUP(wkn_dn_exp[0]);
1722 if (!ret) {
1723 goto out;
1726 for (i=1; i < new_ln; i++) {
1727 char *s = NULL;
1729 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1730 SAFE_FREE(ret);
1731 goto out;
1734 SAFE_FREE(ret);
1735 ret = SMB_STRDUP(s);
1736 free(s);
1737 if (!ret) {
1738 goto out;
1742 out:
1743 SAFE_FREE(base);
1744 ads_msgfree(ads, res);
1745 TALLOC_FREE(wkn_dn);
1746 if (wkn_dn_exp) {
1747 ldap_value_free(wkn_dn_exp);
1749 if (bind_dn_exp) {
1750 ldap_value_free(bind_dn_exp);
1753 return ret;
1757 * Adds (appends) an item to an attribute array, rather then
1758 * replacing the whole list
1759 * @param ctx An initialized TALLOC_CTX
1760 * @param mods An initialized ADS_MODLIST
1761 * @param name name of the ldap attribute to append to
1762 * @param vals an array of values to add
1763 * @return status of addition
1766 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1767 const char *name, const char **vals)
1769 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1770 (const void *) vals);
1774 * Determines the an account's current KVNO via an LDAP lookup
1775 * @param ads An initialized ADS_STRUCT
1776 * @param account_name the NT samaccountname.
1777 * @return the kvno for the account, or -1 in case of a failure.
1780 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
1782 LDAPMessage *res = NULL;
1783 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1784 char *filter;
1785 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1786 char *dn_string = NULL;
1787 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1789 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
1790 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
1791 return kvno;
1793 ret = ads_search(ads, &res, filter, attrs);
1794 SAFE_FREE(filter);
1795 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
1796 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
1797 ads_msgfree(ads, res);
1798 return kvno;
1801 dn_string = ads_get_dn(ads, talloc_tos(), res);
1802 if (!dn_string) {
1803 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1804 ads_msgfree(ads, res);
1805 return kvno;
1807 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1808 TALLOC_FREE(dn_string);
1810 /* ---------------------------------------------------------
1811 * 0 is returned as a default KVNO from this point on...
1812 * This is done because Windows 2000 does not support key
1813 * version numbers. Chances are that a failure in the next
1814 * step is simply due to Windows 2000 being used for a
1815 * domain controller. */
1816 kvno = 0;
1818 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1819 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1820 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1821 ads_msgfree(ads, res);
1822 return kvno;
1825 /* Success */
1826 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1827 ads_msgfree(ads, res);
1828 return kvno;
1832 * Determines the computer account's current KVNO via an LDAP lookup
1833 * @param ads An initialized ADS_STRUCT
1834 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1835 * @return the kvno for the computer account, or -1 in case of a failure.
1838 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
1840 char *computer_account = NULL;
1841 uint32_t kvno = -1;
1843 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
1844 return kvno;
1847 kvno = ads_get_kvno(ads, computer_account);
1848 free(computer_account);
1850 return kvno;
1854 * This clears out all registered spn's for a given hostname
1855 * @param ads An initilaized ADS_STRUCT
1856 * @param machine_name the NetBIOS name of the computer.
1857 * @return 0 upon success, non-zero otherwise.
1860 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1862 TALLOC_CTX *ctx;
1863 LDAPMessage *res = NULL;
1864 ADS_MODLIST mods;
1865 const char *servicePrincipalName[1] = {NULL};
1866 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1867 char *dn_string = NULL;
1869 ret = ads_find_machine_acct(ads, &res, machine_name);
1870 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1871 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1872 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1873 ads_msgfree(ads, res);
1874 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1877 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1878 ctx = talloc_init("ads_clear_service_principal_names");
1879 if (!ctx) {
1880 ads_msgfree(ads, res);
1881 return ADS_ERROR(LDAP_NO_MEMORY);
1884 if (!(mods = ads_init_mods(ctx))) {
1885 talloc_destroy(ctx);
1886 ads_msgfree(ads, res);
1887 return ADS_ERROR(LDAP_NO_MEMORY);
1889 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1890 if (!ADS_ERR_OK(ret)) {
1891 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1892 ads_msgfree(ads, res);
1893 talloc_destroy(ctx);
1894 return ret;
1896 dn_string = ads_get_dn(ads, talloc_tos(), res);
1897 if (!dn_string) {
1898 talloc_destroy(ctx);
1899 ads_msgfree(ads, res);
1900 return ADS_ERROR(LDAP_NO_MEMORY);
1902 ret = ads_gen_mod(ads, dn_string, mods);
1903 TALLOC_FREE(dn_string);
1904 if (!ADS_ERR_OK(ret)) {
1905 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1906 machine_name));
1907 ads_msgfree(ads, res);
1908 talloc_destroy(ctx);
1909 return ret;
1912 ads_msgfree(ads, res);
1913 talloc_destroy(ctx);
1914 return ret;
1918 * @brief This gets the service principal names of an existing computer account.
1920 * @param[in] mem_ctx The memory context to use to allocate the spn array.
1922 * @param[in] ads The ADS context to use.
1924 * @param[in] machine_name The NetBIOS name of the computer, which is used to
1925 * identify the computer account.
1927 * @param[in] spn_array A pointer to store the array for SPNs.
1929 * @param[in] num_spns The number of principals stored in the array.
1931 * @return 0 on success, or a ADS error if a failure occured.
1933 ADS_STATUS ads_get_service_principal_names(TALLOC_CTX *mem_ctx,
1934 ADS_STRUCT *ads,
1935 const char *machine_name,
1936 char ***spn_array,
1937 size_t *num_spns)
1939 ADS_STATUS status;
1940 LDAPMessage *res = NULL;
1941 char *dn;
1942 int count;
1944 status = ads_find_machine_acct(ads,
1945 &res,
1946 machine_name);
1947 if (!ADS_ERR_OK(status)) {
1948 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
1949 machine_name));
1950 return status;
1953 count = ads_count_replies(ads, res);
1954 if (count != 1) {
1955 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1956 goto done;
1959 dn = ads_get_dn(ads, mem_ctx, res);
1960 if (dn == NULL) {
1961 status = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
1962 goto done;
1965 *spn_array = ads_pull_strings(ads,
1966 mem_ctx,
1967 res,
1968 "servicePrincipalName",
1969 num_spns);
1971 done:
1972 ads_msgfree(ads, res);
1974 return status;
1978 * This adds a service principal name to an existing computer account
1979 * (found by hostname) in AD.
1980 * @param ads An initialized ADS_STRUCT
1981 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1982 * @param my_fqdn The fully qualified DNS name of the machine
1983 * @param spn A string of the service principal to add, i.e. 'host'
1984 * @return 0 upon sucess, or non-zero if a failure occurs
1987 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1988 const char *my_fqdn, const char *spn)
1990 ADS_STATUS ret;
1991 TALLOC_CTX *ctx;
1992 LDAPMessage *res = NULL;
1993 char *psp1, *psp2;
1994 ADS_MODLIST mods;
1995 char *dn_string = NULL;
1996 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1998 ret = ads_find_machine_acct(ads, &res, machine_name);
1999 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
2000 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
2001 machine_name));
2002 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
2003 spn, machine_name, ads->config.realm));
2004 ads_msgfree(ads, res);
2005 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2008 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
2009 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
2010 ads_msgfree(ads, res);
2011 return ADS_ERROR(LDAP_NO_MEMORY);
2014 /* add short name spn */
2016 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
2017 talloc_destroy(ctx);
2018 ads_msgfree(ads, res);
2019 return ADS_ERROR(LDAP_NO_MEMORY);
2021 if (!strlower_m(&psp1[strlen(spn) + 1])) {
2022 ret = ADS_ERROR(LDAP_NO_MEMORY);
2023 goto out;
2025 servicePrincipalName[0] = psp1;
2027 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
2028 psp1, machine_name));
2031 /* add fully qualified spn */
2033 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
2034 ret = ADS_ERROR(LDAP_NO_MEMORY);
2035 goto out;
2037 if (!strlower_m(&psp2[strlen(spn) + 1])) {
2038 ret = ADS_ERROR(LDAP_NO_MEMORY);
2039 goto out;
2041 servicePrincipalName[1] = psp2;
2043 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
2044 psp2, machine_name));
2046 if ( (mods = ads_init_mods(ctx)) == NULL ) {
2047 ret = ADS_ERROR(LDAP_NO_MEMORY);
2048 goto out;
2051 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
2052 if (!ADS_ERR_OK(ret)) {
2053 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2054 goto out;
2057 if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
2058 ret = ADS_ERROR(LDAP_NO_MEMORY);
2059 goto out;
2062 ret = ads_gen_mod(ads, dn_string, mods);
2063 if (!ADS_ERR_OK(ret)) {
2064 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2065 goto out;
2068 out:
2069 TALLOC_FREE( ctx );
2070 ads_msgfree(ads, res);
2071 return ret;
2075 * adds a machine account to the ADS server
2076 * @param ads An intialized ADS_STRUCT
2077 * @param machine_name - the NetBIOS machine name of this account.
2078 * @param account_type A number indicating the type of account to create
2079 * @param org_unit The LDAP path in which to place this account
2080 * @return 0 upon success, or non-zero otherwise
2083 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2084 const char *org_unit)
2086 ADS_STATUS ret;
2087 char *samAccountName, *controlstr;
2088 TALLOC_CTX *ctx;
2089 ADS_MODLIST mods;
2090 char *machine_escaped = NULL;
2091 char *new_dn;
2092 const char *objectClass[] = {"top", "person", "organizationalPerson",
2093 "user", "computer", NULL};
2094 LDAPMessage *res = NULL;
2095 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
2096 UF_DONT_EXPIRE_PASSWD |\
2097 UF_ACCOUNTDISABLE );
2099 if (!(ctx = talloc_init("ads_add_machine_acct")))
2100 return ADS_ERROR(LDAP_NO_MEMORY);
2102 ret = ADS_ERROR(LDAP_NO_MEMORY);
2104 machine_escaped = escape_rdn_val_string_alloc(machine_name);
2105 if (!machine_escaped) {
2106 goto done;
2109 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
2110 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
2112 if ( !new_dn || !samAccountName ) {
2113 goto done;
2116 #ifndef ENCTYPE_ARCFOUR_HMAC
2117 acct_control |= UF_USE_DES_KEY_ONLY;
2118 #endif
2120 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
2121 goto done;
2124 if (!(mods = ads_init_mods(ctx))) {
2125 goto done;
2128 ads_mod_str(ctx, &mods, "cn", machine_name);
2129 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
2130 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
2131 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2133 ret = ads_gen_add(ads, new_dn, mods);
2135 done:
2136 SAFE_FREE(machine_escaped);
2137 ads_msgfree(ads, res);
2138 talloc_destroy(ctx);
2140 return ret;
2144 * move a machine account to another OU on the ADS server
2145 * @param ads - An intialized ADS_STRUCT
2146 * @param machine_name - the NetBIOS machine name of this account.
2147 * @param org_unit - The LDAP path in which to place this account
2148 * @param moved - whether we moved the machine account (optional)
2149 * @return 0 upon success, or non-zero otherwise
2152 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2153 const char *org_unit, bool *moved)
2155 ADS_STATUS rc;
2156 int ldap_status;
2157 LDAPMessage *res = NULL;
2158 char *filter = NULL;
2159 char *computer_dn = NULL;
2160 char *parent_dn;
2161 char *computer_rdn = NULL;
2162 bool need_move = False;
2164 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2165 rc = ADS_ERROR(LDAP_NO_MEMORY);
2166 goto done;
2169 /* Find pre-existing machine */
2170 rc = ads_search(ads, &res, filter, NULL);
2171 if (!ADS_ERR_OK(rc)) {
2172 goto done;
2175 computer_dn = ads_get_dn(ads, talloc_tos(), res);
2176 if (!computer_dn) {
2177 rc = ADS_ERROR(LDAP_NO_MEMORY);
2178 goto done;
2181 parent_dn = ads_parent_dn(computer_dn);
2182 if (strequal(parent_dn, org_unit)) {
2183 goto done;
2186 need_move = True;
2188 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2189 rc = ADS_ERROR(LDAP_NO_MEMORY);
2190 goto done;
2193 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2194 org_unit, 1, NULL, NULL);
2195 rc = ADS_ERROR(ldap_status);
2197 done:
2198 ads_msgfree(ads, res);
2199 SAFE_FREE(filter);
2200 TALLOC_FREE(computer_dn);
2201 SAFE_FREE(computer_rdn);
2203 if (!ADS_ERR_OK(rc)) {
2204 need_move = False;
2207 if (moved) {
2208 *moved = need_move;
2211 return rc;
2215 dump a binary result from ldap
2217 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2219 int i, j;
2220 for (i=0; values[i]; i++) {
2221 printf("%s: ", field);
2222 for (j=0; j<values[i]->bv_len; j++) {
2223 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2225 printf("\n");
2229 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2231 int i;
2232 for (i=0; values[i]; i++) {
2233 NTSTATUS status;
2234 DATA_BLOB in = data_blob_const(values[i]->bv_val, values[i]->bv_len);
2235 struct GUID guid;
2237 status = GUID_from_ndr_blob(&in, &guid);
2238 if (NT_STATUS_IS_OK(status)) {
2239 printf("%s: %s\n", field, GUID_string(talloc_tos(), &guid));
2240 } else {
2241 printf("%s: INVALID GUID\n", field);
2247 dump a sid result from ldap
2249 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2251 int i;
2252 for (i=0; values[i]; i++) {
2253 struct dom_sid sid;
2254 fstring tmp;
2255 if (!sid_parse(values[i]->bv_val, values[i]->bv_len, &sid)) {
2256 return;
2258 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
2263 dump ntSecurityDescriptor
2265 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2267 TALLOC_CTX *frame = talloc_stackframe();
2268 struct security_descriptor *psd;
2269 NTSTATUS status;
2271 status = unmarshall_sec_desc(talloc_tos(), (uint8 *)values[0]->bv_val,
2272 values[0]->bv_len, &psd);
2273 if (!NT_STATUS_IS_OK(status)) {
2274 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2275 nt_errstr(status)));
2276 TALLOC_FREE(frame);
2277 return;
2280 if (psd) {
2281 ads_disp_sd(ads, talloc_tos(), psd);
2284 TALLOC_FREE(frame);
2288 dump a string result from ldap
2290 static void dump_string(const char *field, char **values)
2292 int i;
2293 for (i=0; values[i]; i++) {
2294 printf("%s: %s\n", field, values[i]);
2299 dump a field from LDAP on stdout
2300 used for debugging
2303 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2305 const struct {
2306 const char *name;
2307 bool string;
2308 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2309 } handlers[] = {
2310 {"objectGUID", False, dump_guid},
2311 {"netbootGUID", False, dump_guid},
2312 {"nTSecurityDescriptor", False, dump_sd},
2313 {"dnsRecord", False, dump_binary},
2314 {"objectSid", False, dump_sid},
2315 {"tokenGroups", False, dump_sid},
2316 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2317 {"tokengroupsGlobalandUniversal", False, dump_sid},
2318 {"mS-DS-CreatorSID", False, dump_sid},
2319 {"msExchMailboxGuid", False, dump_guid},
2320 {NULL, True, NULL}
2322 int i;
2324 if (!field) { /* must be end of an entry */
2325 printf("\n");
2326 return False;
2329 for (i=0; handlers[i].name; i++) {
2330 if (strcasecmp_m(handlers[i].name, field) == 0) {
2331 if (!values) /* first time, indicate string or not */
2332 return handlers[i].string;
2333 handlers[i].handler(ads, field, (struct berval **) values);
2334 break;
2337 if (!handlers[i].name) {
2338 if (!values) /* first time, indicate string conversion */
2339 return True;
2340 dump_string(field, (char **)values);
2342 return False;
2346 * Dump a result from LDAP on stdout
2347 * used for debugging
2348 * @param ads connection to ads server
2349 * @param res Results to dump
2352 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2354 ads_process_results(ads, res, ads_dump_field, NULL);
2358 * Walk through results, calling a function for each entry found.
2359 * The function receives a field name, a berval * array of values,
2360 * and a data area passed through from the start. The function is
2361 * called once with null for field and values at the end of each
2362 * entry.
2363 * @param ads connection to ads server
2364 * @param res Results to process
2365 * @param fn Function for processing each result
2366 * @param data_area user-defined area to pass to function
2368 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2369 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2370 void *data_area)
2372 LDAPMessage *msg;
2373 TALLOC_CTX *ctx;
2374 size_t converted_size;
2376 if (!(ctx = talloc_init("ads_process_results")))
2377 return;
2379 for (msg = ads_first_entry(ads, res); msg;
2380 msg = ads_next_entry(ads, msg)) {
2381 char *utf8_field;
2382 BerElement *b;
2384 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2385 (LDAPMessage *)msg,&b);
2386 utf8_field;
2387 utf8_field=ldap_next_attribute(ads->ldap.ld,
2388 (LDAPMessage *)msg,b)) {
2389 struct berval **ber_vals;
2390 char **str_vals, **utf8_vals;
2391 char *field;
2392 bool string;
2394 if (!pull_utf8_talloc(ctx, &field, utf8_field,
2395 &converted_size))
2397 DEBUG(0,("ads_process_results: "
2398 "pull_utf8_talloc failed: %s",
2399 strerror(errno)));
2402 string = fn(ads, field, NULL, data_area);
2404 if (string) {
2405 utf8_vals = ldap_get_values(ads->ldap.ld,
2406 (LDAPMessage *)msg, field);
2407 str_vals = ads_pull_strvals(ctx,
2408 (const char **) utf8_vals);
2409 fn(ads, field, (void **) str_vals, data_area);
2410 ldap_value_free(utf8_vals);
2411 } else {
2412 ber_vals = ldap_get_values_len(ads->ldap.ld,
2413 (LDAPMessage *)msg, field);
2414 fn(ads, field, (void **) ber_vals, data_area);
2416 ldap_value_free_len(ber_vals);
2418 ldap_memfree(utf8_field);
2420 ber_free(b, 0);
2421 talloc_free_children(ctx);
2422 fn(ads, NULL, NULL, data_area); /* completed an entry */
2425 talloc_destroy(ctx);
2429 * count how many replies are in a LDAPMessage
2430 * @param ads connection to ads server
2431 * @param res Results to count
2432 * @return number of replies
2434 int ads_count_replies(ADS_STRUCT *ads, void *res)
2436 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2440 * pull the first entry from a ADS result
2441 * @param ads connection to ads server
2442 * @param res Results of search
2443 * @return first entry from result
2445 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2447 return ldap_first_entry(ads->ldap.ld, res);
2451 * pull the next entry from a ADS result
2452 * @param ads connection to ads server
2453 * @param res Results of search
2454 * @return next entry from result
2456 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2458 return ldap_next_entry(ads->ldap.ld, res);
2462 * pull the first message from a ADS result
2463 * @param ads connection to ads server
2464 * @param res Results of search
2465 * @return first message from result
2467 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2469 return ldap_first_message(ads->ldap.ld, res);
2473 * pull the next message from a ADS result
2474 * @param ads connection to ads server
2475 * @param res Results of search
2476 * @return next message from result
2478 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2480 return ldap_next_message(ads->ldap.ld, res);
2484 * pull a single string from a ADS result
2485 * @param ads connection to ads server
2486 * @param mem_ctx TALLOC_CTX to use for allocating result string
2487 * @param msg Results of search
2488 * @param field Attribute to retrieve
2489 * @return Result string in talloc context
2491 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2492 const char *field)
2494 char **values;
2495 char *ret = NULL;
2496 char *ux_string;
2497 size_t converted_size;
2499 values = ldap_get_values(ads->ldap.ld, msg, field);
2500 if (!values)
2501 return NULL;
2503 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2504 &converted_size))
2506 ret = ux_string;
2508 ldap_value_free(values);
2509 return ret;
2513 * pull an array of strings 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 strings in talloc context
2520 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2521 LDAPMessage *msg, const char *field,
2522 size_t *num_values)
2524 char **values;
2525 char **ret = NULL;
2526 int i;
2527 size_t converted_size;
2529 values = ldap_get_values(ads->ldap.ld, msg, field);
2530 if (!values)
2531 return NULL;
2533 *num_values = ldap_count_values(values);
2535 ret = talloc_array(mem_ctx, char *, *num_values + 1);
2536 if (!ret) {
2537 ldap_value_free(values);
2538 return NULL;
2541 for (i=0;i<*num_values;i++) {
2542 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2543 &converted_size))
2545 ldap_value_free(values);
2546 return NULL;
2549 ret[i] = NULL;
2551 ldap_value_free(values);
2552 return ret;
2556 * pull an array of strings from a ADS result
2557 * (handle large multivalue attributes with range retrieval)
2558 * @param ads connection to ads server
2559 * @param mem_ctx TALLOC_CTX to use for allocating result string
2560 * @param msg Results of search
2561 * @param field Attribute to retrieve
2562 * @param current_strings strings returned by a previous call to this function
2563 * @param next_attribute The next query should ask for this attribute
2564 * @param num_values How many values did we get this time?
2565 * @param more_values Are there more values to get?
2566 * @return Result strings in talloc context
2568 char **ads_pull_strings_range(ADS_STRUCT *ads,
2569 TALLOC_CTX *mem_ctx,
2570 LDAPMessage *msg, const char *field,
2571 char **current_strings,
2572 const char **next_attribute,
2573 size_t *num_strings,
2574 bool *more_strings)
2576 char *attr;
2577 char *expected_range_attrib, *range_attr;
2578 BerElement *ptr = NULL;
2579 char **strings;
2580 char **new_strings;
2581 size_t num_new_strings;
2582 unsigned long int range_start;
2583 unsigned long int range_end;
2585 /* we might have been given the whole lot anyway */
2586 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2587 *more_strings = False;
2588 return strings;
2591 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2593 /* look for Range result */
2594 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2595 attr;
2596 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2597 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2598 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2599 range_attr = attr;
2600 break;
2602 ldap_memfree(attr);
2604 if (!attr) {
2605 ber_free(ptr, 0);
2606 /* nothing here - this field is just empty */
2607 *more_strings = False;
2608 return NULL;
2611 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2612 &range_start, &range_end) == 2) {
2613 *more_strings = True;
2614 } else {
2615 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2616 &range_start) == 1) {
2617 *more_strings = False;
2618 } else {
2619 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2620 range_attr));
2621 ldap_memfree(range_attr);
2622 *more_strings = False;
2623 return NULL;
2627 if ((*num_strings) != range_start) {
2628 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2629 " - aborting range retreival\n",
2630 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2631 ldap_memfree(range_attr);
2632 *more_strings = False;
2633 return NULL;
2636 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2638 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2639 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2640 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2641 range_attr, (unsigned long int)range_end - range_start + 1,
2642 (unsigned long int)num_new_strings));
2643 ldap_memfree(range_attr);
2644 *more_strings = False;
2645 return NULL;
2648 strings = talloc_realloc(mem_ctx, current_strings, char *,
2649 *num_strings + num_new_strings);
2651 if (strings == NULL) {
2652 ldap_memfree(range_attr);
2653 *more_strings = False;
2654 return NULL;
2657 if (new_strings && num_new_strings) {
2658 memcpy(&strings[*num_strings], new_strings,
2659 sizeof(*new_strings) * num_new_strings);
2662 (*num_strings) += num_new_strings;
2664 if (*more_strings) {
2665 *next_attribute = talloc_asprintf(mem_ctx,
2666 "%s;range=%d-*",
2667 field,
2668 (int)*num_strings);
2670 if (!*next_attribute) {
2671 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2672 ldap_memfree(range_attr);
2673 *more_strings = False;
2674 return NULL;
2678 ldap_memfree(range_attr);
2680 return strings;
2684 * pull a single uint32 from a ADS result
2685 * @param ads connection to ads server
2686 * @param msg Results of search
2687 * @param field Attribute to retrieve
2688 * @param v Pointer to int to store result
2689 * @return boolean inidicating success
2691 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2692 uint32 *v)
2694 char **values;
2696 values = ldap_get_values(ads->ldap.ld, msg, field);
2697 if (!values)
2698 return False;
2699 if (!values[0]) {
2700 ldap_value_free(values);
2701 return False;
2704 *v = atoi(values[0]);
2705 ldap_value_free(values);
2706 return True;
2710 * pull a single objectGUID from an ADS result
2711 * @param ads connection to ADS server
2712 * @param msg results of search
2713 * @param guid 37-byte area to receive text guid
2714 * @return boolean indicating success
2716 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2718 DATA_BLOB blob;
2719 NTSTATUS status;
2721 if (!smbldap_talloc_single_blob(talloc_tos(), ads->ldap.ld, msg, "objectGUID",
2722 &blob)) {
2723 return false;
2726 status = GUID_from_ndr_blob(&blob, guid);
2727 talloc_free(blob.data);
2728 return NT_STATUS_IS_OK(status);
2733 * pull a single struct dom_sid from a ADS result
2734 * @param ads connection to ads server
2735 * @param msg Results of search
2736 * @param field Attribute to retrieve
2737 * @param sid Pointer to sid to store result
2738 * @return boolean inidicating success
2740 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2741 struct dom_sid *sid)
2743 return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
2747 * pull an array of struct dom_sids from a ADS result
2748 * @param ads connection to ads server
2749 * @param mem_ctx TALLOC_CTX for allocating sid array
2750 * @param msg Results of search
2751 * @param field Attribute to retrieve
2752 * @param sids pointer to sid array to allocate
2753 * @return the count of SIDs pulled
2755 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2756 LDAPMessage *msg, const char *field, struct dom_sid **sids)
2758 struct berval **values;
2759 bool ret;
2760 int count, i;
2762 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2764 if (!values)
2765 return 0;
2767 for (i=0; values[i]; i++)
2768 /* nop */ ;
2770 if (i) {
2771 (*sids) = talloc_array(mem_ctx, struct dom_sid, i);
2772 if (!(*sids)) {
2773 ldap_value_free_len(values);
2774 return 0;
2776 } else {
2777 (*sids) = NULL;
2780 count = 0;
2781 for (i=0; values[i]; i++) {
2782 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2783 if (ret) {
2784 DEBUG(10, ("pulling SID: %s\n",
2785 sid_string_dbg(&(*sids)[count])));
2786 count++;
2790 ldap_value_free_len(values);
2791 return count;
2795 * pull a struct security_descriptor from a ADS result
2796 * @param ads connection to ads server
2797 * @param mem_ctx TALLOC_CTX for allocating sid array
2798 * @param msg Results of search
2799 * @param field Attribute to retrieve
2800 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
2801 * @return boolean inidicating success
2803 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2804 LDAPMessage *msg, const char *field,
2805 struct security_descriptor **sd)
2807 struct berval **values;
2808 bool ret = true;
2810 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2812 if (!values) return false;
2814 if (values[0]) {
2815 NTSTATUS status;
2816 status = unmarshall_sec_desc(mem_ctx,
2817 (uint8 *)values[0]->bv_val,
2818 values[0]->bv_len, sd);
2819 if (!NT_STATUS_IS_OK(status)) {
2820 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2821 nt_errstr(status)));
2822 ret = false;
2826 ldap_value_free_len(values);
2827 return ret;
2831 * in order to support usernames longer than 21 characters we need to
2832 * use both the sAMAccountName and the userPrincipalName attributes
2833 * It seems that not all users have the userPrincipalName attribute set
2835 * @param ads connection to ads server
2836 * @param mem_ctx TALLOC_CTX for allocating sid array
2837 * @param msg Results of search
2838 * @return the username
2840 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2841 LDAPMessage *msg)
2843 #if 0 /* JERRY */
2844 char *ret, *p;
2846 /* lookup_name() only works on the sAMAccountName to
2847 returning the username portion of userPrincipalName
2848 breaks winbindd_getpwnam() */
2850 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2851 if (ret && (p = strchr_m(ret, '@'))) {
2852 *p = 0;
2853 return ret;
2855 #endif
2856 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2861 * find the update serial number - this is the core of the ldap cache
2862 * @param ads connection to ads server
2863 * @param ads connection to ADS server
2864 * @param usn Pointer to retrieved update serial number
2865 * @return status of search
2867 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2869 const char *attrs[] = {"highestCommittedUSN", NULL};
2870 ADS_STATUS status;
2871 LDAPMessage *res;
2873 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2874 if (!ADS_ERR_OK(status))
2875 return status;
2877 if (ads_count_replies(ads, res) != 1) {
2878 ads_msgfree(ads, res);
2879 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2882 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2883 ads_msgfree(ads, res);
2884 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2887 ads_msgfree(ads, res);
2888 return ADS_SUCCESS;
2891 /* parse a ADS timestring - typical string is
2892 '20020917091222.0Z0' which means 09:12.22 17th September
2893 2002, timezone 0 */
2894 static time_t ads_parse_time(const char *str)
2896 struct tm tm;
2898 ZERO_STRUCT(tm);
2900 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2901 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2902 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2903 return 0;
2905 tm.tm_year -= 1900;
2906 tm.tm_mon -= 1;
2908 return timegm(&tm);
2911 /********************************************************************
2912 ********************************************************************/
2914 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2916 const char *attrs[] = {"currentTime", NULL};
2917 ADS_STATUS status;
2918 LDAPMessage *res;
2919 char *timestr;
2920 TALLOC_CTX *ctx;
2921 ADS_STRUCT *ads_s = ads;
2923 if (!(ctx = talloc_init("ads_current_time"))) {
2924 return ADS_ERROR(LDAP_NO_MEMORY);
2927 /* establish a new ldap tcp session if necessary */
2929 if ( !ads->ldap.ld ) {
2930 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2931 ads->server.ldap_server )) == NULL )
2933 goto done;
2935 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2936 status = ads_connect( ads_s );
2937 if ( !ADS_ERR_OK(status))
2938 goto done;
2941 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2942 if (!ADS_ERR_OK(status)) {
2943 goto done;
2946 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2947 if (!timestr) {
2948 ads_msgfree(ads_s, res);
2949 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2950 goto done;
2953 /* but save the time and offset in the original ADS_STRUCT */
2955 ads->config.current_time = ads_parse_time(timestr);
2957 if (ads->config.current_time != 0) {
2958 ads->auth.time_offset = ads->config.current_time - time(NULL);
2959 DEBUG(4,("KDC time offset is %d seconds\n", ads->auth.time_offset));
2962 ads_msgfree(ads, res);
2964 status = ADS_SUCCESS;
2966 done:
2967 /* free any temporary ads connections */
2968 if ( ads_s != ads ) {
2969 ads_destroy( &ads_s );
2971 talloc_destroy(ctx);
2973 return status;
2976 /********************************************************************
2977 ********************************************************************/
2979 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2981 const char *attrs[] = {"domainFunctionality", NULL};
2982 ADS_STATUS status;
2983 LDAPMessage *res;
2984 ADS_STRUCT *ads_s = ads;
2986 *val = DS_DOMAIN_FUNCTION_2000;
2988 /* establish a new ldap tcp session if necessary */
2990 if ( !ads->ldap.ld ) {
2991 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2992 ads->server.ldap_server )) == NULL )
2994 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2995 goto done;
2997 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2998 status = ads_connect( ads_s );
2999 if ( !ADS_ERR_OK(status))
3000 goto done;
3003 /* If the attribute does not exist assume it is a Windows 2000
3004 functional domain */
3006 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3007 if (!ADS_ERR_OK(status)) {
3008 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
3009 status = ADS_SUCCESS;
3011 goto done;
3014 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
3015 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
3017 DEBUG(3,("ads_domain_func_level: %d\n", *val));
3020 ads_msgfree(ads, res);
3022 done:
3023 /* free any temporary ads connections */
3024 if ( ads_s != ads ) {
3025 ads_destroy( &ads_s );
3028 return status;
3032 * find the domain sid for our domain
3033 * @param ads connection to ads server
3034 * @param sid Pointer to domain sid
3035 * @return status of search
3037 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
3039 const char *attrs[] = {"objectSid", NULL};
3040 LDAPMessage *res;
3041 ADS_STATUS rc;
3043 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
3044 attrs, &res);
3045 if (!ADS_ERR_OK(rc)) return rc;
3046 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
3047 ads_msgfree(ads, res);
3048 return ADS_ERROR_SYSTEM(ENOENT);
3050 ads_msgfree(ads, res);
3052 return ADS_SUCCESS;
3056 * find our site name
3057 * @param ads connection to ads server
3058 * @param mem_ctx Pointer to talloc context
3059 * @param site_name Pointer to the sitename
3060 * @return status of search
3062 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
3064 ADS_STATUS status;
3065 LDAPMessage *res;
3066 const char *dn, *service_name;
3067 const char *attrs[] = { "dsServiceName", NULL };
3069 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3070 if (!ADS_ERR_OK(status)) {
3071 return status;
3074 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
3075 if (service_name == NULL) {
3076 ads_msgfree(ads, res);
3077 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3080 ads_msgfree(ads, res);
3082 /* go up three levels */
3083 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
3084 if (dn == NULL) {
3085 return ADS_ERROR(LDAP_NO_MEMORY);
3088 *site_name = talloc_strdup(mem_ctx, dn);
3089 if (*site_name == NULL) {
3090 return ADS_ERROR(LDAP_NO_MEMORY);
3093 return status;
3095 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3100 * find the site dn where a machine resides
3101 * @param ads connection to ads server
3102 * @param mem_ctx Pointer to talloc context
3103 * @param computer_name name of the machine
3104 * @param site_name Pointer to the sitename
3105 * @return status of search
3107 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3109 ADS_STATUS status;
3110 LDAPMessage *res;
3111 const char *parent, *filter;
3112 char *config_context = NULL;
3113 char *dn;
3115 /* shortcut a query */
3116 if (strequal(computer_name, ads->config.ldap_server_name)) {
3117 return ads_site_dn(ads, mem_ctx, site_dn);
3120 status = ads_config_path(ads, mem_ctx, &config_context);
3121 if (!ADS_ERR_OK(status)) {
3122 return status;
3125 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3126 if (filter == NULL) {
3127 return ADS_ERROR(LDAP_NO_MEMORY);
3130 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3131 filter, NULL, &res);
3132 if (!ADS_ERR_OK(status)) {
3133 return status;
3136 if (ads_count_replies(ads, res) != 1) {
3137 ads_msgfree(ads, res);
3138 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3141 dn = ads_get_dn(ads, mem_ctx, res);
3142 if (dn == NULL) {
3143 ads_msgfree(ads, res);
3144 return ADS_ERROR(LDAP_NO_MEMORY);
3147 /* go up three levels */
3148 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3149 if (parent == NULL) {
3150 ads_msgfree(ads, res);
3151 TALLOC_FREE(dn);
3152 return ADS_ERROR(LDAP_NO_MEMORY);
3155 *site_dn = talloc_strdup(mem_ctx, parent);
3156 if (*site_dn == NULL) {
3157 ads_msgfree(ads, res);
3158 TALLOC_FREE(dn);
3159 return ADS_ERROR(LDAP_NO_MEMORY);
3162 TALLOC_FREE(dn);
3163 ads_msgfree(ads, res);
3165 return status;
3169 * get the upn suffixes for a domain
3170 * @param ads connection to ads server
3171 * @param mem_ctx Pointer to talloc context
3172 * @param suffixes Pointer to an array of suffixes
3173 * @param num_suffixes Pointer to the number of suffixes
3174 * @return status of search
3176 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3178 ADS_STATUS status;
3179 LDAPMessage *res;
3180 const char *base;
3181 char *config_context = NULL;
3182 const char *attrs[] = { "uPNSuffixes", NULL };
3184 status = ads_config_path(ads, mem_ctx, &config_context);
3185 if (!ADS_ERR_OK(status)) {
3186 return status;
3189 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3190 if (base == NULL) {
3191 return ADS_ERROR(LDAP_NO_MEMORY);
3194 status = ads_search_dn(ads, &res, base, attrs);
3195 if (!ADS_ERR_OK(status)) {
3196 return status;
3199 if (ads_count_replies(ads, res) != 1) {
3200 ads_msgfree(ads, res);
3201 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3204 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3205 if ((*suffixes) == NULL) {
3206 ads_msgfree(ads, res);
3207 return ADS_ERROR(LDAP_NO_MEMORY);
3210 ads_msgfree(ads, res);
3212 return status;
3216 * get the joinable ous for a domain
3217 * @param ads connection to ads server
3218 * @param mem_ctx Pointer to talloc context
3219 * @param ous Pointer to an array of ous
3220 * @param num_ous Pointer to the number of ous
3221 * @return status of search
3223 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3224 TALLOC_CTX *mem_ctx,
3225 char ***ous,
3226 size_t *num_ous)
3228 ADS_STATUS status;
3229 LDAPMessage *res = NULL;
3230 LDAPMessage *msg = NULL;
3231 const char *attrs[] = { "dn", NULL };
3232 int count = 0;
3234 status = ads_search(ads, &res,
3235 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3236 attrs);
3237 if (!ADS_ERR_OK(status)) {
3238 return status;
3241 count = ads_count_replies(ads, res);
3242 if (count < 1) {
3243 ads_msgfree(ads, res);
3244 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3247 for (msg = ads_first_entry(ads, res); msg;
3248 msg = ads_next_entry(ads, msg)) {
3250 char *dn = NULL;
3252 dn = ads_get_dn(ads, talloc_tos(), msg);
3253 if (!dn) {
3254 ads_msgfree(ads, res);
3255 return ADS_ERROR(LDAP_NO_MEMORY);
3258 if (!add_string_to_array(mem_ctx, dn,
3259 (const char ***)ous,
3260 (int *)num_ous)) {
3261 TALLOC_FREE(dn);
3262 ads_msgfree(ads, res);
3263 return ADS_ERROR(LDAP_NO_MEMORY);
3266 TALLOC_FREE(dn);
3269 ads_msgfree(ads, res);
3271 return status;
3276 * pull a struct dom_sid from an extended dn string
3277 * @param mem_ctx TALLOC_CTX
3278 * @param extended_dn string
3279 * @param flags string type of extended_dn
3280 * @param sid pointer to a struct dom_sid
3281 * @return NT_STATUS_OK on success,
3282 * NT_INVALID_PARAMETER on error,
3283 * NT_STATUS_NOT_FOUND if no SID present
3285 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3286 const char *extended_dn,
3287 enum ads_extended_dn_flags flags,
3288 struct dom_sid *sid)
3290 char *p, *q, *dn;
3292 if (!extended_dn) {
3293 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3296 /* otherwise extended_dn gets stripped off */
3297 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3298 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3301 * ADS_EXTENDED_DN_HEX_STRING:
3302 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3304 * ADS_EXTENDED_DN_STRING (only with w2k3):
3305 * <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
3307 * Object with no SID, such as an Exchange Public Folder
3308 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3311 p = strchr(dn, ';');
3312 if (!p) {
3313 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3316 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3317 DEBUG(5,("No SID present in extended dn\n"));
3318 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3321 p += strlen(";<SID=");
3323 q = strchr(p, '>');
3324 if (!q) {
3325 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3328 *q = '\0';
3330 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3332 switch (flags) {
3334 case ADS_EXTENDED_DN_STRING:
3335 if (!string_to_sid(sid, p)) {
3336 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3338 break;
3339 case ADS_EXTENDED_DN_HEX_STRING: {
3340 fstring buf;
3341 size_t buf_len;
3343 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3344 if (buf_len == 0) {
3345 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3348 if (!sid_parse(buf, buf_len, sid)) {
3349 DEBUG(10,("failed to parse sid\n"));
3350 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3352 break;
3354 default:
3355 DEBUG(10,("unknown extended dn format\n"));
3356 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3359 return ADS_ERROR_NT(NT_STATUS_OK);
3362 /********************************************************************
3363 ********************************************************************/
3365 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3367 LDAPMessage *res = NULL;
3368 ADS_STATUS status;
3369 int count = 0;
3370 char *name = NULL;
3372 status = ads_find_machine_acct(ads, &res, lp_netbios_name());
3373 if (!ADS_ERR_OK(status)) {
3374 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3375 lp_netbios_name()));
3376 goto out;
3379 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3380 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3381 goto out;
3384 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3385 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3388 out:
3389 ads_msgfree(ads, res);
3391 return name;
3394 /********************************************************************
3395 ********************************************************************/
3397 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3399 LDAPMessage *res = NULL;
3400 ADS_STATUS status;
3401 int count = 0;
3402 char *name = NULL;
3404 status = ads_find_machine_acct(ads, &res, machine_name);
3405 if (!ADS_ERR_OK(status)) {
3406 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3407 lp_netbios_name()));
3408 goto out;
3411 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3412 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3413 goto out;
3416 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3417 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3420 out:
3421 ads_msgfree(ads, res);
3423 return name;
3426 /********************************************************************
3427 ********************************************************************/
3429 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3431 LDAPMessage *res = NULL;
3432 ADS_STATUS status;
3433 int count = 0;
3434 char *name = NULL;
3436 status = ads_find_machine_acct(ads, &res, lp_netbios_name());
3437 if (!ADS_ERR_OK(status)) {
3438 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3439 lp_netbios_name()));
3440 goto out;
3443 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3444 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3445 goto out;
3448 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3449 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3452 out:
3453 ads_msgfree(ads, res);
3455 return name;
3458 #if 0
3460 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3463 * Join a machine to a realm
3464 * Creates the machine account and sets the machine password
3465 * @param ads connection to ads server
3466 * @param machine name of host to add
3467 * @param org_unit Organizational unit to place machine in
3468 * @return status of join
3470 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3471 uint32 account_type, const char *org_unit)
3473 ADS_STATUS status;
3474 LDAPMessage *res = NULL;
3475 char *machine;
3477 /* machine name must be lowercase */
3478 machine = SMB_STRDUP(machine_name);
3479 strlower_m(machine);
3482 status = ads_find_machine_acct(ads, (void **)&res, machine);
3483 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3484 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3485 status = ads_leave_realm(ads, machine);
3486 if (!ADS_ERR_OK(status)) {
3487 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3488 machine, ads->config.realm));
3489 return status;
3493 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3494 if (!ADS_ERR_OK(status)) {
3495 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3496 SAFE_FREE(machine);
3497 return status;
3500 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3501 if (!ADS_ERR_OK(status)) {
3502 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3503 SAFE_FREE(machine);
3504 return status;
3507 SAFE_FREE(machine);
3508 ads_msgfree(ads, res);
3510 return status;
3512 #endif
3515 * Delete a machine from the realm
3516 * @param ads connection to ads server
3517 * @param hostname Machine to remove
3518 * @return status of delete
3520 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3522 ADS_STATUS status;
3523 void *msg;
3524 LDAPMessage *res;
3525 char *hostnameDN, *host;
3526 int rc;
3527 LDAPControl ldap_control;
3528 LDAPControl * pldap_control[2] = {NULL, NULL};
3530 pldap_control[0] = &ldap_control;
3531 memset(&ldap_control, 0, sizeof(LDAPControl));
3532 ldap_control.ldctl_oid = discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID);
3534 /* hostname must be lowercase */
3535 host = SMB_STRDUP(hostname);
3536 if (!strlower_m(host)) {
3537 SAFE_FREE(host);
3538 return ADS_ERROR_SYSTEM(EINVAL);
3541 status = ads_find_machine_acct(ads, &res, host);
3542 if (!ADS_ERR_OK(status)) {
3543 DEBUG(0, ("Host account for %s does not exist.\n", host));
3544 SAFE_FREE(host);
3545 return status;
3548 msg = ads_first_entry(ads, res);
3549 if (!msg) {
3550 SAFE_FREE(host);
3551 return ADS_ERROR_SYSTEM(ENOENT);
3554 hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
3555 if (hostnameDN == NULL) {
3556 SAFE_FREE(host);
3557 return ADS_ERROR_SYSTEM(ENOENT);
3560 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3561 if (rc) {
3562 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3563 }else {
3564 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3567 if (rc != LDAP_SUCCESS) {
3568 const char *attrs[] = { "cn", NULL };
3569 LDAPMessage *msg_sub;
3571 /* we only search with scope ONE, we do not expect any further
3572 * objects to be created deeper */
3574 status = ads_do_search_retry(ads, hostnameDN,
3575 LDAP_SCOPE_ONELEVEL,
3576 "(objectclass=*)", attrs, &res);
3578 if (!ADS_ERR_OK(status)) {
3579 SAFE_FREE(host);
3580 TALLOC_FREE(hostnameDN);
3581 return status;
3584 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3585 msg_sub = ads_next_entry(ads, msg_sub)) {
3587 char *dn = NULL;
3589 if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
3590 SAFE_FREE(host);
3591 TALLOC_FREE(hostnameDN);
3592 return ADS_ERROR(LDAP_NO_MEMORY);
3595 status = ads_del_dn(ads, dn);
3596 if (!ADS_ERR_OK(status)) {
3597 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3598 SAFE_FREE(host);
3599 TALLOC_FREE(dn);
3600 TALLOC_FREE(hostnameDN);
3601 return status;
3604 TALLOC_FREE(dn);
3607 /* there should be no subordinate objects anymore */
3608 status = ads_do_search_retry(ads, hostnameDN,
3609 LDAP_SCOPE_ONELEVEL,
3610 "(objectclass=*)", attrs, &res);
3612 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3613 SAFE_FREE(host);
3614 TALLOC_FREE(hostnameDN);
3615 return status;
3618 /* delete hostnameDN now */
3619 status = ads_del_dn(ads, hostnameDN);
3620 if (!ADS_ERR_OK(status)) {
3621 SAFE_FREE(host);
3622 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3623 TALLOC_FREE(hostnameDN);
3624 return status;
3628 TALLOC_FREE(hostnameDN);
3630 status = ads_find_machine_acct(ads, &res, host);
3631 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3632 DEBUG(3, ("Failed to remove host account.\n"));
3633 SAFE_FREE(host);
3634 return status;
3637 SAFE_FREE(host);
3638 return status;
3642 * pull all token-sids from an LDAP dn
3643 * @param ads connection to ads server
3644 * @param mem_ctx TALLOC_CTX for allocating sid array
3645 * @param dn of LDAP object
3646 * @param user_sid pointer to struct dom_sid (objectSid)
3647 * @param primary_group_sid pointer to struct dom_sid (self composed)
3648 * @param sids pointer to sid array to allocate
3649 * @param num_sids counter of SIDs pulled
3650 * @return status of token query
3652 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3653 TALLOC_CTX *mem_ctx,
3654 const char *dn,
3655 struct dom_sid *user_sid,
3656 struct dom_sid *primary_group_sid,
3657 struct dom_sid **sids,
3658 size_t *num_sids)
3660 ADS_STATUS status;
3661 LDAPMessage *res = NULL;
3662 int count = 0;
3663 size_t tmp_num_sids;
3664 struct dom_sid *tmp_sids;
3665 struct dom_sid tmp_user_sid;
3666 struct dom_sid tmp_primary_group_sid;
3667 uint32 pgid;
3668 const char *attrs[] = {
3669 "objectSid",
3670 "tokenGroups",
3671 "primaryGroupID",
3672 NULL
3675 status = ads_search_retry_dn(ads, &res, dn, attrs);
3676 if (!ADS_ERR_OK(status)) {
3677 return status;
3680 count = ads_count_replies(ads, res);
3681 if (count != 1) {
3682 ads_msgfree(ads, res);
3683 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3686 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3687 ads_msgfree(ads, res);
3688 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3691 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3692 ads_msgfree(ads, res);
3693 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3697 /* hack to compose the primary group sid without knowing the
3698 * domsid */
3700 struct dom_sid domsid;
3702 sid_copy(&domsid, &tmp_user_sid);
3704 if (!sid_split_rid(&domsid, NULL)) {
3705 ads_msgfree(ads, res);
3706 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3709 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3710 ads_msgfree(ads, res);
3711 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3715 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3717 if (tmp_num_sids == 0 || !tmp_sids) {
3718 ads_msgfree(ads, res);
3719 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3722 if (num_sids) {
3723 *num_sids = tmp_num_sids;
3726 if (sids) {
3727 *sids = tmp_sids;
3730 if (user_sid) {
3731 *user_sid = tmp_user_sid;
3734 if (primary_group_sid) {
3735 *primary_group_sid = tmp_primary_group_sid;
3738 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3740 ads_msgfree(ads, res);
3741 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3745 * Find a sAMAccoutName in LDAP
3746 * @param ads connection to ads server
3747 * @param mem_ctx TALLOC_CTX for allocating sid array
3748 * @param samaccountname to search
3749 * @param uac_ret uint32 pointer userAccountControl attribute value
3750 * @param dn_ret pointer to dn
3751 * @return status of token query
3753 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3754 TALLOC_CTX *mem_ctx,
3755 const char *samaccountname,
3756 uint32 *uac_ret,
3757 const char **dn_ret)
3759 ADS_STATUS status;
3760 const char *attrs[] = { "userAccountControl", NULL };
3761 const char *filter;
3762 LDAPMessage *res = NULL;
3763 char *dn = NULL;
3764 uint32 uac = 0;
3766 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3767 samaccountname);
3768 if (filter == NULL) {
3769 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3770 goto out;
3773 status = ads_do_search_all(ads, ads->config.bind_path,
3774 LDAP_SCOPE_SUBTREE,
3775 filter, attrs, &res);
3777 if (!ADS_ERR_OK(status)) {
3778 goto out;
3781 if (ads_count_replies(ads, res) != 1) {
3782 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3783 goto out;
3786 dn = ads_get_dn(ads, talloc_tos(), res);
3787 if (dn == NULL) {
3788 status = ADS_ERROR(LDAP_NO_MEMORY);
3789 goto out;
3792 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3793 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3794 goto out;
3797 if (uac_ret) {
3798 *uac_ret = uac;
3801 if (dn_ret) {
3802 *dn_ret = talloc_strdup(mem_ctx, dn);
3803 if (!*dn_ret) {
3804 status = ADS_ERROR(LDAP_NO_MEMORY);
3805 goto out;
3808 out:
3809 TALLOC_FREE(dn);
3810 ads_msgfree(ads, res);
3812 return status;
3816 * find our configuration path
3817 * @param ads connection to ads server
3818 * @param mem_ctx Pointer to talloc context
3819 * @param config_path Pointer to the config path
3820 * @return status of search
3822 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3823 TALLOC_CTX *mem_ctx,
3824 char **config_path)
3826 ADS_STATUS status;
3827 LDAPMessage *res = NULL;
3828 const char *config_context = NULL;
3829 const char *attrs[] = { "configurationNamingContext", NULL };
3831 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3832 "(objectclass=*)", attrs, &res);
3833 if (!ADS_ERR_OK(status)) {
3834 return status;
3837 config_context = ads_pull_string(ads, mem_ctx, res,
3838 "configurationNamingContext");
3839 ads_msgfree(ads, res);
3840 if (!config_context) {
3841 return ADS_ERROR(LDAP_NO_MEMORY);
3844 if (config_path) {
3845 *config_path = talloc_strdup(mem_ctx, config_context);
3846 if (!*config_path) {
3847 return ADS_ERROR(LDAP_NO_MEMORY);
3851 return ADS_ERROR(LDAP_SUCCESS);
3855 * find the displayName of an extended right
3856 * @param ads connection to ads server
3857 * @param config_path The config path
3858 * @param mem_ctx Pointer to talloc context
3859 * @param GUID struct of the rightsGUID
3860 * @return status of search
3862 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3863 const char *config_path,
3864 TALLOC_CTX *mem_ctx,
3865 const struct GUID *rights_guid)
3867 ADS_STATUS rc;
3868 LDAPMessage *res = NULL;
3869 char *expr = NULL;
3870 const char *attrs[] = { "displayName", NULL };
3871 const char *result = NULL;
3872 const char *path;
3874 if (!ads || !mem_ctx || !rights_guid) {
3875 goto done;
3878 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3879 GUID_string(mem_ctx, rights_guid));
3880 if (!expr) {
3881 goto done;
3884 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3885 if (!path) {
3886 goto done;
3889 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3890 expr, attrs, &res);
3891 if (!ADS_ERR_OK(rc)) {
3892 goto done;
3895 if (ads_count_replies(ads, res) != 1) {
3896 goto done;
3899 result = ads_pull_string(ads, mem_ctx, res, "displayName");
3901 done:
3902 ads_msgfree(ads, res);
3903 return result;
3907 * verify or build and verify an account ou
3908 * @param mem_ctx Pointer to talloc context
3909 * @param ads connection to ads server
3910 * @param account_ou
3911 * @return status of search
3914 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3915 ADS_STRUCT *ads,
3916 const char **account_ou)
3918 char **exploded_dn;
3919 const char *name;
3920 char *ou_string;
3922 exploded_dn = ldap_explode_dn(*account_ou, 0);
3923 if (exploded_dn) {
3924 ldap_value_free(exploded_dn);
3925 return ADS_SUCCESS;
3928 ou_string = ads_ou_string(ads, *account_ou);
3929 if (!ou_string) {
3930 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3933 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3934 ads->config.bind_path);
3935 SAFE_FREE(ou_string);
3937 if (!name) {
3938 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3941 exploded_dn = ldap_explode_dn(name, 0);
3942 if (!exploded_dn) {
3943 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3945 ldap_value_free(exploded_dn);
3947 *account_ou = name;
3948 return ADS_SUCCESS;
3951 #endif