Fix extended DN parse error when AD object does not have a SID.
[Samba.git] / source / libads / ldap.c
blob97e802a858172af2f42ccf6785c2a0dd334f09be
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 "lib/ldb/include/includes.h"
27 #ifdef HAVE_LDAP
29 /**
30 * @file ldap.c
31 * @brief basic ldap client-side routines for ads server communications
33 * The routines contained here should do the necessary ldap calls for
34 * ads setups.
36 * Important note: attribute names passed into ads_ routines must
37 * already be in UTF-8 format. We do not convert them because in almost
38 * all cases, they are just ascii (which is represented with the same
39 * codepoints in UTF-8). This may have to change at some point
40 **/
43 #define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"
45 static SIG_ATOMIC_T gotalarm;
47 /***************************************************************
48 Signal function to tell us we timed out.
49 ****************************************************************/
51 static void gotalarm_sig(void)
53 gotalarm = 1;
56 LDAP *ldap_open_with_timeout(const char *server, int port, unsigned int to)
58 LDAP *ldp = NULL;
61 DEBUG(10, ("Opening connection to LDAP server '%s:%d', timeout "
62 "%u seconds\n", server, port, to));
64 /* Setup timeout */
65 gotalarm = 0;
66 CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
67 alarm(to);
68 /* End setup timeout. */
70 ldp = ldap_open(server, port);
72 if (ldp == NULL) {
73 DEBUG(2,("Could not open connection to LDAP server %s:%d: %s\n",
74 server, port, strerror(errno)));
75 } else {
76 DEBUG(10, ("Connected to LDAP server '%s:%d'\n", server, port));
79 /* Teardown timeout. */
80 CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
81 alarm(0);
83 return ldp;
86 static int ldap_search_with_timeout(LDAP *ld,
87 LDAP_CONST char *base,
88 int scope,
89 LDAP_CONST char *filter,
90 char **attrs,
91 int attrsonly,
92 LDAPControl **sctrls,
93 LDAPControl **cctrls,
94 int sizelimit,
95 LDAPMessage **res )
97 struct timeval timeout;
98 int result;
100 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
101 timeout.tv_sec = lp_ldap_timeout();
102 timeout.tv_usec = 0;
104 /* Setup alarm timeout.... Do we need both of these ? JRA. */
105 gotalarm = 0;
106 CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
107 alarm(lp_ldap_timeout());
108 /* End setup timeout. */
110 result = ldap_search_ext_s(ld, base, scope, filter, attrs,
111 attrsonly, sctrls, cctrls, &timeout,
112 sizelimit, res);
114 /* Teardown timeout. */
115 CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
116 alarm(0);
118 if (gotalarm != 0)
119 return LDAP_TIMELIMIT_EXCEEDED;
121 return result;
124 /**********************************************
125 Do client and server sitename match ?
126 **********************************************/
128 bool ads_sitename_match(ADS_STRUCT *ads)
130 if (ads->config.server_site_name == NULL &&
131 ads->config.client_site_name == NULL ) {
132 DEBUG(10,("ads_sitename_match: both null\n"));
133 return True;
135 if (ads->config.server_site_name &&
136 ads->config.client_site_name &&
137 strequal(ads->config.server_site_name,
138 ads->config.client_site_name)) {
139 DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
140 return True;
142 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
143 ads->config.server_site_name ? ads->config.server_site_name : "NULL",
144 ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
145 return False;
148 /**********************************************
149 Is this the closest DC ?
150 **********************************************/
152 bool ads_closest_dc(ADS_STRUCT *ads)
154 if (ads->config.flags & NBT_SERVER_CLOSEST) {
155 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag set\n"));
156 return True;
159 /* not sure if this can ever happen */
160 if (ads_sitename_match(ads)) {
161 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag not set but sites match\n"));
162 return True;
165 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
166 ads->config.ldap_server_name));
168 return False;
173 try a connection to a given ldap server, returning True and setting the servers IP
174 in the ads struct if successful
176 static bool ads_try_connect(ADS_STRUCT *ads, const char *server, bool gc)
178 char *srv;
179 struct nbt_cldap_netlogon_5 cldap_reply;
180 TALLOC_CTX *mem_ctx = NULL;
181 bool ret = false;
183 if (!server || !*server) {
184 return False;
187 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
188 server, ads->server.realm));
190 mem_ctx = talloc_init("ads_try_connect");
191 if (!mem_ctx) {
192 DEBUG(0,("out of memory\n"));
193 return false;
196 /* this copes with inet_ntoa brokenness */
198 srv = SMB_STRDUP(server);
200 ZERO_STRUCT( cldap_reply );
202 if ( !ads_cldap_netlogon_5(mem_ctx, srv, ads->server.realm, &cldap_reply ) ) {
203 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", srv));
204 ret = false;
205 goto out;
208 /* Check the CLDAP reply flags */
210 if ( !(cldap_reply.server_type & NBT_SERVER_LDAP) ) {
211 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
212 srv));
213 ret = false;
214 goto out;
217 /* Fill in the ads->config values */
219 SAFE_FREE(ads->config.realm);
220 SAFE_FREE(ads->config.bind_path);
221 SAFE_FREE(ads->config.ldap_server_name);
222 SAFE_FREE(ads->config.server_site_name);
223 SAFE_FREE(ads->config.client_site_name);
224 SAFE_FREE(ads->server.workgroup);
226 ads->config.flags = cldap_reply.server_type;
227 ads->config.ldap_server_name = SMB_STRDUP(cldap_reply.pdc_dns_name);
228 ads->config.realm = SMB_STRDUP(cldap_reply.dns_domain);
229 strupper_m(ads->config.realm);
230 ads->config.bind_path = ads_build_dn(ads->config.realm);
231 if (*cldap_reply.server_site) {
232 ads->config.server_site_name =
233 SMB_STRDUP(cldap_reply.server_site);
235 if (*cldap_reply.client_site) {
236 ads->config.client_site_name =
237 SMB_STRDUP(cldap_reply.client_site);
239 ads->server.workgroup = SMB_STRDUP(cldap_reply.domain);
241 ads->ldap.port = gc ? LDAP_GC_PORT : LDAP_PORT;
242 if (!interpret_string_addr(&ads->ldap.ss, srv, 0)) {
243 DEBUG(1,("ads_try_connect: unable to convert %s "
244 "to an address\n",
245 srv));
246 ret = false;
247 goto out;
250 /* Store our site name. */
251 sitename_store( cldap_reply.domain, cldap_reply.client_site);
252 sitename_store( cldap_reply.dns_domain, cldap_reply.client_site);
254 ret = true;
255 out:
256 SAFE_FREE(srv);
257 TALLOC_FREE(mem_ctx);
259 return ret;
262 /**********************************************************************
263 Try to find an AD dc using our internal name resolution routines
264 Try the realm first and then then workgroup name if netbios is not
265 disabled
266 **********************************************************************/
268 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
270 const char *c_realm;
271 int count, i=0;
272 struct ip_service *ip_list;
273 const char *realm;
274 bool got_realm = False;
275 bool use_own_domain = False;
276 char *sitename;
277 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
279 /* if the realm and workgroup are both empty, assume they are ours */
281 /* realm */
282 c_realm = ads->server.realm;
284 if ( !c_realm || !*c_realm ) {
285 /* special case where no realm and no workgroup means our own */
286 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
287 use_own_domain = True;
288 c_realm = lp_realm();
292 if (c_realm && *c_realm)
293 got_realm = True;
295 /* we need to try once with the realm name and fallback to the
296 netbios domain name if we fail (if netbios has not been disabled */
298 if ( !got_realm && !lp_disable_netbios() ) {
299 c_realm = ads->server.workgroup;
300 if (!c_realm || !*c_realm) {
301 if ( use_own_domain )
302 c_realm = lp_workgroup();
305 if ( !c_realm || !*c_realm ) {
306 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
307 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
311 realm = c_realm;
313 sitename = sitename_fetch(realm);
315 again:
317 DEBUG(6,("ads_find_dc: looking for %s '%s'\n",
318 (got_realm ? "realm" : "domain"), realm));
320 status = get_sorted_dc_list(realm, sitename, &ip_list, &count, got_realm);
321 if (!NT_STATUS_IS_OK(status)) {
322 /* fall back to netbios if we can */
323 if ( got_realm && !lp_disable_netbios() ) {
324 got_realm = False;
325 goto again;
328 SAFE_FREE(sitename);
329 return status;
332 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
333 for ( i=0; i<count; i++ ) {
334 char server[INET6_ADDRSTRLEN];
336 print_sockaddr(server, sizeof(server), &ip_list[i].ss);
338 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
339 continue;
341 if (!got_realm) {
342 /* realm in this case is a workgroup name. We need
343 to ignore any IP addresses in the negative connection
344 cache that match ip addresses returned in the ad realm
345 case. It sucks that I have to reproduce the logic above... */
346 c_realm = ads->server.realm;
347 if ( !c_realm || !*c_realm ) {
348 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
349 c_realm = lp_realm();
352 if (c_realm && *c_realm &&
353 !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm, server))) {
354 /* Ensure we add the workgroup name for this
355 IP address as negative too. */
356 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
357 continue;
361 if ( ads_try_connect(ads, server, false) ) {
362 SAFE_FREE(ip_list);
363 SAFE_FREE(sitename);
364 return NT_STATUS_OK;
367 /* keep track of failures */
368 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
371 SAFE_FREE(ip_list);
373 /* In case we failed to contact one of our closest DC on our site we
374 * need to try to find another DC, retry with a site-less SRV DNS query
375 * - Guenther */
377 if (sitename) {
378 DEBUG(1,("ads_find_dc: failed to find a valid DC on our site (%s), "
379 "trying to find another DC\n", sitename));
380 SAFE_FREE(sitename);
381 namecache_delete(realm, 0x1C);
382 goto again;
385 return NT_STATUS_NO_LOGON_SERVERS;
388 /*********************************************************************
389 *********************************************************************/
391 static NTSTATUS ads_lookup_site(void)
393 ADS_STRUCT *ads = NULL;
394 ADS_STATUS ads_status;
395 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
397 ads = ads_init(lp_realm(), NULL, NULL);
398 if (!ads) {
399 return NT_STATUS_NO_MEMORY;
402 /* The NO_BIND here will find a DC and set the client site
403 but not establish the TCP connection */
405 ads->auth.flags = ADS_AUTH_NO_BIND;
406 ads_status = ads_connect(ads);
407 if (!ADS_ERR_OK(ads_status)) {
408 DEBUG(4, ("ads_lookup_site: ads_connect to our realm failed! (%s)\n",
409 ads_errstr(ads_status)));
411 nt_status = ads_ntstatus(ads_status);
413 if (ads) {
414 ads_destroy(&ads);
417 return nt_status;
420 /*********************************************************************
421 *********************************************************************/
423 static const char* host_dns_domain(const char *fqdn)
425 const char *p = fqdn;
427 /* go to next char following '.' */
429 if ((p = strchr_m(fqdn, '.')) != NULL) {
430 p++;
433 return p;
438 * Connect to the Global Catalog server
439 * @param ads Pointer to an existing ADS_STRUCT
440 * @return status of connection
442 * Simple wrapper around ads_connect() that fills in the
443 * GC ldap server information
446 ADS_STATUS ads_connect_gc(ADS_STRUCT *ads)
448 TALLOC_CTX *frame = talloc_stackframe();
449 struct dns_rr_srv *gcs_list;
450 int num_gcs;
451 char *realm = ads->server.realm;
452 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
453 ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
454 int i;
455 bool done = false;
456 char *sitename = NULL;
458 if (!realm)
459 realm = lp_realm();
461 if ((sitename = sitename_fetch(realm)) == NULL) {
462 ads_lookup_site();
463 sitename = sitename_fetch(realm);
466 do {
467 /* We try once with a sitename and once without
468 (unless we don't have a sitename and then we're
469 done */
471 if (sitename == NULL)
472 done = true;
474 nt_status = ads_dns_query_gcs(frame, realm, sitename,
475 &gcs_list, &num_gcs);
477 SAFE_FREE(sitename);
479 if (!NT_STATUS_IS_OK(nt_status)) {
480 ads_status = ADS_ERROR_NT(nt_status);
481 goto done;
484 /* Loop until we get a successful connection or have gone
485 through them all. When connecting a GC server, make sure that
486 the realm is the server's DNS name and not the forest root */
488 for (i=0; i<num_gcs; i++) {
489 ads->server.gc = true;
490 ads->server.ldap_server = SMB_STRDUP(gcs_list[i].hostname);
491 ads->server.realm = SMB_STRDUP(host_dns_domain(ads->server.ldap_server));
492 ads_status = ads_connect(ads);
493 if (ADS_ERR_OK(ads_status)) {
494 /* Reset the bind_dn to "". A Global Catalog server
495 may host multiple domain trees in a forest.
496 Windows 2003 GC server will accept "" as the search
497 path to imply search all domain trees in the forest */
499 SAFE_FREE(ads->config.bind_path);
500 ads->config.bind_path = SMB_STRDUP("");
503 goto done;
505 SAFE_FREE(ads->server.ldap_server);
506 SAFE_FREE(ads->server.realm);
509 TALLOC_FREE(gcs_list);
510 num_gcs = 0;
511 } while (!done);
513 done:
514 SAFE_FREE(sitename);
515 talloc_destroy(frame);
517 return ads_status;
522 * Connect to the LDAP server
523 * @param ads Pointer to an existing ADS_STRUCT
524 * @return status of connection
526 ADS_STATUS ads_connect(ADS_STRUCT *ads)
528 int version = LDAP_VERSION3;
529 ADS_STATUS status;
530 NTSTATUS ntstatus;
531 char addr[INET6_ADDRSTRLEN];
533 ZERO_STRUCT(ads->ldap);
534 ads->ldap.last_attempt = time(NULL);
535 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
537 /* try with a user specified server */
539 if (DEBUGLEVEL >= 11) {
540 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
541 DEBUG(11,("ads_connect: entering\n"));
542 DEBUGADD(11,("%s\n", s));
543 TALLOC_FREE(s);
546 if (ads->server.ldap_server &&
547 ads_try_connect(ads, ads->server.ldap_server, ads->server.gc)) {
548 goto got_connection;
551 ntstatus = ads_find_dc(ads);
552 if (NT_STATUS_IS_OK(ntstatus)) {
553 goto got_connection;
556 status = ADS_ERROR_NT(ntstatus);
557 goto out;
559 got_connection:
561 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
562 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
564 if (!ads->auth.user_name) {
565 /* Must use the userPrincipalName value here or sAMAccountName
566 and not servicePrincipalName; found by Guenther Deschner */
568 asprintf(&ads->auth.user_name, "%s$", global_myname() );
571 if (!ads->auth.realm) {
572 ads->auth.realm = SMB_STRDUP(ads->config.realm);
575 if (!ads->auth.kdc_server) {
576 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
577 ads->auth.kdc_server = SMB_STRDUP(addr);
580 #if KRB5_DNS_HACK
581 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
582 to MIT kerberos to work (tridge) */
584 char *env;
585 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
586 setenv(env, ads->auth.kdc_server, 1);
587 free(env);
589 #endif
591 /* If the caller() requested no LDAP bind, then we are done */
593 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
594 status = ADS_SUCCESS;
595 goto out;
598 ads->ldap.mem_ctx = talloc_init("ads LDAP connection memory");
599 if (!ads->ldap.mem_ctx) {
600 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
601 goto out;
604 /* Otherwise setup the TCP LDAP session */
606 ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name,
607 ads->ldap.port, lp_ldap_timeout());
608 if (ads->ldap.ld == NULL) {
609 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
610 goto out;
612 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
614 /* cache the successful connection for workgroup and realm */
615 if (ads_closest_dc(ads)) {
616 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
617 saf_store( ads->server.workgroup, addr);
618 saf_store( ads->server.realm, addr);
621 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
623 status = ADS_ERROR(smb_ldap_start_tls(ads->ldap.ld, version));
624 if (!ADS_ERR_OK(status)) {
625 goto out;
628 /* fill in the current time and offsets */
630 status = ads_current_time( ads );
631 if ( !ADS_ERR_OK(status) ) {
632 goto out;
635 /* Now do the bind */
637 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
638 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
639 goto out;
642 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
643 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
644 goto out;
647 status = ads_sasl_bind(ads);
649 out:
650 if (DEBUGLEVEL >= 11) {
651 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
652 DEBUG(11,("ads_connect: leaving with: %s\n",
653 ads_errstr(status)));
654 DEBUGADD(11,("%s\n", s));
655 TALLOC_FREE(s);
658 return status;
662 * Connect to the LDAP server using given credentials
663 * @param ads Pointer to an existing ADS_STRUCT
664 * @return status of connection
666 ADS_STATUS ads_connect_user_creds(ADS_STRUCT *ads)
668 ads->auth.flags |= ADS_AUTH_USER_CREDS;
670 return ads_connect(ads);
674 * Disconnect the LDAP server
675 * @param ads Pointer to an existing ADS_STRUCT
677 void ads_disconnect(ADS_STRUCT *ads)
679 if (ads->ldap.ld) {
680 ldap_unbind(ads->ldap.ld);
681 ads->ldap.ld = NULL;
683 if (ads->ldap.wrap_ops && ads->ldap.wrap_ops->disconnect) {
684 ads->ldap.wrap_ops->disconnect(ads);
686 if (ads->ldap.mem_ctx) {
687 talloc_free(ads->ldap.mem_ctx);
689 ZERO_STRUCT(ads->ldap);
693 Duplicate a struct berval into talloc'ed memory
695 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
697 struct berval *value;
699 if (!in_val) return NULL;
701 value = TALLOC_ZERO_P(ctx, struct berval);
702 if (value == NULL)
703 return NULL;
704 if (in_val->bv_len == 0) return value;
706 value->bv_len = in_val->bv_len;
707 value->bv_val = (char *)TALLOC_MEMDUP(ctx, in_val->bv_val,
708 in_val->bv_len);
709 return value;
713 Make a values list out of an array of (struct berval *)
715 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
716 const struct berval **in_vals)
718 struct berval **values;
719 int i;
721 if (!in_vals) return NULL;
722 for (i=0; in_vals[i]; i++)
723 ; /* count values */
724 values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
725 if (!values) return NULL;
727 for (i=0; in_vals[i]; i++) {
728 values[i] = dup_berval(ctx, in_vals[i]);
730 return values;
734 UTF8-encode a values list out of an array of (char *)
736 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
738 char **values;
739 int i;
740 size_t size;
742 if (!in_vals) return NULL;
743 for (i=0; in_vals[i]; i++)
744 ; /* count values */
745 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
746 if (!values) return NULL;
748 for (i=0; in_vals[i]; i++) {
749 if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
750 TALLOC_FREE(values);
751 return NULL;
754 return values;
758 Pull a (char *) array out of a UTF8-encoded values list
760 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
762 char **values;
763 int i;
764 size_t converted_size;
766 if (!in_vals) return NULL;
767 for (i=0; in_vals[i]; i++)
768 ; /* count values */
769 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
770 if (!values) return NULL;
772 for (i=0; in_vals[i]; i++) {
773 if (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
774 &converted_size)) {
775 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
776 "%s", strerror(errno)));
779 return values;
783 * Do a search with paged results. cookie must be null on the first
784 * call, and then returned on each subsequent call. It will be null
785 * again when the entire search is complete
786 * @param ads connection to ads server
787 * @param bind_path Base dn for the search
788 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
789 * @param expr Search expression - specified in local charset
790 * @param attrs Attributes to retrieve - specified in utf8 or ascii
791 * @param res ** which will contain results - free res* with ads_msgfree()
792 * @param count Number of entries retrieved on this page
793 * @param cookie The paged results cookie to be returned on subsequent calls
794 * @return status of search
796 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
797 const char *bind_path,
798 int scope, const char *expr,
799 const char **attrs, void *args,
800 LDAPMessage **res,
801 int *count, struct berval **cookie)
803 int rc, i, version;
804 char *utf8_expr, *utf8_path, **search_attrs;
805 size_t converted_size;
806 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
807 BerElement *cookie_be = NULL;
808 struct berval *cookie_bv= NULL;
809 BerElement *ext_be = NULL;
810 struct berval *ext_bv= NULL;
812 TALLOC_CTX *ctx;
813 ads_control *external_control = (ads_control *) args;
815 *res = NULL;
817 if (!(ctx = talloc_init("ads_do_paged_search_args")))
818 return ADS_ERROR(LDAP_NO_MEMORY);
820 /* 0 means the conversion worked but the result was empty
821 so we only fail if it's -1. In any case, it always
822 at least nulls out the dest */
823 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
824 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
826 rc = LDAP_NO_MEMORY;
827 goto done;
830 if (!attrs || !(*attrs))
831 search_attrs = NULL;
832 else {
833 /* This would be the utf8-encoded version...*/
834 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
835 if (!(str_list_copy(talloc_tos(), &search_attrs, attrs))) {
836 rc = LDAP_NO_MEMORY;
837 goto done;
841 /* Paged results only available on ldap v3 or later */
842 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
843 if (version < LDAP_VERSION3) {
844 rc = LDAP_NOT_SUPPORTED;
845 goto done;
848 cookie_be = ber_alloc_t(LBER_USE_DER);
849 if (*cookie) {
850 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
851 ber_bvfree(*cookie); /* don't need it from last time */
852 *cookie = NULL;
853 } else {
854 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
856 ber_flatten(cookie_be, &cookie_bv);
857 PagedResults.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID);
858 PagedResults.ldctl_iscritical = (char) 1;
859 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
860 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
862 NoReferrals.ldctl_oid = CONST_DISCARD(char *, ADS_NO_REFERRALS_OID);
863 NoReferrals.ldctl_iscritical = (char) 0;
864 NoReferrals.ldctl_value.bv_len = 0;
865 NoReferrals.ldctl_value.bv_val = CONST_DISCARD(char *, "");
867 if (external_control &&
868 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
869 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
871 ExternalCtrl.ldctl_oid = CONST_DISCARD(char *, external_control->control);
872 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
874 /* win2k does not accept a ldctl_value beeing passed in */
876 if (external_control->val != 0) {
878 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
879 rc = LDAP_NO_MEMORY;
880 goto done;
883 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
884 rc = LDAP_NO_MEMORY;
885 goto done;
887 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
888 rc = LDAP_NO_MEMORY;
889 goto done;
892 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
893 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
895 } else {
896 ExternalCtrl.ldctl_value.bv_len = 0;
897 ExternalCtrl.ldctl_value.bv_val = NULL;
900 controls[0] = &NoReferrals;
901 controls[1] = &PagedResults;
902 controls[2] = &ExternalCtrl;
903 controls[3] = NULL;
905 } else {
906 controls[0] = &NoReferrals;
907 controls[1] = &PagedResults;
908 controls[2] = NULL;
911 /* we need to disable referrals as the openldap libs don't
912 handle them and paged results at the same time. Using them
913 together results in the result record containing the server
914 page control being removed from the result list (tridge/jmcd)
916 leaving this in despite the control that says don't generate
917 referrals, in case the server doesn't support it (jmcd)
919 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
921 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
922 search_attrs, 0, controls,
923 NULL, LDAP_NO_LIMIT,
924 (LDAPMessage **)res);
926 ber_free(cookie_be, 1);
927 ber_bvfree(cookie_bv);
929 if (rc) {
930 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
931 ldap_err2string(rc)));
932 goto done;
935 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
936 NULL, &rcontrols, 0);
938 if (!rcontrols) {
939 goto done;
942 for (i=0; rcontrols[i]; i++) {
943 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
944 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
945 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
946 &cookie_bv);
947 /* the berval is the cookie, but must be freed when
948 it is all done */
949 if (cookie_bv->bv_len) /* still more to do */
950 *cookie=ber_bvdup(cookie_bv);
951 else
952 *cookie=NULL;
953 ber_bvfree(cookie_bv);
954 ber_free(cookie_be, 1);
955 break;
958 ldap_controls_free(rcontrols);
960 done:
961 talloc_destroy(ctx);
963 if (ext_be) {
964 ber_free(ext_be, 1);
967 if (ext_bv) {
968 ber_bvfree(ext_bv);
971 /* if/when we decide to utf8-encode attrs, take out this next line */
972 TALLOC_FREE(search_attrs);
974 return ADS_ERROR(rc);
977 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
978 int scope, const char *expr,
979 const char **attrs, LDAPMessage **res,
980 int *count, struct berval **cookie)
982 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
987 * Get all results for a search. This uses ads_do_paged_search() to return
988 * all entries in a large search.
989 * @param ads connection to ads server
990 * @param bind_path Base dn for the search
991 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
992 * @param expr Search expression
993 * @param attrs Attributes to retrieve
994 * @param res ** which will contain results - free res* with ads_msgfree()
995 * @return status of search
997 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
998 int scope, const char *expr,
999 const char **attrs, void *args,
1000 LDAPMessage **res)
1002 struct berval *cookie = NULL;
1003 int count = 0;
1004 ADS_STATUS status;
1006 *res = NULL;
1007 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1008 &count, &cookie);
1010 if (!ADS_ERR_OK(status))
1011 return status;
1013 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1014 while (cookie) {
1015 LDAPMessage *res2 = NULL;
1016 ADS_STATUS status2;
1017 LDAPMessage *msg, *next;
1019 status2 = ads_do_paged_search_args(ads, bind_path, scope, expr,
1020 attrs, args, &res2, &count, &cookie);
1022 if (!ADS_ERR_OK(status2)) break;
1024 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1025 that this works on all ldap libs, but I have only tested with openldap */
1026 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1027 next = ads_next_message(ads, msg);
1028 ldap_add_result_entry((LDAPMessage **)res, msg);
1030 /* note that we do not free res2, as the memory is now
1031 part of the main returned list */
1033 #else
1034 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1035 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1036 #endif
1038 return status;
1041 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1042 int scope, const char *expr,
1043 const char **attrs, LDAPMessage **res)
1045 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1048 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1049 int scope, const char *expr,
1050 const char **attrs, uint32 sd_flags,
1051 LDAPMessage **res)
1053 ads_control args;
1055 args.control = ADS_SD_FLAGS_OID;
1056 args.val = sd_flags;
1057 args.critical = True;
1059 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1064 * Run a function on all results for a search. Uses ads_do_paged_search() and
1065 * runs the function as each page is returned, using ads_process_results()
1066 * @param ads connection to ads server
1067 * @param bind_path Base dn for the search
1068 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1069 * @param expr Search expression - specified in local charset
1070 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1071 * @param fn Function which takes attr name, values list, and data_area
1072 * @param data_area Pointer which is passed to function on each call
1073 * @return status of search
1075 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1076 int scope, const char *expr, const char **attrs,
1077 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1078 void *data_area)
1080 struct berval *cookie = NULL;
1081 int count = 0;
1082 ADS_STATUS status;
1083 LDAPMessage *res;
1085 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1086 &count, &cookie);
1088 if (!ADS_ERR_OK(status)) return status;
1090 ads_process_results(ads, res, fn, data_area);
1091 ads_msgfree(ads, res);
1093 while (cookie) {
1094 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1095 &res, &count, &cookie);
1097 if (!ADS_ERR_OK(status)) break;
1099 ads_process_results(ads, res, fn, data_area);
1100 ads_msgfree(ads, res);
1103 return status;
1107 * Do a search with a timeout.
1108 * @param ads connection to ads server
1109 * @param bind_path Base dn for the search
1110 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1111 * @param expr Search expression
1112 * @param attrs Attributes to retrieve
1113 * @param res ** which will contain results - free res* with ads_msgfree()
1114 * @return status of search
1116 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1117 const char *expr,
1118 const char **attrs, LDAPMessage **res)
1120 int rc;
1121 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1122 size_t converted_size;
1123 TALLOC_CTX *ctx;
1125 *res = NULL;
1126 if (!(ctx = talloc_init("ads_do_search"))) {
1127 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1128 return ADS_ERROR(LDAP_NO_MEMORY);
1131 /* 0 means the conversion worked but the result was empty
1132 so we only fail if it's negative. In any case, it always
1133 at least nulls out the dest */
1134 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1135 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1137 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1138 rc = LDAP_NO_MEMORY;
1139 goto done;
1142 if (!attrs || !(*attrs))
1143 search_attrs = NULL;
1144 else {
1145 /* This would be the utf8-encoded version...*/
1146 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1147 if (!(str_list_copy(talloc_tos(), &search_attrs, attrs)))
1149 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1150 rc = LDAP_NO_MEMORY;
1151 goto done;
1155 /* see the note in ads_do_paged_search - we *must* disable referrals */
1156 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1158 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1159 search_attrs, 0, NULL, NULL,
1160 LDAP_NO_LIMIT,
1161 (LDAPMessage **)res);
1163 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1164 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1165 rc = 0;
1168 done:
1169 talloc_destroy(ctx);
1170 /* if/when we decide to utf8-encode attrs, take out this next line */
1171 TALLOC_FREE(search_attrs);
1172 return ADS_ERROR(rc);
1175 * Do a general ADS search
1176 * @param ads connection to ads server
1177 * @param res ** which will contain results - free res* with ads_msgfree()
1178 * @param expr Search expression
1179 * @param attrs Attributes to retrieve
1180 * @return status of search
1182 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1183 const char *expr, const char **attrs)
1185 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1186 expr, attrs, res);
1190 * Do a search on a specific DistinguishedName
1191 * @param ads connection to ads server
1192 * @param res ** which will contain results - free res* with ads_msgfree()
1193 * @param dn DistinguishName to search
1194 * @param attrs Attributes to retrieve
1195 * @return status of search
1197 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1198 const char *dn, const char **attrs)
1200 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1201 attrs, res);
1205 * Free up memory from a ads_search
1206 * @param ads connection to ads server
1207 * @param msg Search results to free
1209 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1211 if (!msg) return;
1212 ldap_msgfree(msg);
1216 * Free up memory from various ads requests
1217 * @param ads connection to ads server
1218 * @param mem Area to free
1220 void ads_memfree(ADS_STRUCT *ads, void *mem)
1222 SAFE_FREE(mem);
1226 * Get a dn from search results
1227 * @param ads connection to ads server
1228 * @param msg Search result
1229 * @return dn string
1231 char *ads_get_dn(ADS_STRUCT *ads, LDAPMessage *msg)
1233 char *utf8_dn, *unix_dn;
1234 size_t converted_size;
1236 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1238 if (!utf8_dn) {
1239 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1240 return NULL;
1243 if (!pull_utf8_allocate(&unix_dn, utf8_dn, &converted_size)) {
1244 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1245 utf8_dn ));
1246 return NULL;
1248 ldap_memfree(utf8_dn);
1249 return unix_dn;
1253 * Get the parent from a dn
1254 * @param dn the dn to return the parent from
1255 * @return parent dn string
1257 char *ads_parent_dn(const char *dn)
1259 char *p;
1261 if (dn == NULL) {
1262 return NULL;
1265 p = strchr(dn, ',');
1267 if (p == NULL) {
1268 return NULL;
1271 return p+1;
1275 * Find a machine account given a hostname
1276 * @param ads connection to ads server
1277 * @param res ** which will contain results - free res* with ads_msgfree()
1278 * @param host Hostname to search for
1279 * @return status of search
1281 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1282 const char *machine)
1284 ADS_STATUS status;
1285 char *expr;
1286 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1288 *res = NULL;
1290 /* the easiest way to find a machine account anywhere in the tree
1291 is to look for hostname$ */
1292 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1293 DEBUG(1, ("asprintf failed!\n"));
1294 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1297 status = ads_search(ads, res, expr, attrs);
1298 SAFE_FREE(expr);
1299 return status;
1303 * Initialize a list of mods to be used in a modify request
1304 * @param ctx An initialized TALLOC_CTX
1305 * @return allocated ADS_MODLIST
1307 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1309 #define ADS_MODLIST_ALLOC_SIZE 10
1310 LDAPMod **mods;
1312 if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1313 /* -1 is safety to make sure we don't go over the end.
1314 need to reset it to NULL before doing ldap modify */
1315 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1317 return (ADS_MODLIST)mods;
1322 add an attribute to the list, with values list already constructed
1324 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1325 int mod_op, const char *name,
1326 const void *_invals)
1328 const void **invals = (const void **)_invals;
1329 int curmod;
1330 LDAPMod **modlist = (LDAPMod **) *mods;
1331 struct berval **ber_values = NULL;
1332 char **char_values = NULL;
1334 if (!invals) {
1335 mod_op = LDAP_MOD_DELETE;
1336 } else {
1337 if (mod_op & LDAP_MOD_BVALUES)
1338 ber_values = ads_dup_values(ctx,
1339 (const struct berval **)invals);
1340 else
1341 char_values = ads_push_strvals(ctx,
1342 (const char **) invals);
1345 /* find the first empty slot */
1346 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1347 curmod++);
1348 if (modlist[curmod] == (LDAPMod *) -1) {
1349 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
1350 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1351 return ADS_ERROR(LDAP_NO_MEMORY);
1352 memset(&modlist[curmod], 0,
1353 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1354 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1355 *mods = (ADS_MODLIST)modlist;
1358 if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
1359 return ADS_ERROR(LDAP_NO_MEMORY);
1360 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1361 if (mod_op & LDAP_MOD_BVALUES) {
1362 modlist[curmod]->mod_bvalues = ber_values;
1363 } else if (mod_op & LDAP_MOD_DELETE) {
1364 modlist[curmod]->mod_values = NULL;
1365 } else {
1366 modlist[curmod]->mod_values = char_values;
1369 modlist[curmod]->mod_op = mod_op;
1370 return ADS_ERROR(LDAP_SUCCESS);
1374 * Add a single string value to a mod list
1375 * @param ctx An initialized TALLOC_CTX
1376 * @param mods An initialized ADS_MODLIST
1377 * @param name The attribute name to add
1378 * @param val The value to add - NULL means DELETE
1379 * @return ADS STATUS indicating success of add
1381 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1382 const char *name, const char *val)
1384 const char *values[2];
1386 values[0] = val;
1387 values[1] = NULL;
1389 if (!val)
1390 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1391 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1395 * Add an array of string values to a mod list
1396 * @param ctx An initialized TALLOC_CTX
1397 * @param mods An initialized ADS_MODLIST
1398 * @param name The attribute name to add
1399 * @param vals The array of string values to add - NULL means DELETE
1400 * @return ADS STATUS indicating success of add
1402 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1403 const char *name, const char **vals)
1405 if (!vals)
1406 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1407 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1408 name, (const void **) vals);
1411 #if 0
1413 * Add a single ber-encoded value to a mod list
1414 * @param ctx An initialized TALLOC_CTX
1415 * @param mods An initialized ADS_MODLIST
1416 * @param name The attribute name to add
1417 * @param val The value to add - NULL means DELETE
1418 * @return ADS STATUS indicating success of add
1420 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1421 const char *name, const struct berval *val)
1423 const struct berval *values[2];
1425 values[0] = val;
1426 values[1] = NULL;
1427 if (!val)
1428 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1429 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1430 name, (const void **) values);
1432 #endif
1435 * Perform an ldap modify
1436 * @param ads connection to ads server
1437 * @param mod_dn DistinguishedName to modify
1438 * @param mods list of modifications to perform
1439 * @return status of modify
1441 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1443 int ret,i;
1444 char *utf8_dn = NULL;
1445 size_t converted_size;
1447 this control is needed to modify that contains a currently
1448 non-existent attribute (but allowable for the object) to run
1450 LDAPControl PermitModify = {
1451 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
1452 {0, NULL},
1453 (char) 1};
1454 LDAPControl *controls[2];
1456 controls[0] = &PermitModify;
1457 controls[1] = NULL;
1459 if (!push_utf8_allocate(&utf8_dn, mod_dn, &converted_size)) {
1460 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1463 /* find the end of the list, marked by NULL or -1 */
1464 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1465 /* make sure the end of the list is NULL */
1466 mods[i] = NULL;
1467 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1468 (LDAPMod **) mods, controls, NULL);
1469 SAFE_FREE(utf8_dn);
1470 return ADS_ERROR(ret);
1474 * Perform an ldap add
1475 * @param ads connection to ads server
1476 * @param new_dn DistinguishedName to add
1477 * @param mods list of attributes and values for DN
1478 * @return status of add
1480 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1482 int ret, i;
1483 char *utf8_dn = NULL;
1484 size_t converted_size;
1486 if (!push_utf8_allocate(&utf8_dn, new_dn, &converted_size)) {
1487 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1488 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1491 /* find the end of the list, marked by NULL or -1 */
1492 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1493 /* make sure the end of the list is NULL */
1494 mods[i] = NULL;
1496 ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1497 SAFE_FREE(utf8_dn);
1498 return ADS_ERROR(ret);
1502 * Delete a DistinguishedName
1503 * @param ads connection to ads server
1504 * @param new_dn DistinguishedName to delete
1505 * @return status of delete
1507 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1509 int ret;
1510 char *utf8_dn = NULL;
1511 size_t converted_size;
1512 if (!push_utf8_allocate(&utf8_dn, del_dn, &converted_size)) {
1513 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1514 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1517 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1518 SAFE_FREE(utf8_dn);
1519 return ADS_ERROR(ret);
1523 * Build an org unit string
1524 * if org unit is Computers or blank then assume a container, otherwise
1525 * assume a / separated list of organisational units.
1526 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1527 * @param ads connection to ads server
1528 * @param org_unit Organizational unit
1529 * @return org unit string - caller must free
1531 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1533 char *ret = NULL;
1535 if (!org_unit || !*org_unit) {
1537 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1539 /* samba4 might not yet respond to a wellknownobject-query */
1540 return ret ? ret : SMB_STRDUP("cn=Computers");
1543 if (strequal(org_unit, "Computers")) {
1544 return SMB_STRDUP("cn=Computers");
1547 /* jmcd: removed "\\" from the separation chars, because it is
1548 needed as an escape for chars like '#' which are valid in an
1549 OU name */
1550 return ads_build_path(org_unit, "/", "ou=", 1);
1554 * Get a org unit string for a well-known GUID
1555 * @param ads connection to ads server
1556 * @param wknguid Well known GUID
1557 * @return org unit string - caller must free
1559 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1561 ADS_STATUS status;
1562 LDAPMessage *res = NULL;
1563 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1564 **bind_dn_exp = NULL;
1565 const char *attrs[] = {"distinguishedName", NULL};
1566 int new_ln, wkn_ln, bind_ln, i;
1568 if (wknguid == NULL) {
1569 return NULL;
1572 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1573 DEBUG(1, ("asprintf failed!\n"));
1574 return NULL;
1577 status = ads_search_dn(ads, &res, base, attrs);
1578 if (!ADS_ERR_OK(status)) {
1579 DEBUG(1,("Failed while searching for: %s\n", base));
1580 goto out;
1583 if (ads_count_replies(ads, res) != 1) {
1584 goto out;
1587 /* substitute the bind-path from the well-known-guid-search result */
1588 wkn_dn = ads_get_dn(ads, res);
1589 if (!wkn_dn) {
1590 goto out;
1593 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1594 if (!wkn_dn_exp) {
1595 goto out;
1598 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1599 if (!bind_dn_exp) {
1600 goto out;
1603 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1605 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1608 new_ln = wkn_ln - bind_ln;
1610 ret = SMB_STRDUP(wkn_dn_exp[0]);
1611 if (!ret) {
1612 goto out;
1615 for (i=1; i < new_ln; i++) {
1616 char *s = NULL;
1618 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1619 SAFE_FREE(ret);
1620 goto out;
1623 SAFE_FREE(ret);
1624 ret = SMB_STRDUP(s);
1625 free(s);
1626 if (!ret) {
1627 goto out;
1631 out:
1632 SAFE_FREE(base);
1633 ads_msgfree(ads, res);
1634 ads_memfree(ads, wkn_dn);
1635 if (wkn_dn_exp) {
1636 ldap_value_free(wkn_dn_exp);
1638 if (bind_dn_exp) {
1639 ldap_value_free(bind_dn_exp);
1642 return ret;
1646 * Adds (appends) an item to an attribute array, rather then
1647 * replacing the whole list
1648 * @param ctx An initialized TALLOC_CTX
1649 * @param mods An initialized ADS_MODLIST
1650 * @param name name of the ldap attribute to append to
1651 * @param vals an array of values to add
1652 * @return status of addition
1655 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1656 const char *name, const char **vals)
1658 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1659 (const void *) vals);
1663 * Determines the an account's current KVNO via an LDAP lookup
1664 * @param ads An initialized ADS_STRUCT
1665 * @param account_name the NT samaccountname.
1666 * @return the kvno for the account, or -1 in case of a failure.
1669 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
1671 LDAPMessage *res = NULL;
1672 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1673 char *filter;
1674 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1675 char *dn_string = NULL;
1676 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1678 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
1679 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
1680 return kvno;
1682 ret = ads_search(ads, &res, filter, attrs);
1683 SAFE_FREE(filter);
1684 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
1685 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
1686 ads_msgfree(ads, res);
1687 return kvno;
1690 dn_string = ads_get_dn(ads, res);
1691 if (!dn_string) {
1692 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1693 ads_msgfree(ads, res);
1694 return kvno;
1696 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1697 ads_memfree(ads, dn_string);
1699 /* ---------------------------------------------------------
1700 * 0 is returned as a default KVNO from this point on...
1701 * This is done because Windows 2000 does not support key
1702 * version numbers. Chances are that a failure in the next
1703 * step is simply due to Windows 2000 being used for a
1704 * domain controller. */
1705 kvno = 0;
1707 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1708 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1709 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1710 ads_msgfree(ads, res);
1711 return kvno;
1714 /* Success */
1715 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1716 ads_msgfree(ads, res);
1717 return kvno;
1721 * Determines the computer account's current KVNO via an LDAP lookup
1722 * @param ads An initialized ADS_STRUCT
1723 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1724 * @return the kvno for the computer account, or -1 in case of a failure.
1727 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
1729 char *computer_account = NULL;
1730 uint32_t kvno = -1;
1732 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
1733 return kvno;
1736 kvno = ads_get_kvno(ads, computer_account);
1737 free(computer_account);
1739 return kvno;
1743 * This clears out all registered spn's for a given hostname
1744 * @param ads An initilaized ADS_STRUCT
1745 * @param machine_name the NetBIOS name of the computer.
1746 * @return 0 upon success, non-zero otherwise.
1749 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1751 TALLOC_CTX *ctx;
1752 LDAPMessage *res = NULL;
1753 ADS_MODLIST mods;
1754 const char *servicePrincipalName[1] = {NULL};
1755 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1756 char *dn_string = NULL;
1758 ret = ads_find_machine_acct(ads, &res, machine_name);
1759 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1760 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1761 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1762 ads_msgfree(ads, res);
1763 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1766 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1767 ctx = talloc_init("ads_clear_service_principal_names");
1768 if (!ctx) {
1769 ads_msgfree(ads, res);
1770 return ADS_ERROR(LDAP_NO_MEMORY);
1773 if (!(mods = ads_init_mods(ctx))) {
1774 talloc_destroy(ctx);
1775 ads_msgfree(ads, res);
1776 return ADS_ERROR(LDAP_NO_MEMORY);
1778 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1779 if (!ADS_ERR_OK(ret)) {
1780 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1781 ads_msgfree(ads, res);
1782 talloc_destroy(ctx);
1783 return ret;
1785 dn_string = ads_get_dn(ads, res);
1786 if (!dn_string) {
1787 talloc_destroy(ctx);
1788 ads_msgfree(ads, res);
1789 return ADS_ERROR(LDAP_NO_MEMORY);
1791 ret = ads_gen_mod(ads, dn_string, mods);
1792 ads_memfree(ads,dn_string);
1793 if (!ADS_ERR_OK(ret)) {
1794 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1795 machine_name));
1796 ads_msgfree(ads, res);
1797 talloc_destroy(ctx);
1798 return ret;
1801 ads_msgfree(ads, res);
1802 talloc_destroy(ctx);
1803 return ret;
1807 * This adds a service principal name to an existing computer account
1808 * (found by hostname) in AD.
1809 * @param ads An initialized ADS_STRUCT
1810 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1811 * @param my_fqdn The fully qualified DNS name of the machine
1812 * @param spn A string of the service principal to add, i.e. 'host'
1813 * @return 0 upon sucess, or non-zero if a failure occurs
1816 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1817 const char *my_fqdn, const char *spn)
1819 ADS_STATUS ret;
1820 TALLOC_CTX *ctx;
1821 LDAPMessage *res = NULL;
1822 char *psp1, *psp2;
1823 ADS_MODLIST mods;
1824 char *dn_string = NULL;
1825 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1827 ret = ads_find_machine_acct(ads, &res, machine_name);
1828 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1829 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1830 machine_name));
1831 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1832 spn, machine_name, ads->config.realm));
1833 ads_msgfree(ads, res);
1834 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1837 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1838 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1839 ads_msgfree(ads, res);
1840 return ADS_ERROR(LDAP_NO_MEMORY);
1843 /* add short name spn */
1845 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1846 talloc_destroy(ctx);
1847 ads_msgfree(ads, res);
1848 return ADS_ERROR(LDAP_NO_MEMORY);
1850 strupper_m(psp1);
1851 strlower_m(&psp1[strlen(spn)]);
1852 servicePrincipalName[0] = psp1;
1854 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1855 psp1, machine_name));
1858 /* add fully qualified spn */
1860 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1861 ret = ADS_ERROR(LDAP_NO_MEMORY);
1862 goto out;
1864 strupper_m(psp2);
1865 strlower_m(&psp2[strlen(spn)]);
1866 servicePrincipalName[1] = psp2;
1868 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1869 psp2, machine_name));
1871 if ( (mods = ads_init_mods(ctx)) == NULL ) {
1872 ret = ADS_ERROR(LDAP_NO_MEMORY);
1873 goto out;
1876 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1877 if (!ADS_ERR_OK(ret)) {
1878 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1879 goto out;
1882 if ( (dn_string = ads_get_dn(ads, res)) == NULL ) {
1883 ret = ADS_ERROR(LDAP_NO_MEMORY);
1884 goto out;
1887 ret = ads_gen_mod(ads, dn_string, mods);
1888 ads_memfree(ads,dn_string);
1889 if (!ADS_ERR_OK(ret)) {
1890 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1891 goto out;
1894 out:
1895 TALLOC_FREE( ctx );
1896 ads_msgfree(ads, res);
1897 return ret;
1901 * adds a machine account to the ADS server
1902 * @param ads An intialized ADS_STRUCT
1903 * @param machine_name - the NetBIOS machine name of this account.
1904 * @param account_type A number indicating the type of account to create
1905 * @param org_unit The LDAP path in which to place this account
1906 * @return 0 upon success, or non-zero otherwise
1909 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1910 const char *org_unit)
1912 ADS_STATUS ret;
1913 char *samAccountName, *controlstr;
1914 TALLOC_CTX *ctx;
1915 ADS_MODLIST mods;
1916 char *machine_escaped = NULL;
1917 char *new_dn;
1918 const char *objectClass[] = {"top", "person", "organizationalPerson",
1919 "user", "computer", NULL};
1920 LDAPMessage *res = NULL;
1921 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
1922 UF_DONT_EXPIRE_PASSWD |\
1923 UF_ACCOUNTDISABLE );
1925 if (!(ctx = talloc_init("ads_add_machine_acct")))
1926 return ADS_ERROR(LDAP_NO_MEMORY);
1928 ret = ADS_ERROR(LDAP_NO_MEMORY);
1930 machine_escaped = escape_rdn_val_string_alloc(machine_name);
1931 if (!machine_escaped) {
1932 goto done;
1935 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
1936 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
1938 if ( !new_dn || !samAccountName ) {
1939 goto done;
1942 #ifndef ENCTYPE_ARCFOUR_HMAC
1943 acct_control |= UF_USE_DES_KEY_ONLY;
1944 #endif
1946 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
1947 goto done;
1950 if (!(mods = ads_init_mods(ctx))) {
1951 goto done;
1954 ads_mod_str(ctx, &mods, "cn", machine_name);
1955 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1956 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1957 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1959 ret = ads_gen_add(ads, new_dn, mods);
1961 done:
1962 SAFE_FREE(machine_escaped);
1963 ads_msgfree(ads, res);
1964 talloc_destroy(ctx);
1966 return ret;
1970 * move a machine account to another OU on the ADS server
1971 * @param ads - An intialized ADS_STRUCT
1972 * @param machine_name - the NetBIOS machine name of this account.
1973 * @param org_unit - The LDAP path in which to place this account
1974 * @param moved - whether we moved the machine account (optional)
1975 * @return 0 upon success, or non-zero otherwise
1978 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1979 const char *org_unit, bool *moved)
1981 ADS_STATUS rc;
1982 int ldap_status;
1983 LDAPMessage *res = NULL;
1984 char *filter = NULL;
1985 char *computer_dn = NULL;
1986 char *parent_dn;
1987 char *computer_rdn = NULL;
1988 bool need_move = False;
1990 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1991 rc = ADS_ERROR(LDAP_NO_MEMORY);
1992 goto done;
1995 /* Find pre-existing machine */
1996 rc = ads_search(ads, &res, filter, NULL);
1997 if (!ADS_ERR_OK(rc)) {
1998 goto done;
2001 computer_dn = ads_get_dn(ads, res);
2002 if (!computer_dn) {
2003 rc = ADS_ERROR(LDAP_NO_MEMORY);
2004 goto done;
2007 parent_dn = ads_parent_dn(computer_dn);
2008 if (strequal(parent_dn, org_unit)) {
2009 goto done;
2012 need_move = True;
2014 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2015 rc = ADS_ERROR(LDAP_NO_MEMORY);
2016 goto done;
2019 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2020 org_unit, 1, NULL, NULL);
2021 rc = ADS_ERROR(ldap_status);
2023 done:
2024 ads_msgfree(ads, res);
2025 SAFE_FREE(filter);
2026 SAFE_FREE(computer_dn);
2027 SAFE_FREE(computer_rdn);
2029 if (!ADS_ERR_OK(rc)) {
2030 need_move = False;
2033 if (moved) {
2034 *moved = need_move;
2037 return rc;
2041 dump a binary result from ldap
2043 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2045 int i, j;
2046 for (i=0; values[i]; i++) {
2047 printf("%s: ", field);
2048 for (j=0; j<values[i]->bv_len; j++) {
2049 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2051 printf("\n");
2055 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2057 int i;
2058 for (i=0; values[i]; i++) {
2060 UUID_FLAT guid;
2061 struct GUID tmp;
2063 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
2064 smb_uuid_unpack(guid, &tmp);
2065 printf("%s: %s\n", field, smb_uuid_string(talloc_tos(), tmp));
2070 dump a sid result from ldap
2072 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2074 int i;
2075 for (i=0; values[i]; i++) {
2076 DOM_SID sid;
2077 fstring tmp;
2078 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
2079 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
2084 dump ntSecurityDescriptor
2086 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2088 TALLOC_CTX *frame = talloc_stackframe();
2089 struct security_descriptor *psd;
2090 NTSTATUS status;
2092 status = unmarshall_sec_desc(talloc_tos(), (uint8 *)values[0]->bv_val,
2093 values[0]->bv_len, &psd);
2094 if (!NT_STATUS_IS_OK(status)) {
2095 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2096 nt_errstr(status)));
2097 TALLOC_FREE(frame);
2098 return;
2101 if (psd) {
2102 ads_disp_sd(ads, talloc_tos(), psd);
2105 TALLOC_FREE(frame);
2109 dump a string result from ldap
2111 static void dump_string(const char *field, char **values)
2113 int i;
2114 for (i=0; values[i]; i++) {
2115 printf("%s: %s\n", field, values[i]);
2120 dump a field from LDAP on stdout
2121 used for debugging
2124 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2126 const struct {
2127 const char *name;
2128 bool string;
2129 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2130 } handlers[] = {
2131 {"objectGUID", False, dump_guid},
2132 {"netbootGUID", False, dump_guid},
2133 {"nTSecurityDescriptor", False, dump_sd},
2134 {"dnsRecord", False, dump_binary},
2135 {"objectSid", False, dump_sid},
2136 {"tokenGroups", False, dump_sid},
2137 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2138 {"tokengroupsGlobalandUniversal", False, dump_sid},
2139 {"mS-DS-CreatorSID", False, dump_sid},
2140 {"msExchMailboxGuid", False, dump_guid},
2141 {NULL, True, NULL}
2143 int i;
2145 if (!field) { /* must be end of an entry */
2146 printf("\n");
2147 return False;
2150 for (i=0; handlers[i].name; i++) {
2151 if (StrCaseCmp(handlers[i].name, field) == 0) {
2152 if (!values) /* first time, indicate string or not */
2153 return handlers[i].string;
2154 handlers[i].handler(ads, field, (struct berval **) values);
2155 break;
2158 if (!handlers[i].name) {
2159 if (!values) /* first time, indicate string conversion */
2160 return True;
2161 dump_string(field, (char **)values);
2163 return False;
2167 * Dump a result from LDAP on stdout
2168 * used for debugging
2169 * @param ads connection to ads server
2170 * @param res Results to dump
2173 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2175 ads_process_results(ads, res, ads_dump_field, NULL);
2179 * Walk through results, calling a function for each entry found.
2180 * The function receives a field name, a berval * array of values,
2181 * and a data area passed through from the start. The function is
2182 * called once with null for field and values at the end of each
2183 * entry.
2184 * @param ads connection to ads server
2185 * @param res Results to process
2186 * @param fn Function for processing each result
2187 * @param data_area user-defined area to pass to function
2189 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2190 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2191 void *data_area)
2193 LDAPMessage *msg;
2194 TALLOC_CTX *ctx;
2195 size_t converted_size;
2197 if (!(ctx = talloc_init("ads_process_results")))
2198 return;
2200 for (msg = ads_first_entry(ads, res); msg;
2201 msg = ads_next_entry(ads, msg)) {
2202 char *utf8_field;
2203 BerElement *b;
2205 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2206 (LDAPMessage *)msg,&b);
2207 utf8_field;
2208 utf8_field=ldap_next_attribute(ads->ldap.ld,
2209 (LDAPMessage *)msg,b)) {
2210 struct berval **ber_vals;
2211 char **str_vals, **utf8_vals;
2212 char *field;
2213 bool string;
2215 if (!pull_utf8_talloc(ctx, &field, utf8_field,
2216 &converted_size))
2218 DEBUG(0,("ads_process_results: "
2219 "pull_utf8_talloc failed: %s",
2220 strerror(errno)));
2223 string = fn(ads, field, NULL, data_area);
2225 if (string) {
2226 utf8_vals = ldap_get_values(ads->ldap.ld,
2227 (LDAPMessage *)msg, field);
2228 str_vals = ads_pull_strvals(ctx,
2229 (const char **) utf8_vals);
2230 fn(ads, field, (void **) str_vals, data_area);
2231 ldap_value_free(utf8_vals);
2232 } else {
2233 ber_vals = ldap_get_values_len(ads->ldap.ld,
2234 (LDAPMessage *)msg, field);
2235 fn(ads, field, (void **) ber_vals, data_area);
2237 ldap_value_free_len(ber_vals);
2239 ldap_memfree(utf8_field);
2241 ber_free(b, 0);
2242 talloc_free_children(ctx);
2243 fn(ads, NULL, NULL, data_area); /* completed an entry */
2246 talloc_destroy(ctx);
2250 * count how many replies are in a LDAPMessage
2251 * @param ads connection to ads server
2252 * @param res Results to count
2253 * @return number of replies
2255 int ads_count_replies(ADS_STRUCT *ads, void *res)
2257 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2261 * pull the first entry from a ADS result
2262 * @param ads connection to ads server
2263 * @param res Results of search
2264 * @return first entry from result
2266 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2268 return ldap_first_entry(ads->ldap.ld, res);
2272 * pull the next entry from a ADS result
2273 * @param ads connection to ads server
2274 * @param res Results of search
2275 * @return next entry from result
2277 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2279 return ldap_next_entry(ads->ldap.ld, res);
2283 * pull the first message from a ADS result
2284 * @param ads connection to ads server
2285 * @param res Results of search
2286 * @return first message from result
2288 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2290 return ldap_first_message(ads->ldap.ld, res);
2294 * pull the next message from a ADS result
2295 * @param ads connection to ads server
2296 * @param res Results of search
2297 * @return next message from result
2299 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2301 return ldap_next_message(ads->ldap.ld, res);
2305 * pull a single string from a ADS result
2306 * @param ads connection to ads server
2307 * @param mem_ctx TALLOC_CTX to use for allocating result string
2308 * @param msg Results of search
2309 * @param field Attribute to retrieve
2310 * @return Result string in talloc context
2312 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2313 const char *field)
2315 char **values;
2316 char *ret = NULL;
2317 char *ux_string;
2318 size_t converted_size;
2320 values = ldap_get_values(ads->ldap.ld, msg, field);
2321 if (!values)
2322 return NULL;
2324 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2325 &converted_size))
2327 ret = ux_string;
2329 ldap_value_free(values);
2330 return ret;
2334 * pull an array of strings from a ADS result
2335 * @param ads connection to ads server
2336 * @param mem_ctx TALLOC_CTX to use for allocating result string
2337 * @param msg Results of search
2338 * @param field Attribute to retrieve
2339 * @return Result strings in talloc context
2341 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2342 LDAPMessage *msg, const char *field,
2343 size_t *num_values)
2345 char **values;
2346 char **ret = NULL;
2347 int i;
2348 size_t converted_size;
2350 values = ldap_get_values(ads->ldap.ld, msg, field);
2351 if (!values)
2352 return NULL;
2354 *num_values = ldap_count_values(values);
2356 ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
2357 if (!ret) {
2358 ldap_value_free(values);
2359 return NULL;
2362 for (i=0;i<*num_values;i++) {
2363 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2364 &converted_size))
2366 ldap_value_free(values);
2367 return NULL;
2370 ret[i] = NULL;
2372 ldap_value_free(values);
2373 return ret;
2377 * pull an array of strings from a ADS result
2378 * (handle large multivalue attributes with range retrieval)
2379 * @param ads connection to ads server
2380 * @param mem_ctx TALLOC_CTX to use for allocating result string
2381 * @param msg Results of search
2382 * @param field Attribute to retrieve
2383 * @param current_strings strings returned by a previous call to this function
2384 * @param next_attribute The next query should ask for this attribute
2385 * @param num_values How many values did we get this time?
2386 * @param more_values Are there more values to get?
2387 * @return Result strings in talloc context
2389 char **ads_pull_strings_range(ADS_STRUCT *ads,
2390 TALLOC_CTX *mem_ctx,
2391 LDAPMessage *msg, const char *field,
2392 char **current_strings,
2393 const char **next_attribute,
2394 size_t *num_strings,
2395 bool *more_strings)
2397 char *attr;
2398 char *expected_range_attrib, *range_attr;
2399 BerElement *ptr = NULL;
2400 char **strings;
2401 char **new_strings;
2402 size_t num_new_strings;
2403 unsigned long int range_start;
2404 unsigned long int range_end;
2406 /* we might have been given the whole lot anyway */
2407 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2408 *more_strings = False;
2409 return strings;
2412 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2414 /* look for Range result */
2415 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2416 attr;
2417 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2418 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2419 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2420 range_attr = attr;
2421 break;
2423 ldap_memfree(attr);
2425 if (!attr) {
2426 ber_free(ptr, 0);
2427 /* nothing here - this field is just empty */
2428 *more_strings = False;
2429 return NULL;
2432 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2433 &range_start, &range_end) == 2) {
2434 *more_strings = True;
2435 } else {
2436 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2437 &range_start) == 1) {
2438 *more_strings = False;
2439 } else {
2440 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2441 range_attr));
2442 ldap_memfree(range_attr);
2443 *more_strings = False;
2444 return NULL;
2448 if ((*num_strings) != range_start) {
2449 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2450 " - aborting range retreival\n",
2451 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2452 ldap_memfree(range_attr);
2453 *more_strings = False;
2454 return NULL;
2457 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2459 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2460 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2461 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2462 range_attr, (unsigned long int)range_end - range_start + 1,
2463 (unsigned long int)num_new_strings));
2464 ldap_memfree(range_attr);
2465 *more_strings = False;
2466 return NULL;
2469 strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2470 *num_strings + num_new_strings);
2472 if (strings == NULL) {
2473 ldap_memfree(range_attr);
2474 *more_strings = False;
2475 return NULL;
2478 if (new_strings && num_new_strings) {
2479 memcpy(&strings[*num_strings], new_strings,
2480 sizeof(*new_strings) * num_new_strings);
2483 (*num_strings) += num_new_strings;
2485 if (*more_strings) {
2486 *next_attribute = talloc_asprintf(mem_ctx,
2487 "%s;range=%d-*",
2488 field,
2489 (int)*num_strings);
2491 if (!*next_attribute) {
2492 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2493 ldap_memfree(range_attr);
2494 *more_strings = False;
2495 return NULL;
2499 ldap_memfree(range_attr);
2501 return strings;
2505 * pull a single uint32 from a ADS result
2506 * @param ads connection to ads server
2507 * @param msg Results of search
2508 * @param field Attribute to retrieve
2509 * @param v Pointer to int to store result
2510 * @return boolean inidicating success
2512 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2513 uint32 *v)
2515 char **values;
2517 values = ldap_get_values(ads->ldap.ld, msg, field);
2518 if (!values)
2519 return False;
2520 if (!values[0]) {
2521 ldap_value_free(values);
2522 return False;
2525 *v = atoi(values[0]);
2526 ldap_value_free(values);
2527 return True;
2531 * pull a single objectGUID from an ADS result
2532 * @param ads connection to ADS server
2533 * @param msg results of search
2534 * @param guid 37-byte area to receive text guid
2535 * @return boolean indicating success
2537 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2539 char **values;
2540 UUID_FLAT flat_guid;
2542 values = ldap_get_values(ads->ldap.ld, msg, "objectGUID");
2543 if (!values)
2544 return False;
2546 if (values[0]) {
2547 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2548 smb_uuid_unpack(flat_guid, guid);
2549 ldap_value_free(values);
2550 return True;
2552 ldap_value_free(values);
2553 return False;
2559 * pull a single DOM_SID from a ADS result
2560 * @param ads connection to ads server
2561 * @param msg Results of search
2562 * @param field Attribute to retrieve
2563 * @param sid Pointer to sid to store result
2564 * @return boolean inidicating success
2566 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2567 DOM_SID *sid)
2569 struct berval **values;
2570 bool ret = False;
2572 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2574 if (!values)
2575 return False;
2577 if (values[0])
2578 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2580 ldap_value_free_len(values);
2581 return ret;
2585 * pull an array of DOM_SIDs from a ADS result
2586 * @param ads connection to ads server
2587 * @param mem_ctx TALLOC_CTX for allocating sid array
2588 * @param msg Results of search
2589 * @param field Attribute to retrieve
2590 * @param sids pointer to sid array to allocate
2591 * @return the count of SIDs pulled
2593 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2594 LDAPMessage *msg, const char *field, DOM_SID **sids)
2596 struct berval **values;
2597 bool ret;
2598 int count, i;
2600 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2602 if (!values)
2603 return 0;
2605 for (i=0; values[i]; i++)
2606 /* nop */ ;
2608 if (i) {
2609 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2610 if (!(*sids)) {
2611 ldap_value_free_len(values);
2612 return 0;
2614 } else {
2615 (*sids) = NULL;
2618 count = 0;
2619 for (i=0; values[i]; i++) {
2620 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2621 if (ret) {
2622 DEBUG(10, ("pulling SID: %s\n",
2623 sid_string_dbg(&(*sids)[count])));
2624 count++;
2628 ldap_value_free_len(values);
2629 return count;
2633 * pull a SEC_DESC from a ADS result
2634 * @param ads connection to ads server
2635 * @param mem_ctx TALLOC_CTX for allocating sid array
2636 * @param msg Results of search
2637 * @param field Attribute to retrieve
2638 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2639 * @return boolean inidicating success
2641 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2642 LDAPMessage *msg, const char *field, SEC_DESC **sd)
2644 struct berval **values;
2645 bool ret = true;
2647 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2649 if (!values) return false;
2651 if (values[0]) {
2652 NTSTATUS status;
2653 status = unmarshall_sec_desc(mem_ctx,
2654 (uint8 *)values[0]->bv_val,
2655 values[0]->bv_len, sd);
2656 if (!NT_STATUS_IS_OK(status)) {
2657 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2658 nt_errstr(status)));
2659 ret = false;
2663 ldap_value_free_len(values);
2664 return ret;
2668 * in order to support usernames longer than 21 characters we need to
2669 * use both the sAMAccountName and the userPrincipalName attributes
2670 * It seems that not all users have the userPrincipalName attribute set
2672 * @param ads connection to ads server
2673 * @param mem_ctx TALLOC_CTX for allocating sid array
2674 * @param msg Results of search
2675 * @return the username
2677 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2678 LDAPMessage *msg)
2680 #if 0 /* JERRY */
2681 char *ret, *p;
2683 /* lookup_name() only works on the sAMAccountName to
2684 returning the username portion of userPrincipalName
2685 breaks winbindd_getpwnam() */
2687 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2688 if (ret && (p = strchr_m(ret, '@'))) {
2689 *p = 0;
2690 return ret;
2692 #endif
2693 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2698 * find the update serial number - this is the core of the ldap cache
2699 * @param ads connection to ads server
2700 * @param ads connection to ADS server
2701 * @param usn Pointer to retrieved update serial number
2702 * @return status of search
2704 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2706 const char *attrs[] = {"highestCommittedUSN", NULL};
2707 ADS_STATUS status;
2708 LDAPMessage *res;
2710 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2711 if (!ADS_ERR_OK(status))
2712 return status;
2714 if (ads_count_replies(ads, res) != 1) {
2715 ads_msgfree(ads, res);
2716 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2719 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2720 ads_msgfree(ads, res);
2721 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2724 ads_msgfree(ads, res);
2725 return ADS_SUCCESS;
2728 /* parse a ADS timestring - typical string is
2729 '20020917091222.0Z0' which means 09:12.22 17th September
2730 2002, timezone 0 */
2731 static time_t ads_parse_time(const char *str)
2733 struct tm tm;
2735 ZERO_STRUCT(tm);
2737 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2738 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2739 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2740 return 0;
2742 tm.tm_year -= 1900;
2743 tm.tm_mon -= 1;
2745 return timegm(&tm);
2748 /********************************************************************
2749 ********************************************************************/
2751 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2753 const char *attrs[] = {"currentTime", NULL};
2754 ADS_STATUS status;
2755 LDAPMessage *res;
2756 char *timestr;
2757 TALLOC_CTX *ctx;
2758 ADS_STRUCT *ads_s = ads;
2760 if (!(ctx = talloc_init("ads_current_time"))) {
2761 return ADS_ERROR(LDAP_NO_MEMORY);
2764 /* establish a new ldap tcp session if necessary */
2766 if ( !ads->ldap.ld ) {
2767 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2768 ads->server.ldap_server )) == NULL )
2770 goto done;
2772 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2773 status = ads_connect( ads_s );
2774 if ( !ADS_ERR_OK(status))
2775 goto done;
2778 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2779 if (!ADS_ERR_OK(status)) {
2780 goto done;
2783 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2784 if (!timestr) {
2785 ads_msgfree(ads_s, res);
2786 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2787 goto done;
2790 /* but save the time and offset in the original ADS_STRUCT */
2792 ads->config.current_time = ads_parse_time(timestr);
2794 if (ads->config.current_time != 0) {
2795 ads->auth.time_offset = ads->config.current_time - time(NULL);
2796 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2799 ads_msgfree(ads, res);
2801 status = ADS_SUCCESS;
2803 done:
2804 /* free any temporary ads connections */
2805 if ( ads_s != ads ) {
2806 ads_destroy( &ads_s );
2808 talloc_destroy(ctx);
2810 return status;
2813 /********************************************************************
2814 ********************************************************************/
2816 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2818 const char *attrs[] = {"domainFunctionality", NULL};
2819 ADS_STATUS status;
2820 LDAPMessage *res;
2821 ADS_STRUCT *ads_s = ads;
2823 *val = DS_DOMAIN_FUNCTION_2000;
2825 /* establish a new ldap tcp session if necessary */
2827 if ( !ads->ldap.ld ) {
2828 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2829 ads->server.ldap_server )) == NULL )
2831 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2832 goto done;
2834 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2835 status = ads_connect( ads_s );
2836 if ( !ADS_ERR_OK(status))
2837 goto done;
2840 /* If the attribute does not exist assume it is a Windows 2000
2841 functional domain */
2843 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2844 if (!ADS_ERR_OK(status)) {
2845 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2846 status = ADS_SUCCESS;
2848 goto done;
2851 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2852 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2854 DEBUG(3,("ads_domain_func_level: %d\n", *val));
2857 ads_msgfree(ads, res);
2859 done:
2860 /* free any temporary ads connections */
2861 if ( ads_s != ads ) {
2862 ads_destroy( &ads_s );
2865 return status;
2869 * find the domain sid for our domain
2870 * @param ads connection to ads server
2871 * @param sid Pointer to domain sid
2872 * @return status of search
2874 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2876 const char *attrs[] = {"objectSid", NULL};
2877 LDAPMessage *res;
2878 ADS_STATUS rc;
2880 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2881 attrs, &res);
2882 if (!ADS_ERR_OK(rc)) return rc;
2883 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2884 ads_msgfree(ads, res);
2885 return ADS_ERROR_SYSTEM(ENOENT);
2887 ads_msgfree(ads, res);
2889 return ADS_SUCCESS;
2893 * find our site name
2894 * @param ads connection to ads server
2895 * @param mem_ctx Pointer to talloc context
2896 * @param site_name Pointer to the sitename
2897 * @return status of search
2899 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2901 ADS_STATUS status;
2902 LDAPMessage *res;
2903 const char *dn, *service_name;
2904 const char *attrs[] = { "dsServiceName", NULL };
2906 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2907 if (!ADS_ERR_OK(status)) {
2908 return status;
2911 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2912 if (service_name == NULL) {
2913 ads_msgfree(ads, res);
2914 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2917 ads_msgfree(ads, res);
2919 /* go up three levels */
2920 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2921 if (dn == NULL) {
2922 return ADS_ERROR(LDAP_NO_MEMORY);
2925 *site_name = talloc_strdup(mem_ctx, dn);
2926 if (*site_name == NULL) {
2927 return ADS_ERROR(LDAP_NO_MEMORY);
2930 return status;
2932 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2937 * find the site dn where a machine resides
2938 * @param ads connection to ads server
2939 * @param mem_ctx Pointer to talloc context
2940 * @param computer_name name of the machine
2941 * @param site_name Pointer to the sitename
2942 * @return status of search
2944 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
2946 ADS_STATUS status;
2947 LDAPMessage *res;
2948 const char *parent, *filter;
2949 char *config_context = NULL;
2950 char *dn;
2952 /* shortcut a query */
2953 if (strequal(computer_name, ads->config.ldap_server_name)) {
2954 return ads_site_dn(ads, mem_ctx, site_dn);
2957 status = ads_config_path(ads, mem_ctx, &config_context);
2958 if (!ADS_ERR_OK(status)) {
2959 return status;
2962 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
2963 if (filter == NULL) {
2964 return ADS_ERROR(LDAP_NO_MEMORY);
2967 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
2968 filter, NULL, &res);
2969 if (!ADS_ERR_OK(status)) {
2970 return status;
2973 if (ads_count_replies(ads, res) != 1) {
2974 ads_msgfree(ads, res);
2975 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2978 dn = ads_get_dn(ads, res);
2979 if (dn == NULL) {
2980 ads_msgfree(ads, res);
2981 return ADS_ERROR(LDAP_NO_MEMORY);
2984 /* go up three levels */
2985 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
2986 if (parent == NULL) {
2987 ads_msgfree(ads, res);
2988 ads_memfree(ads, dn);
2989 return ADS_ERROR(LDAP_NO_MEMORY);
2992 *site_dn = talloc_strdup(mem_ctx, parent);
2993 if (*site_dn == NULL) {
2994 ads_msgfree(ads, res);
2995 ads_memfree(ads, dn);
2996 return ADS_ERROR(LDAP_NO_MEMORY);
2999 ads_memfree(ads, dn);
3000 ads_msgfree(ads, res);
3002 return status;
3006 * get the upn suffixes for a domain
3007 * @param ads connection to ads server
3008 * @param mem_ctx Pointer to talloc context
3009 * @param suffixes Pointer to an array of suffixes
3010 * @param num_suffixes Pointer to the number of suffixes
3011 * @return status of search
3013 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3015 ADS_STATUS status;
3016 LDAPMessage *res;
3017 const char *base;
3018 char *config_context = NULL;
3019 const char *attrs[] = { "uPNSuffixes", NULL };
3021 status = ads_config_path(ads, mem_ctx, &config_context);
3022 if (!ADS_ERR_OK(status)) {
3023 return status;
3026 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3027 if (base == NULL) {
3028 return ADS_ERROR(LDAP_NO_MEMORY);
3031 status = ads_search_dn(ads, &res, base, attrs);
3032 if (!ADS_ERR_OK(status)) {
3033 return status;
3036 if (ads_count_replies(ads, res) != 1) {
3037 ads_msgfree(ads, res);
3038 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3041 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3042 if ((*suffixes) == NULL) {
3043 ads_msgfree(ads, res);
3044 return ADS_ERROR(LDAP_NO_MEMORY);
3047 ads_msgfree(ads, res);
3049 return status;
3053 * get the joinable ous for a domain
3054 * @param ads connection to ads server
3055 * @param mem_ctx Pointer to talloc context
3056 * @param ous Pointer to an array of ous
3057 * @param num_ous Pointer to the number of ous
3058 * @return status of search
3060 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3061 TALLOC_CTX *mem_ctx,
3062 char ***ous,
3063 size_t *num_ous)
3065 ADS_STATUS status;
3066 LDAPMessage *res = NULL;
3067 LDAPMessage *msg = NULL;
3068 const char *attrs[] = { "dn", NULL };
3069 int count = 0;
3071 status = ads_search(ads, &res,
3072 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3073 attrs);
3074 if (!ADS_ERR_OK(status)) {
3075 return status;
3078 count = ads_count_replies(ads, res);
3079 if (count < 1) {
3080 ads_msgfree(ads, res);
3081 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3084 for (msg = ads_first_entry(ads, res); msg;
3085 msg = ads_next_entry(ads, msg)) {
3087 char *dn = NULL;
3089 dn = ads_get_dn(ads, msg);
3090 if (!dn) {
3091 ads_msgfree(ads, res);
3092 return ADS_ERROR(LDAP_NO_MEMORY);
3095 if (!add_string_to_array(mem_ctx, dn,
3096 (const char ***)ous,
3097 (int *)num_ous)) {
3098 ads_memfree(ads, dn);
3099 ads_msgfree(ads, res);
3100 return ADS_ERROR(LDAP_NO_MEMORY);
3103 ads_memfree(ads, dn);
3106 ads_msgfree(ads, res);
3108 return status;
3113 * pull a DOM_SID from an extended dn string
3114 * @param mem_ctx TALLOC_CTX
3115 * @param extended_dn string
3116 * @param flags string type of extended_dn
3117 * @param sid pointer to a DOM_SID
3118 * @return NT_STATUS_OK on success,
3119 * NT_INVALID_PARAMETER on error,
3120 * NT_STATUS_NOT_FOUND if no SID present
3122 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3123 const char *extended_dn,
3124 enum ads_extended_dn_flags flags,
3125 DOM_SID *sid)
3127 char *p, *q, *dn;
3129 if (!extended_dn) {
3130 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3133 /* otherwise extended_dn gets stripped off */
3134 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3135 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3138 * ADS_EXTENDED_DN_HEX_STRING:
3139 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3141 * ADS_EXTENDED_DN_STRING (only with w2k3):
3142 * <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
3144 * Object with no SID, such as an Exchange Public Folder
3145 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3148 p = strchr(dn, ';');
3149 if (!p) {
3150 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3153 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3154 DEBUG(5,("No SID present in extended dn\n"));
3155 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3158 p += strlen(";<SID=");
3160 q = strchr(p, '>');
3161 if (!q) {
3162 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3165 *q = '\0';
3167 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3169 switch (flags) {
3171 case ADS_EXTENDED_DN_STRING:
3172 if (!string_to_sid(sid, p)) {
3173 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3175 break;
3176 case ADS_EXTENDED_DN_HEX_STRING: {
3177 fstring buf;
3178 size_t buf_len;
3180 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3181 if (buf_len == 0) {
3182 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3185 if (!sid_parse(buf, buf_len, sid)) {
3186 DEBUG(10,("failed to parse sid\n"));
3187 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3189 break;
3191 default:
3192 DEBUG(10,("unknown extended dn format\n"));
3193 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3196 return ADS_ERROR_NT(NT_STATUS_OK);
3200 * pull an array of DOM_SIDs from a ADS result
3201 * @param ads connection to ads server
3202 * @param mem_ctx TALLOC_CTX for allocating sid array
3203 * @param msg Results of search
3204 * @param field Attribute to retrieve
3205 * @param flags string type of extended_dn
3206 * @param sids pointer to sid array to allocate
3207 * @return the count of SIDs pulled
3209 int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads,
3210 TALLOC_CTX *mem_ctx,
3211 LDAPMessage *msg,
3212 const char *field,
3213 enum ads_extended_dn_flags flags,
3214 DOM_SID **sids)
3216 int i;
3217 ADS_STATUS rc;
3218 size_t dn_count, ret_count = 0;
3219 char **dn_strings;
3221 if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field,
3222 &dn_count)) == NULL) {
3223 return 0;
3226 (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, dn_count + 1);
3227 if (!(*sids)) {
3228 TALLOC_FREE(dn_strings);
3229 return 0;
3232 for (i=0; i<dn_count; i++) {
3233 rc = ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i],
3234 flags, &(*sids)[i]);
3235 if (!ADS_ERR_OK(rc)) {
3236 if (NT_STATUS_EQUAL(ads_ntstatus(rc),
3237 NT_STATUS_NOT_FOUND)) {
3238 continue;
3240 else {
3241 TALLOC_FREE(*sids);
3242 TALLOC_FREE(dn_strings);
3243 return 0;
3246 ret_count++;
3249 TALLOC_FREE(dn_strings);
3251 return ret_count;
3254 /********************************************************************
3255 ********************************************************************/
3257 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3259 LDAPMessage *res = NULL;
3260 ADS_STATUS status;
3261 int count = 0;
3262 char *name = NULL;
3264 status = ads_find_machine_acct(ads, &res, global_myname());
3265 if (!ADS_ERR_OK(status)) {
3266 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3267 global_myname()));
3268 goto out;
3271 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3272 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3273 goto out;
3276 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3277 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3280 out:
3281 ads_msgfree(ads, res);
3283 return name;
3286 /********************************************************************
3287 ********************************************************************/
3289 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3291 LDAPMessage *res = NULL;
3292 ADS_STATUS status;
3293 int count = 0;
3294 char *name = NULL;
3296 status = ads_find_machine_acct(ads, &res, machine_name);
3297 if (!ADS_ERR_OK(status)) {
3298 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3299 global_myname()));
3300 goto out;
3303 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3304 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3305 goto out;
3308 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3309 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3312 out:
3313 ads_msgfree(ads, res);
3315 return name;
3318 /********************************************************************
3319 ********************************************************************/
3321 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3323 LDAPMessage *res = NULL;
3324 ADS_STATUS status;
3325 int count = 0;
3326 char *name = NULL;
3328 status = ads_find_machine_acct(ads, &res, global_myname());
3329 if (!ADS_ERR_OK(status)) {
3330 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3331 global_myname()));
3332 goto out;
3335 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3336 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3337 goto out;
3340 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3341 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3344 out:
3345 ads_msgfree(ads, res);
3347 return name;
3350 #if 0
3352 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3355 * Join a machine to a realm
3356 * Creates the machine account and sets the machine password
3357 * @param ads connection to ads server
3358 * @param machine name of host to add
3359 * @param org_unit Organizational unit to place machine in
3360 * @return status of join
3362 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3363 uint32 account_type, const char *org_unit)
3365 ADS_STATUS status;
3366 LDAPMessage *res = NULL;
3367 char *machine;
3369 /* machine name must be lowercase */
3370 machine = SMB_STRDUP(machine_name);
3371 strlower_m(machine);
3374 status = ads_find_machine_acct(ads, (void **)&res, machine);
3375 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3376 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3377 status = ads_leave_realm(ads, machine);
3378 if (!ADS_ERR_OK(status)) {
3379 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3380 machine, ads->config.realm));
3381 return status;
3385 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3386 if (!ADS_ERR_OK(status)) {
3387 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3388 SAFE_FREE(machine);
3389 return status;
3392 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3393 if (!ADS_ERR_OK(status)) {
3394 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3395 SAFE_FREE(machine);
3396 return status;
3399 SAFE_FREE(machine);
3400 ads_msgfree(ads, res);
3402 return status;
3404 #endif
3407 * Delete a machine from the realm
3408 * @param ads connection to ads server
3409 * @param hostname Machine to remove
3410 * @return status of delete
3412 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3414 ADS_STATUS status;
3415 void *msg;
3416 LDAPMessage *res;
3417 char *hostnameDN, *host;
3418 int rc;
3419 LDAPControl ldap_control;
3420 LDAPControl * pldap_control[2] = {NULL, NULL};
3422 pldap_control[0] = &ldap_control;
3423 memset(&ldap_control, 0, sizeof(LDAPControl));
3424 ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
3426 /* hostname must be lowercase */
3427 host = SMB_STRDUP(hostname);
3428 strlower_m(host);
3430 status = ads_find_machine_acct(ads, &res, host);
3431 if (!ADS_ERR_OK(status)) {
3432 DEBUG(0, ("Host account for %s does not exist.\n", host));
3433 SAFE_FREE(host);
3434 return status;
3437 msg = ads_first_entry(ads, res);
3438 if (!msg) {
3439 SAFE_FREE(host);
3440 return ADS_ERROR_SYSTEM(ENOENT);
3443 hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
3445 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3446 if (rc) {
3447 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3448 }else {
3449 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3452 if (rc != LDAP_SUCCESS) {
3453 const char *attrs[] = { "cn", NULL };
3454 LDAPMessage *msg_sub;
3456 /* we only search with scope ONE, we do not expect any further
3457 * objects to be created deeper */
3459 status = ads_do_search_retry(ads, hostnameDN,
3460 LDAP_SCOPE_ONELEVEL,
3461 "(objectclass=*)", attrs, &res);
3463 if (!ADS_ERR_OK(status)) {
3464 SAFE_FREE(host);
3465 ads_memfree(ads, hostnameDN);
3466 return status;
3469 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3470 msg_sub = ads_next_entry(ads, msg_sub)) {
3472 char *dn = NULL;
3474 if ((dn = ads_get_dn(ads, msg_sub)) == NULL) {
3475 SAFE_FREE(host);
3476 ads_memfree(ads, hostnameDN);
3477 return ADS_ERROR(LDAP_NO_MEMORY);
3480 status = ads_del_dn(ads, dn);
3481 if (!ADS_ERR_OK(status)) {
3482 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3483 SAFE_FREE(host);
3484 ads_memfree(ads, dn);
3485 ads_memfree(ads, hostnameDN);
3486 return status;
3489 ads_memfree(ads, dn);
3492 /* there should be no subordinate objects anymore */
3493 status = ads_do_search_retry(ads, hostnameDN,
3494 LDAP_SCOPE_ONELEVEL,
3495 "(objectclass=*)", attrs, &res);
3497 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3498 SAFE_FREE(host);
3499 ads_memfree(ads, hostnameDN);
3500 return status;
3503 /* delete hostnameDN now */
3504 status = ads_del_dn(ads, hostnameDN);
3505 if (!ADS_ERR_OK(status)) {
3506 SAFE_FREE(host);
3507 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3508 ads_memfree(ads, hostnameDN);
3509 return status;
3513 ads_memfree(ads, hostnameDN);
3515 status = ads_find_machine_acct(ads, &res, host);
3516 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3517 DEBUG(3, ("Failed to remove host account.\n"));
3518 SAFE_FREE(host);
3519 return status;
3522 SAFE_FREE(host);
3523 return status;
3527 * pull all token-sids from an LDAP dn
3528 * @param ads connection to ads server
3529 * @param mem_ctx TALLOC_CTX for allocating sid array
3530 * @param dn of LDAP object
3531 * @param user_sid pointer to DOM_SID (objectSid)
3532 * @param primary_group_sid pointer to DOM_SID (self composed)
3533 * @param sids pointer to sid array to allocate
3534 * @param num_sids counter of SIDs pulled
3535 * @return status of token query
3537 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3538 TALLOC_CTX *mem_ctx,
3539 const char *dn,
3540 DOM_SID *user_sid,
3541 DOM_SID *primary_group_sid,
3542 DOM_SID **sids,
3543 size_t *num_sids)
3545 ADS_STATUS status;
3546 LDAPMessage *res = NULL;
3547 int count = 0;
3548 size_t tmp_num_sids;
3549 DOM_SID *tmp_sids;
3550 DOM_SID tmp_user_sid;
3551 DOM_SID tmp_primary_group_sid;
3552 uint32 pgid;
3553 const char *attrs[] = {
3554 "objectSid",
3555 "tokenGroups",
3556 "primaryGroupID",
3557 NULL
3560 status = ads_search_retry_dn(ads, &res, dn, attrs);
3561 if (!ADS_ERR_OK(status)) {
3562 return status;
3565 count = ads_count_replies(ads, res);
3566 if (count != 1) {
3567 ads_msgfree(ads, res);
3568 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3571 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3572 ads_msgfree(ads, res);
3573 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3576 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3577 ads_msgfree(ads, res);
3578 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3582 /* hack to compose the primary group sid without knowing the
3583 * domsid */
3585 DOM_SID domsid;
3586 uint32 dummy_rid;
3588 sid_copy(&domsid, &tmp_user_sid);
3590 if (!sid_split_rid(&domsid, &dummy_rid)) {
3591 ads_msgfree(ads, res);
3592 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3595 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3596 ads_msgfree(ads, res);
3597 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3601 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3603 if (tmp_num_sids == 0 || !tmp_sids) {
3604 ads_msgfree(ads, res);
3605 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3608 if (num_sids) {
3609 *num_sids = tmp_num_sids;
3612 if (sids) {
3613 *sids = tmp_sids;
3616 if (user_sid) {
3617 *user_sid = tmp_user_sid;
3620 if (primary_group_sid) {
3621 *primary_group_sid = tmp_primary_group_sid;
3624 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3626 ads_msgfree(ads, res);
3627 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3631 * Find a sAMAccoutName in LDAP
3632 * @param ads connection to ads server
3633 * @param mem_ctx TALLOC_CTX for allocating sid array
3634 * @param samaccountname to search
3635 * @param uac_ret uint32 pointer userAccountControl attribute value
3636 * @param dn_ret pointer to dn
3637 * @return status of token query
3639 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3640 TALLOC_CTX *mem_ctx,
3641 const char *samaccountname,
3642 uint32 *uac_ret,
3643 const char **dn_ret)
3645 ADS_STATUS status;
3646 const char *attrs[] = { "userAccountControl", NULL };
3647 const char *filter;
3648 LDAPMessage *res = NULL;
3649 char *dn = NULL;
3650 uint32 uac = 0;
3652 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3653 samaccountname);
3654 if (filter == NULL) {
3655 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3656 goto out;
3659 status = ads_do_search_all(ads, ads->config.bind_path,
3660 LDAP_SCOPE_SUBTREE,
3661 filter, attrs, &res);
3663 if (!ADS_ERR_OK(status)) {
3664 goto out;
3667 if (ads_count_replies(ads, res) != 1) {
3668 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3669 goto out;
3672 dn = ads_get_dn(ads, res);
3673 if (dn == NULL) {
3674 status = ADS_ERROR(LDAP_NO_MEMORY);
3675 goto out;
3678 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3679 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3680 goto out;
3683 if (uac_ret) {
3684 *uac_ret = uac;
3687 if (dn_ret) {
3688 *dn_ret = talloc_strdup(mem_ctx, dn);
3689 if (!*dn_ret) {
3690 status = ADS_ERROR(LDAP_NO_MEMORY);
3691 goto out;
3694 out:
3695 ads_memfree(ads, dn);
3696 ads_msgfree(ads, res);
3698 return status;
3702 * find our configuration path
3703 * @param ads connection to ads server
3704 * @param mem_ctx Pointer to talloc context
3705 * @param config_path Pointer to the config path
3706 * @return status of search
3708 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3709 TALLOC_CTX *mem_ctx,
3710 char **config_path)
3712 ADS_STATUS status;
3713 LDAPMessage *res = NULL;
3714 const char *config_context = NULL;
3715 const char *attrs[] = { "configurationNamingContext", NULL };
3717 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3718 "(objectclass=*)", attrs, &res);
3719 if (!ADS_ERR_OK(status)) {
3720 return status;
3723 config_context = ads_pull_string(ads, mem_ctx, res,
3724 "configurationNamingContext");
3725 ads_msgfree(ads, res);
3726 if (!config_context) {
3727 return ADS_ERROR(LDAP_NO_MEMORY);
3730 if (config_path) {
3731 *config_path = talloc_strdup(mem_ctx, config_context);
3732 if (!*config_path) {
3733 return ADS_ERROR(LDAP_NO_MEMORY);
3737 return ADS_ERROR(LDAP_SUCCESS);
3741 * find the displayName of an extended right
3742 * @param ads connection to ads server
3743 * @param config_path The config path
3744 * @param mem_ctx Pointer to talloc context
3745 * @param GUID struct of the rightsGUID
3746 * @return status of search
3748 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3749 const char *config_path,
3750 TALLOC_CTX *mem_ctx,
3751 const struct GUID *rights_guid)
3753 ADS_STATUS rc;
3754 LDAPMessage *res = NULL;
3755 char *expr = NULL;
3756 const char *attrs[] = { "displayName", NULL };
3757 const char *result = NULL;
3758 const char *path;
3760 if (!ads || !mem_ctx || !rights_guid) {
3761 goto done;
3764 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3765 smb_uuid_string(mem_ctx, *rights_guid));
3766 if (!expr) {
3767 goto done;
3770 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3771 if (!path) {
3772 goto done;
3775 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3776 expr, attrs, &res);
3777 if (!ADS_ERR_OK(rc)) {
3778 goto done;
3781 if (ads_count_replies(ads, res) != 1) {
3782 goto done;
3785 result = ads_pull_string(ads, mem_ctx, res, "displayName");
3787 done:
3788 ads_msgfree(ads, res);
3789 return result;
3794 * verify or build and verify an account ou
3795 * @param mem_ctx Pointer to talloc context
3796 * @param ads connection to ads server
3797 * @param account_ou
3798 * @return status of search
3801 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3802 ADS_STRUCT *ads,
3803 const char **account_ou)
3805 struct ldb_dn *name_dn = NULL;
3806 const char *name = NULL;
3807 char *ou_string = NULL;
3809 name_dn = ldb_dn_explode(mem_ctx, *account_ou);
3810 if (name_dn) {
3811 return ADS_SUCCESS;
3814 ou_string = ads_ou_string(ads, *account_ou);
3815 if (!ou_string) {
3816 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3819 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3820 ads->config.bind_path);
3821 SAFE_FREE(ou_string);
3822 if (!name) {
3823 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3826 name_dn = ldb_dn_explode(mem_ctx, name);
3827 if (!name_dn) {
3828 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3831 *account_ou = talloc_strdup(mem_ctx, name);
3832 if (!*account_ou) {
3833 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3836 return ADS_SUCCESS;
3839 #endif