r20839: Fix other C++ warnings
[Samba.git] / source / libads / ldap.c
blobc263e8e133bcef34c9b99eeb438eaea5224c5a97
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 2 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, write to the Free Software
22 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 #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;
60 /* Setup timeout */
61 gotalarm = 0;
62 CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
63 alarm(to);
64 /* End setup timeout. */
66 ldp = ldap_open(server, port);
68 if (ldp == NULL) {
69 DEBUG(2,("Could not open LDAP connection to %s:%d: %s\n",
70 server, port, strerror(errno)));
73 /* Teardown timeout. */
74 CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
75 alarm(0);
77 return ldp;
80 static int ldap_search_with_timeout(LDAP *ld,
81 LDAP_CONST char *base,
82 int scope,
83 LDAP_CONST char *filter,
84 char **attrs,
85 int attrsonly,
86 LDAPControl **sctrls,
87 LDAPControl **cctrls,
88 int sizelimit,
89 LDAPMessage **res )
91 struct timeval timeout;
92 int result;
94 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
95 timeout.tv_sec = lp_ldap_timeout();
96 timeout.tv_usec = 0;
98 /* Setup alarm timeout.... Do we need both of these ? JRA. */
99 gotalarm = 0;
100 CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
101 alarm(lp_ldap_timeout());
102 /* End setup timeout. */
104 result = ldap_search_ext_s(ld, base, scope, filter, attrs,
105 attrsonly, sctrls, cctrls, &timeout,
106 sizelimit, res);
108 /* Teardown timeout. */
109 CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
110 alarm(0);
112 if (gotalarm != 0)
113 return LDAP_TIMELIMIT_EXCEEDED;
115 return result;
118 /**********************************************
119 Do client and server sitename match ?
120 **********************************************/
122 BOOL ads_sitename_match(ADS_STRUCT *ads)
124 if (ads->config.server_site_name == NULL &&
125 ads->config.client_site_name == NULL ) {
126 DEBUG(10,("ads_sitename_match: both null\n"));
127 return True;
129 if (ads->config.server_site_name &&
130 ads->config.client_site_name &&
131 strequal(ads->config.server_site_name,
132 ads->config.client_site_name)) {
133 DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
134 return True;
136 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
137 ads->config.server_site_name ? ads->config.server_site_name : "NULL",
138 ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
139 return False;
142 /**********************************************
143 Is this the closest DC ?
144 **********************************************/
146 BOOL ads_closest_dc(ADS_STRUCT *ads)
148 if (ads->config.flags & ADS_CLOSEST) {
149 DEBUG(10,("ads_closest_dc: ADS_CLOSEST flag set\n"));
150 return True;
153 /* not sure if this can ever happen */
154 if (ads_sitename_match(ads)) {
155 DEBUG(10,("ads_closest_dc: ADS_CLOSEST flag not set but sites match\n"));
156 return True;
159 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
160 ads->config.ldap_server_name));
162 return False;
167 try a connection to a given ldap server, returning True and setting the servers IP
168 in the ads struct if successful
170 BOOL ads_try_connect(ADS_STRUCT *ads, const char *server )
172 char *srv;
173 struct cldap_netlogon_reply cldap_reply;
175 if (!server || !*server) {
176 return False;
179 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
180 server, ads->server.realm));
182 /* this copes with inet_ntoa brokenness */
184 srv = SMB_STRDUP(server);
186 ZERO_STRUCT( cldap_reply );
188 if ( !ads_cldap_netlogon( srv, ads->server.realm, &cldap_reply ) ) {
189 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", srv));
190 SAFE_FREE( srv );
191 return False;
194 /* Check the CLDAP reply flags */
196 if ( !(cldap_reply.flags & ADS_LDAP) ) {
197 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
198 srv));
199 SAFE_FREE( srv );
200 return False;
203 /* Fill in the ads->config values */
205 SAFE_FREE(ads->config.realm);
206 SAFE_FREE(ads->config.bind_path);
207 SAFE_FREE(ads->config.ldap_server_name);
208 SAFE_FREE(ads->config.server_site_name);
209 SAFE_FREE(ads->config.client_site_name);
210 SAFE_FREE(ads->server.workgroup);
212 ads->config.flags = cldap_reply.flags;
213 ads->config.ldap_server_name = SMB_STRDUP(cldap_reply.hostname);
214 strupper_m(cldap_reply.domain);
215 ads->config.realm = SMB_STRDUP(cldap_reply.domain);
216 ads->config.bind_path = ads_build_dn(ads->config.realm);
217 if (*cldap_reply.server_site_name) {
218 ads->config.server_site_name =
219 SMB_STRDUP(cldap_reply.server_site_name);
221 if (*cldap_reply.client_site_name) {
222 ads->config.client_site_name =
223 SMB_STRDUP(cldap_reply.client_site_name);
226 ads->server.workgroup = SMB_STRDUP(cldap_reply.netbios_domain);
228 ads->ldap_port = LDAP_PORT;
229 ads->ldap_ip = *interpret_addr2(srv);
230 SAFE_FREE(srv);
232 /* Store our site name. */
233 sitename_store( cldap_reply.client_site_name );
235 return True;
238 /**********************************************************************
239 Try to find an AD dc using our internal name resolution routines
240 Try the realm first and then then workgroup name if netbios is not
241 disabled
242 **********************************************************************/
244 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
246 const char *c_realm;
247 int count, i=0;
248 struct ip_service *ip_list;
249 pstring realm;
250 BOOL got_realm = False;
251 BOOL use_own_domain = False;
252 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
254 /* if the realm and workgroup are both empty, assume they are ours */
256 /* realm */
257 c_realm = ads->server.realm;
259 if ( !c_realm || !*c_realm ) {
260 /* special case where no realm and no workgroup means our own */
261 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
262 use_own_domain = True;
263 c_realm = lp_realm();
267 if (c_realm && *c_realm)
268 got_realm = True;
270 again:
271 /* we need to try once with the realm name and fallback to the
272 netbios domain name if we fail (if netbios has not been disabled */
274 if ( !got_realm && !lp_disable_netbios() ) {
275 c_realm = ads->server.workgroup;
276 if (!c_realm || !*c_realm) {
277 if ( use_own_domain )
278 c_realm = lp_workgroup();
281 if ( !c_realm || !*c_realm ) {
282 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
283 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
287 pstrcpy( realm, c_realm );
289 DEBUG(6,("ads_find_dc: looking for %s '%s'\n",
290 (got_realm ? "realm" : "domain"), realm));
292 status = get_sorted_dc_list(realm, &ip_list, &count, got_realm);
293 if (!NT_STATUS_IS_OK(status)) {
294 /* fall back to netbios if we can */
295 if ( got_realm && !lp_disable_netbios() ) {
296 got_realm = False;
297 goto again;
300 return status;
303 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
304 for ( i=0; i<count; i++ ) {
305 fstring server;
307 fstrcpy( server, inet_ntoa(ip_list[i].ip) );
309 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
310 continue;
312 if (!got_realm) {
313 /* realm in this case is a workgroup name. We need
314 to ignore any IP addresses in the negative connection
315 cache that match ip addresses returned in the ad realm
316 case. It sucks that I have to reproduce the logic above... */
317 c_realm = ads->server.realm;
318 if ( !c_realm || !*c_realm ) {
319 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
320 c_realm = lp_realm();
323 if (c_realm && *c_realm &&
324 !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm, server))) {
325 /* Ensure we add the workgroup name for this
326 IP address as negative too. */
327 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
328 continue;
332 if ( ads_try_connect(ads, server) ) {
333 SAFE_FREE(ip_list);
334 return NT_STATUS_OK;
337 /* keep track of failures */
338 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
341 SAFE_FREE(ip_list);
343 return NT_STATUS_NO_LOGON_SERVERS;
348 * Connect to the LDAP server
349 * @param ads Pointer to an existing ADS_STRUCT
350 * @return status of connection
352 ADS_STATUS ads_connect(ADS_STRUCT *ads)
354 int version = LDAP_VERSION3;
355 ADS_STATUS status;
356 NTSTATUS ntstatus;
358 ads->last_attempt = time(NULL);
359 ads->ld = NULL;
361 /* try with a user specified server */
363 if (ads->server.ldap_server &&
364 ads_try_connect(ads, ads->server.ldap_server)) {
365 goto got_connection;
368 ntstatus = ads_find_dc(ads);
369 if (NT_STATUS_IS_OK(ntstatus)) {
370 goto got_connection;
373 return ADS_ERROR_NT(ntstatus);
375 got_connection:
376 DEBUG(3,("Connected to LDAP server %s\n", inet_ntoa(ads->ldap_ip)));
378 if (!ads->auth.user_name) {
379 /* Must use the userPrincipalName value here or sAMAccountName
380 and not servicePrincipalName; found by Guenther Deschner */
382 asprintf(&ads->auth.user_name, "%s$", global_myname() );
385 if (!ads->auth.realm) {
386 ads->auth.realm = SMB_STRDUP(ads->config.realm);
389 if (!ads->auth.kdc_server) {
390 ads->auth.kdc_server = SMB_STRDUP(inet_ntoa(ads->ldap_ip));
393 #if KRB5_DNS_HACK
394 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
395 to MIT kerberos to work (tridge) */
397 char *env;
398 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
399 setenv(env, ads->auth.kdc_server, 1);
400 free(env);
402 #endif
404 /* If the caller() requested no LDAP bind, then we are done */
406 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
407 return ADS_SUCCESS;
410 /* Otherwise setup the TCP LDAP session */
412 if ( (ads->ld = ldap_open_with_timeout(ads->config.ldap_server_name,
413 LDAP_PORT, lp_ldap_timeout())) == NULL )
415 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
418 /* cache the successful connection for workgroup and realm */
419 if (ads_closest_dc(ads)) {
420 saf_store( ads->server.workgroup, inet_ntoa(ads->ldap_ip));
421 saf_store( ads->server.realm, inet_ntoa(ads->ldap_ip));
424 ldap_set_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
426 status = ADS_ERROR(smb_ldap_start_tls(ads->ld, version));
427 if (!ADS_ERR_OK(status)) {
428 return status;
431 /* fill in the current time and offsets */
433 status = ads_current_time( ads );
434 if ( !ADS_ERR_OK(status) ) {
435 return status;
438 /* Now do the bind */
440 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
441 return ADS_ERROR(ldap_simple_bind_s( ads->ld, NULL, NULL));
444 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
445 return ADS_ERROR(ldap_simple_bind_s( ads->ld, ads->auth.user_name, ads->auth.password));
448 return ads_sasl_bind(ads);
452 Duplicate a struct berval into talloc'ed memory
454 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
456 struct berval *value;
458 if (!in_val) return NULL;
460 value = TALLOC_ZERO_P(ctx, struct berval);
461 if (value == NULL)
462 return NULL;
463 if (in_val->bv_len == 0) return value;
465 value->bv_len = in_val->bv_len;
466 value->bv_val = (char *)TALLOC_MEMDUP(ctx, in_val->bv_val,
467 in_val->bv_len);
468 return value;
472 Make a values list out of an array of (struct berval *)
474 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
475 const struct berval **in_vals)
477 struct berval **values;
478 int i;
480 if (!in_vals) return NULL;
481 for (i=0; in_vals[i]; i++)
482 ; /* count values */
483 values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
484 if (!values) return NULL;
486 for (i=0; in_vals[i]; i++) {
487 values[i] = dup_berval(ctx, in_vals[i]);
489 return values;
493 UTF8-encode a values list out of an array of (char *)
495 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
497 char **values;
498 int i;
500 if (!in_vals) return NULL;
501 for (i=0; in_vals[i]; i++)
502 ; /* count values */
503 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
504 if (!values) return NULL;
506 for (i=0; in_vals[i]; i++) {
507 push_utf8_talloc(ctx, &values[i], in_vals[i]);
509 return values;
513 Pull a (char *) array out of a UTF8-encoded values list
515 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
517 char **values;
518 int i;
520 if (!in_vals) return NULL;
521 for (i=0; in_vals[i]; i++)
522 ; /* count values */
523 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
524 if (!values) return NULL;
526 for (i=0; in_vals[i]; i++) {
527 pull_utf8_talloc(ctx, &values[i], in_vals[i]);
529 return values;
533 * Do a search with paged results. cookie must be null on the first
534 * call, and then returned on each subsequent call. It will be null
535 * again when the entire search is complete
536 * @param ads connection to ads server
537 * @param bind_path Base dn for the search
538 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
539 * @param expr Search expression - specified in local charset
540 * @param attrs Attributes to retrieve - specified in utf8 or ascii
541 * @param res ** which will contain results - free res* with ads_msgfree()
542 * @param count Number of entries retrieved on this page
543 * @param cookie The paged results cookie to be returned on subsequent calls
544 * @return status of search
546 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
547 const char *bind_path,
548 int scope, const char *expr,
549 const char **attrs, void *args,
550 LDAPMessage **res,
551 int *count, struct berval **cookie)
553 int rc, i, version;
554 char *utf8_expr, *utf8_path, **search_attrs;
555 LDAPControl PagedResults, NoReferrals, ExtendedDn, *controls[4], **rcontrols;
556 BerElement *cookie_be = NULL;
557 struct berval *cookie_bv= NULL;
558 BerElement *extdn_be = NULL;
559 struct berval *extdn_bv= NULL;
561 TALLOC_CTX *ctx;
562 ads_control *external_control = (ads_control *) args;
564 *res = NULL;
566 if (!(ctx = talloc_init("ads_do_paged_search_args")))
567 return ADS_ERROR(LDAP_NO_MEMORY);
569 /* 0 means the conversion worked but the result was empty
570 so we only fail if it's -1. In any case, it always
571 at least nulls out the dest */
572 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
573 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
574 rc = LDAP_NO_MEMORY;
575 goto done;
578 if (!attrs || !(*attrs))
579 search_attrs = NULL;
580 else {
581 /* This would be the utf8-encoded version...*/
582 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
583 if (!(str_list_copy(&search_attrs, attrs))) {
584 rc = LDAP_NO_MEMORY;
585 goto done;
590 /* Paged results only available on ldap v3 or later */
591 ldap_get_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
592 if (version < LDAP_VERSION3) {
593 rc = LDAP_NOT_SUPPORTED;
594 goto done;
597 cookie_be = ber_alloc_t(LBER_USE_DER);
598 if (*cookie) {
599 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
600 ber_bvfree(*cookie); /* don't need it from last time */
601 *cookie = NULL;
602 } else {
603 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
605 ber_flatten(cookie_be, &cookie_bv);
606 PagedResults.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID);
607 PagedResults.ldctl_iscritical = (char) 1;
608 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
609 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
611 NoReferrals.ldctl_oid = CONST_DISCARD(char *, ADS_NO_REFERRALS_OID);
612 NoReferrals.ldctl_iscritical = (char) 0;
613 NoReferrals.ldctl_value.bv_len = 0;
614 NoReferrals.ldctl_value.bv_val = CONST_DISCARD(char *, "");
616 if (external_control && strequal(external_control->control, ADS_EXTENDED_DN_OID)) {
618 ExtendedDn.ldctl_oid = CONST_DISCARD(char *, external_control->control);
619 ExtendedDn.ldctl_iscritical = (char) external_control->critical;
621 /* win2k does not accept a ldctl_value beeing passed in */
623 if (external_control->val != 0) {
625 if ((extdn_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
626 rc = LDAP_NO_MEMORY;
627 goto done;
630 if ((ber_printf(extdn_be, "{i}", (ber_int_t) external_control->val)) == -1) {
631 rc = LDAP_NO_MEMORY;
632 goto done;
634 if ((ber_flatten(extdn_be, &extdn_bv)) == -1) {
635 rc = LDAP_NO_MEMORY;
636 goto done;
639 ExtendedDn.ldctl_value.bv_len = extdn_bv->bv_len;
640 ExtendedDn.ldctl_value.bv_val = extdn_bv->bv_val;
642 } else {
643 ExtendedDn.ldctl_value.bv_len = 0;
644 ExtendedDn.ldctl_value.bv_val = NULL;
647 controls[0] = &NoReferrals;
648 controls[1] = &PagedResults;
649 controls[2] = &ExtendedDn;
650 controls[3] = NULL;
652 } else {
653 controls[0] = &NoReferrals;
654 controls[1] = &PagedResults;
655 controls[2] = NULL;
658 /* we need to disable referrals as the openldap libs don't
659 handle them and paged results at the same time. Using them
660 together results in the result record containing the server
661 page control being removed from the result list (tridge/jmcd)
663 leaving this in despite the control that says don't generate
664 referrals, in case the server doesn't support it (jmcd)
666 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
668 rc = ldap_search_with_timeout(ads->ld, utf8_path, scope, utf8_expr,
669 search_attrs, 0, controls,
670 NULL, LDAP_NO_LIMIT,
671 (LDAPMessage **)res);
673 ber_free(cookie_be, 1);
674 ber_bvfree(cookie_bv);
676 if (rc) {
677 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
678 ldap_err2string(rc)));
679 goto done;
682 rc = ldap_parse_result(ads->ld, *res, NULL, NULL, NULL,
683 NULL, &rcontrols, 0);
685 if (!rcontrols) {
686 goto done;
689 for (i=0; rcontrols[i]; i++) {
690 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
691 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
692 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
693 &cookie_bv);
694 /* the berval is the cookie, but must be freed when
695 it is all done */
696 if (cookie_bv->bv_len) /* still more to do */
697 *cookie=ber_bvdup(cookie_bv);
698 else
699 *cookie=NULL;
700 ber_bvfree(cookie_bv);
701 ber_free(cookie_be, 1);
702 break;
705 ldap_controls_free(rcontrols);
707 done:
708 talloc_destroy(ctx);
710 if (extdn_be) {
711 ber_free(extdn_be, 1);
714 if (extdn_bv) {
715 ber_bvfree(extdn_bv);
718 /* if/when we decide to utf8-encode attrs, take out this next line */
719 str_list_free(&search_attrs);
721 return ADS_ERROR(rc);
724 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
725 int scope, const char *expr,
726 const char **attrs, LDAPMessage **res,
727 int *count, struct berval **cookie)
729 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
734 * Get all results for a search. This uses ads_do_paged_search() to return
735 * all entries in a large search.
736 * @param ads connection to ads server
737 * @param bind_path Base dn for the search
738 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
739 * @param expr Search expression
740 * @param attrs Attributes to retrieve
741 * @param res ** which will contain results - free res* with ads_msgfree()
742 * @return status of search
744 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
745 int scope, const char *expr,
746 const char **attrs, void *args,
747 LDAPMessage **res)
749 struct berval *cookie = NULL;
750 int count = 0;
751 ADS_STATUS status;
753 *res = NULL;
754 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
755 &count, &cookie);
757 if (!ADS_ERR_OK(status))
758 return status;
760 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
761 while (cookie) {
762 LDAPMessage *res2 = NULL;
763 ADS_STATUS status2;
764 LDAPMessage *msg, *next;
766 status2 = ads_do_paged_search_args(ads, bind_path, scope, expr,
767 attrs, args, &res2, &count, &cookie);
769 if (!ADS_ERR_OK(status2)) break;
771 /* this relies on the way that ldap_add_result_entry() works internally. I hope
772 that this works on all ldap libs, but I have only tested with openldap */
773 for (msg = ads_first_entry(ads, res2); msg; msg = next) {
774 next = ads_next_entry(ads, msg);
775 ldap_add_result_entry((LDAPMessage **)res, msg);
777 /* note that we do not free res2, as the memory is now
778 part of the main returned list */
780 #else
781 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
782 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
783 #endif
785 return status;
788 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
789 int scope, const char *expr,
790 const char **attrs, LDAPMessage **res)
792 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
796 * Run a function on all results for a search. Uses ads_do_paged_search() and
797 * runs the function as each page is returned, using ads_process_results()
798 * @param ads connection to ads server
799 * @param bind_path Base dn for the search
800 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
801 * @param expr Search expression - specified in local charset
802 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
803 * @param fn Function which takes attr name, values list, and data_area
804 * @param data_area Pointer which is passed to function on each call
805 * @return status of search
807 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
808 int scope, const char *expr, const char **attrs,
809 BOOL(*fn)(char *, void **, void *),
810 void *data_area)
812 struct berval *cookie = NULL;
813 int count = 0;
814 ADS_STATUS status;
815 LDAPMessage *res;
817 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
818 &count, &cookie);
820 if (!ADS_ERR_OK(status)) return status;
822 ads_process_results(ads, res, fn, data_area);
823 ads_msgfree(ads, res);
825 while (cookie) {
826 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
827 &res, &count, &cookie);
829 if (!ADS_ERR_OK(status)) break;
831 ads_process_results(ads, res, fn, data_area);
832 ads_msgfree(ads, res);
835 return status;
839 * Do a search with a timeout.
840 * @param ads connection to ads server
841 * @param bind_path Base dn for the search
842 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
843 * @param expr Search expression
844 * @param attrs Attributes to retrieve
845 * @param res ** which will contain results - free res* with ads_msgfree()
846 * @return status of search
848 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
849 const char *expr,
850 const char **attrs, LDAPMessage **res)
852 int rc;
853 char *utf8_expr, *utf8_path, **search_attrs = NULL;
854 TALLOC_CTX *ctx;
856 *res = NULL;
857 if (!(ctx = talloc_init("ads_do_search"))) {
858 DEBUG(1,("ads_do_search: talloc_init() failed!"));
859 return ADS_ERROR(LDAP_NO_MEMORY);
862 /* 0 means the conversion worked but the result was empty
863 so we only fail if it's negative. In any case, it always
864 at least nulls out the dest */
865 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
866 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
867 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
868 rc = LDAP_NO_MEMORY;
869 goto done;
872 if (!attrs || !(*attrs))
873 search_attrs = NULL;
874 else {
875 /* This would be the utf8-encoded version...*/
876 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
877 if (!(str_list_copy(&search_attrs, attrs)))
879 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
880 rc = LDAP_NO_MEMORY;
881 goto done;
885 /* see the note in ads_do_paged_search - we *must* disable referrals */
886 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
888 rc = ldap_search_with_timeout(ads->ld, utf8_path, scope, utf8_expr,
889 search_attrs, 0, NULL, NULL,
890 LDAP_NO_LIMIT,
891 (LDAPMessage **)res);
893 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
894 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
895 rc = 0;
898 done:
899 talloc_destroy(ctx);
900 /* if/when we decide to utf8-encode attrs, take out this next line */
901 str_list_free(&search_attrs);
902 return ADS_ERROR(rc);
905 * Do a general ADS search
906 * @param ads connection to ads server
907 * @param res ** which will contain results - free res* with ads_msgfree()
908 * @param expr Search expression
909 * @param attrs Attributes to retrieve
910 * @return status of search
912 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
913 const char *expr, const char **attrs)
915 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
916 expr, attrs, res);
920 * Do a search on a specific DistinguishedName
921 * @param ads connection to ads server
922 * @param res ** which will contain results - free res* with ads_msgfree()
923 * @param dn DistinguishName to search
924 * @param attrs Attributes to retrieve
925 * @return status of search
927 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
928 const char *dn, const char **attrs)
930 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
931 attrs, res);
935 * Free up memory from a ads_search
936 * @param ads connection to ads server
937 * @param msg Search results to free
939 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
941 if (!msg) return;
942 ldap_msgfree(msg);
946 * Free up memory from various ads requests
947 * @param ads connection to ads server
948 * @param mem Area to free
950 void ads_memfree(ADS_STRUCT *ads, void *mem)
952 SAFE_FREE(mem);
956 * Get a dn from search results
957 * @param ads connection to ads server
958 * @param msg Search result
959 * @return dn string
961 char *ads_get_dn(ADS_STRUCT *ads, LDAPMessage *msg)
963 char *utf8_dn, *unix_dn;
965 utf8_dn = ldap_get_dn(ads->ld, msg);
967 if (!utf8_dn) {
968 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
969 return NULL;
972 if (pull_utf8_allocate(&unix_dn, utf8_dn) == (size_t)-1) {
973 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
974 utf8_dn ));
975 return NULL;
977 ldap_memfree(utf8_dn);
978 return unix_dn;
982 * Get the parent from a dn
983 * @param dn the dn to return the parent from
984 * @return parent dn string
986 char *ads_parent_dn(const char *dn)
988 char *p;
990 if (dn == NULL) {
991 return NULL;
994 p = strchr(dn, ',');
996 if (p == NULL) {
997 return NULL;
1000 return p+1;
1004 * Find a machine account given a hostname
1005 * @param ads connection to ads server
1006 * @param res ** which will contain results - free res* with ads_msgfree()
1007 * @param host Hostname to search for
1008 * @return status of search
1010 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1011 const char *machine)
1013 ADS_STATUS status;
1014 char *expr;
1015 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1017 *res = NULL;
1019 /* the easiest way to find a machine account anywhere in the tree
1020 is to look for hostname$ */
1021 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1022 DEBUG(1, ("asprintf failed!\n"));
1023 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1026 status = ads_search(ads, res, expr, attrs);
1027 SAFE_FREE(expr);
1028 return status;
1032 * Initialize a list of mods to be used in a modify request
1033 * @param ctx An initialized TALLOC_CTX
1034 * @return allocated ADS_MODLIST
1036 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1038 #define ADS_MODLIST_ALLOC_SIZE 10
1039 LDAPMod **mods;
1041 if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1042 /* -1 is safety to make sure we don't go over the end.
1043 need to reset it to NULL before doing ldap modify */
1044 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1046 return (ADS_MODLIST)mods;
1051 add an attribute to the list, with values list already constructed
1053 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1054 int mod_op, const char *name,
1055 const void *_invals)
1057 const void **invals = (const void **)_invals;
1058 int curmod;
1059 LDAPMod **modlist = (LDAPMod **) *mods;
1060 struct berval **ber_values = NULL;
1061 char **char_values = NULL;
1063 if (!invals) {
1064 mod_op = LDAP_MOD_DELETE;
1065 } else {
1066 if (mod_op & LDAP_MOD_BVALUES)
1067 ber_values = ads_dup_values(ctx,
1068 (const struct berval **)invals);
1069 else
1070 char_values = ads_push_strvals(ctx,
1071 (const char **) invals);
1074 /* find the first empty slot */
1075 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1076 curmod++);
1077 if (modlist[curmod] == (LDAPMod *) -1) {
1078 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
1079 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1080 return ADS_ERROR(LDAP_NO_MEMORY);
1081 memset(&modlist[curmod], 0,
1082 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1083 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1084 *mods = (ADS_MODLIST)modlist;
1087 if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
1088 return ADS_ERROR(LDAP_NO_MEMORY);
1089 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1090 if (mod_op & LDAP_MOD_BVALUES) {
1091 modlist[curmod]->mod_bvalues = ber_values;
1092 } else if (mod_op & LDAP_MOD_DELETE) {
1093 modlist[curmod]->mod_values = NULL;
1094 } else {
1095 modlist[curmod]->mod_values = char_values;
1098 modlist[curmod]->mod_op = mod_op;
1099 return ADS_ERROR(LDAP_SUCCESS);
1103 * Add a single string value to a mod list
1104 * @param ctx An initialized TALLOC_CTX
1105 * @param mods An initialized ADS_MODLIST
1106 * @param name The attribute name to add
1107 * @param val The value to add - NULL means DELETE
1108 * @return ADS STATUS indicating success of add
1110 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1111 const char *name, const char *val)
1113 const char *values[2];
1115 values[0] = val;
1116 values[1] = NULL;
1118 if (!val)
1119 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1120 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1124 * Add an array of string values to a mod list
1125 * @param ctx An initialized TALLOC_CTX
1126 * @param mods An initialized ADS_MODLIST
1127 * @param name The attribute name to add
1128 * @param vals The array of string values to add - NULL means DELETE
1129 * @return ADS STATUS indicating success of add
1131 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1132 const char *name, const char **vals)
1134 if (!vals)
1135 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1136 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1137 name, (const void **) vals);
1140 #if 0
1142 * Add a single ber-encoded value to a mod list
1143 * @param ctx An initialized TALLOC_CTX
1144 * @param mods An initialized ADS_MODLIST
1145 * @param name The attribute name to add
1146 * @param val The value to add - NULL means DELETE
1147 * @return ADS STATUS indicating success of add
1149 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1150 const char *name, const struct berval *val)
1152 const struct berval *values[2];
1154 values[0] = val;
1155 values[1] = NULL;
1156 if (!val)
1157 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1158 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1159 name, (const void **) values);
1161 #endif
1164 * Perform an ldap modify
1165 * @param ads connection to ads server
1166 * @param mod_dn DistinguishedName to modify
1167 * @param mods list of modifications to perform
1168 * @return status of modify
1170 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1172 int ret,i;
1173 char *utf8_dn = NULL;
1175 this control is needed to modify that contains a currently
1176 non-existent attribute (but allowable for the object) to run
1178 LDAPControl PermitModify = {
1179 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
1180 {0, NULL},
1181 (char) 1};
1182 LDAPControl *controls[2];
1184 controls[0] = &PermitModify;
1185 controls[1] = NULL;
1187 if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) {
1188 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1191 /* find the end of the list, marked by NULL or -1 */
1192 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1193 /* make sure the end of the list is NULL */
1194 mods[i] = NULL;
1195 ret = ldap_modify_ext_s(ads->ld, utf8_dn,
1196 (LDAPMod **) mods, controls, NULL);
1197 SAFE_FREE(utf8_dn);
1198 return ADS_ERROR(ret);
1202 * Perform an ldap add
1203 * @param ads connection to ads server
1204 * @param new_dn DistinguishedName to add
1205 * @param mods list of attributes and values for DN
1206 * @return status of add
1208 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1210 int ret, i;
1211 char *utf8_dn = NULL;
1213 if (push_utf8_allocate(&utf8_dn, new_dn) == -1) {
1214 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1215 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1218 /* find the end of the list, marked by NULL or -1 */
1219 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1220 /* make sure the end of the list is NULL */
1221 mods[i] = NULL;
1223 ret = ldap_add_s(ads->ld, utf8_dn, (LDAPMod**)mods);
1224 SAFE_FREE(utf8_dn);
1225 return ADS_ERROR(ret);
1229 * Delete a DistinguishedName
1230 * @param ads connection to ads server
1231 * @param new_dn DistinguishedName to delete
1232 * @return status of delete
1234 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1236 int ret;
1237 char *utf8_dn = NULL;
1238 if (push_utf8_allocate(&utf8_dn, del_dn) == -1) {
1239 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1240 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1243 ret = ldap_delete_s(ads->ld, utf8_dn);
1244 SAFE_FREE(utf8_dn);
1245 return ADS_ERROR(ret);
1249 * Build an org unit string
1250 * if org unit is Computers or blank then assume a container, otherwise
1251 * assume a / separated list of organisational units.
1252 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1253 * @param ads connection to ads server
1254 * @param org_unit Organizational unit
1255 * @return org unit string - caller must free
1257 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1259 char *ret = NULL;
1261 if (!org_unit || !*org_unit) {
1263 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1265 /* samba4 might not yet respond to a wellknownobject-query */
1266 return ret ? ret : SMB_STRDUP("cn=Computers");
1269 if (strequal(org_unit, "Computers")) {
1270 return SMB_STRDUP("cn=Computers");
1273 /* jmcd: removed "\\" from the separation chars, because it is
1274 needed as an escape for chars like '#' which are valid in an
1275 OU name */
1276 return ads_build_path(org_unit, "/", "ou=", 1);
1280 * Get a org unit string for a well-known GUID
1281 * @param ads connection to ads server
1282 * @param wknguid Well known GUID
1283 * @return org unit string - caller must free
1285 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1287 ADS_STATUS status;
1288 LDAPMessage *res = NULL;
1289 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1290 **bind_dn_exp = NULL;
1291 const char *attrs[] = {"distinguishedName", NULL};
1292 int new_ln, wkn_ln, bind_ln, i;
1294 if (wknguid == NULL) {
1295 return NULL;
1298 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1299 DEBUG(1, ("asprintf failed!\n"));
1300 return NULL;
1303 status = ads_search_dn(ads, &res, base, attrs);
1304 if (!ADS_ERR_OK(status)) {
1305 DEBUG(1,("Failed while searching for: %s\n", base));
1306 goto out;
1309 if (ads_count_replies(ads, res) != 1) {
1310 goto out;
1313 /* substitute the bind-path from the well-known-guid-search result */
1314 wkn_dn = ads_get_dn(ads, res);
1315 if (!wkn_dn) {
1316 goto out;
1319 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1320 if (!wkn_dn_exp) {
1321 goto out;
1324 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1325 if (!bind_dn_exp) {
1326 goto out;
1329 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1331 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1334 new_ln = wkn_ln - bind_ln;
1336 ret = SMB_STRDUP(wkn_dn_exp[0]);
1337 if (!ret) {
1338 goto out;
1341 for (i=1; i < new_ln; i++) {
1342 char *s = NULL;
1344 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1345 SAFE_FREE(ret);
1346 goto out;
1349 SAFE_FREE(ret);
1350 ret = SMB_STRDUP(s);
1351 free(s);
1352 if (!ret) {
1353 goto out;
1357 out:
1358 SAFE_FREE(base);
1359 ads_msgfree(ads, res);
1360 ads_memfree(ads, wkn_dn);
1361 if (wkn_dn_exp) {
1362 ldap_value_free(wkn_dn_exp);
1364 if (bind_dn_exp) {
1365 ldap_value_free(bind_dn_exp);
1368 return ret;
1372 * Adds (appends) an item to an attribute array, rather then
1373 * replacing the whole list
1374 * @param ctx An initialized TALLOC_CTX
1375 * @param mods An initialized ADS_MODLIST
1376 * @param name name of the ldap attribute to append to
1377 * @param vals an array of values to add
1378 * @return status of addition
1381 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1382 const char *name, const char **vals)
1384 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1385 (const void *) vals);
1389 * Determines the computer account's current KVNO via an LDAP lookup
1390 * @param ads An initialized ADS_STRUCT
1391 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1392 * @return the kvno for the computer account, or -1 in case of a failure.
1395 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *machine_name)
1397 LDAPMessage *res = NULL;
1398 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1399 char *filter;
1400 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1401 char *dn_string = NULL;
1402 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1404 DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name));
1405 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1406 return kvno;
1408 ret = ads_search(ads, &res, filter, attrs);
1409 SAFE_FREE(filter);
1410 if (!ADS_ERR_OK(ret) && ads_count_replies(ads, res)) {
1411 DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name));
1412 ads_msgfree(ads, res);
1413 return kvno;
1416 dn_string = ads_get_dn(ads, res);
1417 if (!dn_string) {
1418 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1419 ads_msgfree(ads, res);
1420 return kvno;
1422 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1423 ads_memfree(ads, dn_string);
1425 /* ---------------------------------------------------------
1426 * 0 is returned as a default KVNO from this point on...
1427 * This is done because Windows 2000 does not support key
1428 * version numbers. Chances are that a failure in the next
1429 * step is simply due to Windows 2000 being used for a
1430 * domain controller. */
1431 kvno = 0;
1433 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1434 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1435 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1436 ads_msgfree(ads, res);
1437 return kvno;
1440 /* Success */
1441 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1442 ads_msgfree(ads, res);
1443 return kvno;
1447 * This clears out all registered spn's for a given hostname
1448 * @param ads An initilaized ADS_STRUCT
1449 * @param machine_name the NetBIOS name of the computer.
1450 * @return 0 upon success, non-zero otherwise.
1453 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1455 TALLOC_CTX *ctx;
1456 LDAPMessage *res = NULL;
1457 ADS_MODLIST mods;
1458 const char *servicePrincipalName[1] = {NULL};
1459 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1460 char *dn_string = NULL;
1462 ret = ads_find_machine_acct(ads, &res, machine_name);
1463 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1464 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1465 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1466 ads_msgfree(ads, res);
1467 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1470 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1471 ctx = talloc_init("ads_clear_service_principal_names");
1472 if (!ctx) {
1473 ads_msgfree(ads, res);
1474 return ADS_ERROR(LDAP_NO_MEMORY);
1477 if (!(mods = ads_init_mods(ctx))) {
1478 talloc_destroy(ctx);
1479 ads_msgfree(ads, res);
1480 return ADS_ERROR(LDAP_NO_MEMORY);
1482 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1483 if (!ADS_ERR_OK(ret)) {
1484 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1485 ads_msgfree(ads, res);
1486 talloc_destroy(ctx);
1487 return ret;
1489 dn_string = ads_get_dn(ads, res);
1490 if (!dn_string) {
1491 talloc_destroy(ctx);
1492 ads_msgfree(ads, res);
1493 return ADS_ERROR(LDAP_NO_MEMORY);
1495 ret = ads_gen_mod(ads, dn_string, mods);
1496 ads_memfree(ads,dn_string);
1497 if (!ADS_ERR_OK(ret)) {
1498 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1499 machine_name));
1500 ads_msgfree(ads, res);
1501 talloc_destroy(ctx);
1502 return ret;
1505 ads_msgfree(ads, res);
1506 talloc_destroy(ctx);
1507 return ret;
1511 * This adds a service principal name to an existing computer account
1512 * (found by hostname) in AD.
1513 * @param ads An initialized ADS_STRUCT
1514 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1515 * @param my_fqdn The fully qualified DNS name of the machine
1516 * @param spn A string of the service principal to add, i.e. 'host'
1517 * @return 0 upon sucess, or non-zero if a failure occurs
1520 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1521 const char *my_fqdn, const char *spn)
1523 ADS_STATUS ret;
1524 TALLOC_CTX *ctx;
1525 LDAPMessage *res = NULL;
1526 char *psp1, *psp2;
1527 ADS_MODLIST mods;
1528 char *dn_string = NULL;
1529 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1531 ret = ads_find_machine_acct(ads, &res, machine_name);
1532 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1533 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1534 machine_name));
1535 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1536 spn, machine_name, ads->config.realm));
1537 ads_msgfree(ads, res);
1538 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1541 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1542 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1543 ads_msgfree(ads, res);
1544 return ADS_ERROR(LDAP_NO_MEMORY);
1547 /* add short name spn */
1549 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1550 talloc_destroy(ctx);
1551 ads_msgfree(ads, res);
1552 return ADS_ERROR(LDAP_NO_MEMORY);
1554 strupper_m(psp1);
1555 strlower_m(&psp1[strlen(spn)]);
1556 servicePrincipalName[0] = psp1;
1558 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1559 psp1, machine_name));
1562 /* add fully qualified spn */
1564 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1565 ret = ADS_ERROR(LDAP_NO_MEMORY);
1566 goto out;
1568 strupper_m(psp2);
1569 strlower_m(&psp2[strlen(spn)]);
1570 servicePrincipalName[1] = psp2;
1572 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1573 psp2, machine_name));
1575 if ( (mods = ads_init_mods(ctx)) == NULL ) {
1576 ret = ADS_ERROR(LDAP_NO_MEMORY);
1577 goto out;
1580 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1581 if (!ADS_ERR_OK(ret)) {
1582 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1583 goto out;
1586 if ( (dn_string = ads_get_dn(ads, res)) == NULL ) {
1587 ret = ADS_ERROR(LDAP_NO_MEMORY);
1588 goto out;
1591 ret = ads_gen_mod(ads, dn_string, mods);
1592 ads_memfree(ads,dn_string);
1593 if (!ADS_ERR_OK(ret)) {
1594 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1595 goto out;
1598 out:
1599 TALLOC_FREE( ctx );
1600 ads_msgfree(ads, res);
1601 return ret;
1605 * adds a machine account to the ADS server
1606 * @param ads An intialized ADS_STRUCT
1607 * @param machine_name - the NetBIOS machine name of this account.
1608 * @param account_type A number indicating the type of account to create
1609 * @param org_unit The LDAP path in which to place this account
1610 * @return 0 upon success, or non-zero otherwise
1613 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1614 const char *org_unit)
1616 ADS_STATUS ret;
1617 char *samAccountName, *controlstr;
1618 TALLOC_CTX *ctx;
1619 ADS_MODLIST mods;
1620 char *new_dn;
1621 const char *objectClass[] = {"top", "person", "organizationalPerson",
1622 "user", "computer", NULL};
1623 LDAPMessage *res = NULL;
1624 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
1625 UF_DONT_EXPIRE_PASSWD |\
1626 UF_ACCOUNTDISABLE );
1628 if (!(ctx = talloc_init("ads_add_machine_acct")))
1629 return ADS_ERROR(LDAP_NO_MEMORY);
1631 ret = ADS_ERROR(LDAP_NO_MEMORY);
1633 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_name, org_unit);
1634 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
1636 if ( !new_dn || !samAccountName ) {
1637 goto done;
1640 #ifndef ENCTYPE_ARCFOUR_HMAC
1641 acct_control |= UF_USE_DES_KEY_ONLY;
1642 #endif
1644 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
1645 goto done;
1648 if (!(mods = ads_init_mods(ctx))) {
1649 goto done;
1652 ads_mod_str(ctx, &mods, "cn", machine_name);
1653 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1654 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1655 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1657 ret = ads_gen_add(ads, new_dn, mods);
1659 done:
1660 ads_msgfree(ads, res);
1661 talloc_destroy(ctx);
1663 return ret;
1667 dump a binary result from ldap
1669 static void dump_binary(const char *field, struct berval **values)
1671 int i, j;
1672 for (i=0; values[i]; i++) {
1673 printf("%s: ", field);
1674 for (j=0; j<values[i]->bv_len; j++) {
1675 printf("%02X", (unsigned char)values[i]->bv_val[j]);
1677 printf("\n");
1681 static void dump_guid(const char *field, struct berval **values)
1683 int i;
1684 UUID_FLAT guid;
1685 for (i=0; values[i]; i++) {
1686 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1687 printf("%s: %s\n", field,
1688 smb_uuid_string_static(smb_uuid_unpack_static(guid)));
1693 dump a sid result from ldap
1695 static void dump_sid(const char *field, struct berval **values)
1697 int i;
1698 for (i=0; values[i]; i++) {
1699 DOM_SID sid;
1700 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1701 printf("%s: %s\n", field, sid_string_static(&sid));
1706 dump ntSecurityDescriptor
1708 static void dump_sd(const char *filed, struct berval **values)
1710 prs_struct ps;
1712 SEC_DESC *psd = 0;
1713 TALLOC_CTX *ctx = 0;
1715 if (!(ctx = talloc_init("sec_io_desc")))
1716 return;
1718 /* prepare data */
1719 prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
1720 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
1721 prs_set_offset(&ps,0);
1723 /* parse secdesc */
1724 if (!sec_io_desc("sd", &psd, &ps, 1)) {
1725 prs_mem_free(&ps);
1726 talloc_destroy(ctx);
1727 return;
1729 if (psd) ads_disp_sd(psd);
1731 prs_mem_free(&ps);
1732 talloc_destroy(ctx);
1736 dump a string result from ldap
1738 static void dump_string(const char *field, char **values)
1740 int i;
1741 for (i=0; values[i]; i++) {
1742 printf("%s: %s\n", field, values[i]);
1747 dump a field from LDAP on stdout
1748 used for debugging
1751 static BOOL ads_dump_field(char *field, void **values, void *data_area)
1753 const struct {
1754 const char *name;
1755 BOOL string;
1756 void (*handler)(const char *, struct berval **);
1757 } handlers[] = {
1758 {"objectGUID", False, dump_guid},
1759 {"netbootGUID", False, dump_guid},
1760 {"nTSecurityDescriptor", False, dump_sd},
1761 {"dnsRecord", False, dump_binary},
1762 {"objectSid", False, dump_sid},
1763 {"tokenGroups", False, dump_sid},
1764 {"tokenGroupsNoGCAcceptable", False, dump_sid},
1765 {"tokengroupsGlobalandUniversal", False, dump_sid},
1766 {"mS-DS-CreatorSID", False, dump_sid},
1767 {NULL, True, NULL}
1769 int i;
1771 if (!field) { /* must be end of an entry */
1772 printf("\n");
1773 return False;
1776 for (i=0; handlers[i].name; i++) {
1777 if (StrCaseCmp(handlers[i].name, field) == 0) {
1778 if (!values) /* first time, indicate string or not */
1779 return handlers[i].string;
1780 handlers[i].handler(field, (struct berval **) values);
1781 break;
1784 if (!handlers[i].name) {
1785 if (!values) /* first time, indicate string conversion */
1786 return True;
1787 dump_string(field, (char **)values);
1789 return False;
1793 * Dump a result from LDAP on stdout
1794 * used for debugging
1795 * @param ads connection to ads server
1796 * @param res Results to dump
1799 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
1801 ads_process_results(ads, res, ads_dump_field, NULL);
1805 * Walk through results, calling a function for each entry found.
1806 * The function receives a field name, a berval * array of values,
1807 * and a data area passed through from the start. The function is
1808 * called once with null for field and values at the end of each
1809 * entry.
1810 * @param ads connection to ads server
1811 * @param res Results to process
1812 * @param fn Function for processing each result
1813 * @param data_area user-defined area to pass to function
1815 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
1816 BOOL(*fn)(char *, void **, void *),
1817 void *data_area)
1819 LDAPMessage *msg;
1820 TALLOC_CTX *ctx;
1822 if (!(ctx = talloc_init("ads_process_results")))
1823 return;
1825 for (msg = ads_first_entry(ads, res); msg;
1826 msg = ads_next_entry(ads, msg)) {
1827 char *utf8_field;
1828 BerElement *b;
1830 for (utf8_field=ldap_first_attribute(ads->ld,
1831 (LDAPMessage *)msg,&b);
1832 utf8_field;
1833 utf8_field=ldap_next_attribute(ads->ld,
1834 (LDAPMessage *)msg,b)) {
1835 struct berval **ber_vals;
1836 char **str_vals, **utf8_vals;
1837 char *field;
1838 BOOL string;
1840 pull_utf8_talloc(ctx, &field, utf8_field);
1841 string = fn(field, NULL, data_area);
1843 if (string) {
1844 utf8_vals = ldap_get_values(ads->ld,
1845 (LDAPMessage *)msg, field);
1846 str_vals = ads_pull_strvals(ctx,
1847 (const char **) utf8_vals);
1848 fn(field, (void **) str_vals, data_area);
1849 ldap_value_free(utf8_vals);
1850 } else {
1851 ber_vals = ldap_get_values_len(ads->ld,
1852 (LDAPMessage *)msg, field);
1853 fn(field, (void **) ber_vals, data_area);
1855 ldap_value_free_len(ber_vals);
1857 ldap_memfree(utf8_field);
1859 ber_free(b, 0);
1860 talloc_free_children(ctx);
1861 fn(NULL, NULL, data_area); /* completed an entry */
1864 talloc_destroy(ctx);
1868 * count how many replies are in a LDAPMessage
1869 * @param ads connection to ads server
1870 * @param res Results to count
1871 * @return number of replies
1873 int ads_count_replies(ADS_STRUCT *ads, void *res)
1875 return ldap_count_entries(ads->ld, (LDAPMessage *)res);
1879 * pull the first entry from a ADS result
1880 * @param ads connection to ads server
1881 * @param res Results of search
1882 * @return first entry from result
1884 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
1886 return ldap_first_entry(ads->ld, res);
1890 * pull the next entry from a ADS result
1891 * @param ads connection to ads server
1892 * @param res Results of search
1893 * @return next entry from result
1895 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
1897 return ldap_next_entry(ads->ld, res);
1901 * pull a single string from a ADS result
1902 * @param ads connection to ads server
1903 * @param mem_ctx TALLOC_CTX to use for allocating result string
1904 * @param msg Results of search
1905 * @param field Attribute to retrieve
1906 * @return Result string in talloc context
1908 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
1909 const char *field)
1911 char **values;
1912 char *ret = NULL;
1913 char *ux_string;
1914 size_t rc;
1916 values = ldap_get_values(ads->ld, msg, field);
1917 if (!values)
1918 return NULL;
1920 if (values[0]) {
1921 rc = pull_utf8_talloc(mem_ctx, &ux_string,
1922 values[0]);
1923 if (rc != (size_t)-1)
1924 ret = ux_string;
1927 ldap_value_free(values);
1928 return ret;
1932 * pull an array of strings from a ADS result
1933 * @param ads connection to ads server
1934 * @param mem_ctx TALLOC_CTX to use for allocating result string
1935 * @param msg Results of search
1936 * @param field Attribute to retrieve
1937 * @return Result strings in talloc context
1939 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
1940 LDAPMessage *msg, const char *field,
1941 size_t *num_values)
1943 char **values;
1944 char **ret = NULL;
1945 int i;
1947 values = ldap_get_values(ads->ld, msg, field);
1948 if (!values)
1949 return NULL;
1951 *num_values = ldap_count_values(values);
1953 ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
1954 if (!ret) {
1955 ldap_value_free(values);
1956 return NULL;
1959 for (i=0;i<*num_values;i++) {
1960 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
1961 ldap_value_free(values);
1962 return NULL;
1965 ret[i] = NULL;
1967 ldap_value_free(values);
1968 return ret;
1972 * pull an array of strings from a ADS result
1973 * (handle large multivalue attributes with range retrieval)
1974 * @param ads connection to ads server
1975 * @param mem_ctx TALLOC_CTX to use for allocating result string
1976 * @param msg Results of search
1977 * @param field Attribute to retrieve
1978 * @param current_strings strings returned by a previous call to this function
1979 * @param next_attribute The next query should ask for this attribute
1980 * @param num_values How many values did we get this time?
1981 * @param more_values Are there more values to get?
1982 * @return Result strings in talloc context
1984 char **ads_pull_strings_range(ADS_STRUCT *ads,
1985 TALLOC_CTX *mem_ctx,
1986 LDAPMessage *msg, const char *field,
1987 char **current_strings,
1988 const char **next_attribute,
1989 size_t *num_strings,
1990 BOOL *more_strings)
1992 char *attr;
1993 char *expected_range_attrib, *range_attr;
1994 BerElement *ptr = NULL;
1995 char **strings;
1996 char **new_strings;
1997 size_t num_new_strings;
1998 unsigned long int range_start;
1999 unsigned long int range_end;
2001 /* we might have been given the whole lot anyway */
2002 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2003 *more_strings = False;
2004 return strings;
2007 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2009 /* look for Range result */
2010 for (attr = ldap_first_attribute(ads->ld, (LDAPMessage *)msg, &ptr);
2011 attr;
2012 attr = ldap_next_attribute(ads->ld, (LDAPMessage *)msg, ptr)) {
2013 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2014 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2015 range_attr = attr;
2016 break;
2018 ldap_memfree(attr);
2020 if (!attr) {
2021 ber_free(ptr, 0);
2022 /* nothing here - this field is just empty */
2023 *more_strings = False;
2024 return NULL;
2027 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2028 &range_start, &range_end) == 2) {
2029 *more_strings = True;
2030 } else {
2031 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2032 &range_start) == 1) {
2033 *more_strings = False;
2034 } else {
2035 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2036 range_attr));
2037 ldap_memfree(range_attr);
2038 *more_strings = False;
2039 return NULL;
2043 if ((*num_strings) != range_start) {
2044 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2045 " - aborting range retreival\n",
2046 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2047 ldap_memfree(range_attr);
2048 *more_strings = False;
2049 return NULL;
2052 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2054 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2055 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2056 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2057 range_attr, (unsigned long int)range_end - range_start + 1,
2058 (unsigned long int)num_new_strings));
2059 ldap_memfree(range_attr);
2060 *more_strings = False;
2061 return NULL;
2064 strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2065 *num_strings + num_new_strings);
2067 if (strings == NULL) {
2068 ldap_memfree(range_attr);
2069 *more_strings = False;
2070 return NULL;
2073 if (new_strings && num_new_strings) {
2074 memcpy(&strings[*num_strings], new_strings,
2075 sizeof(*new_strings) * num_new_strings);
2078 (*num_strings) += num_new_strings;
2080 if (*more_strings) {
2081 *next_attribute = talloc_asprintf(mem_ctx,
2082 "%s;range=%d-*",
2083 field,
2084 (int)*num_strings);
2086 if (!*next_attribute) {
2087 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2088 ldap_memfree(range_attr);
2089 *more_strings = False;
2090 return NULL;
2094 ldap_memfree(range_attr);
2096 return strings;
2100 * pull a single uint32 from a ADS result
2101 * @param ads connection to ads server
2102 * @param msg Results of search
2103 * @param field Attribute to retrieve
2104 * @param v Pointer to int to store result
2105 * @return boolean inidicating success
2107 BOOL ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2108 uint32 *v)
2110 char **values;
2112 values = ldap_get_values(ads->ld, msg, field);
2113 if (!values)
2114 return False;
2115 if (!values[0]) {
2116 ldap_value_free(values);
2117 return False;
2120 *v = atoi(values[0]);
2121 ldap_value_free(values);
2122 return True;
2126 * pull a single objectGUID from an ADS result
2127 * @param ads connection to ADS server
2128 * @param msg results of search
2129 * @param guid 37-byte area to receive text guid
2130 * @return boolean indicating success
2132 BOOL ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2134 char **values;
2135 UUID_FLAT flat_guid;
2137 values = ldap_get_values(ads->ld, msg, "objectGUID");
2138 if (!values)
2139 return False;
2141 if (values[0]) {
2142 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2143 smb_uuid_unpack(flat_guid, guid);
2144 ldap_value_free(values);
2145 return True;
2147 ldap_value_free(values);
2148 return False;
2154 * pull a single DOM_SID from a ADS result
2155 * @param ads connection to ads server
2156 * @param msg Results of search
2157 * @param field Attribute to retrieve
2158 * @param sid Pointer to sid to store result
2159 * @return boolean inidicating success
2161 BOOL ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2162 DOM_SID *sid)
2164 struct berval **values;
2165 BOOL ret = False;
2167 values = ldap_get_values_len(ads->ld, msg, field);
2169 if (!values)
2170 return False;
2172 if (values[0])
2173 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2175 ldap_value_free_len(values);
2176 return ret;
2180 * pull an array of DOM_SIDs from a ADS result
2181 * @param ads connection to ads server
2182 * @param mem_ctx TALLOC_CTX for allocating sid array
2183 * @param msg Results of search
2184 * @param field Attribute to retrieve
2185 * @param sids pointer to sid array to allocate
2186 * @return the count of SIDs pulled
2188 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2189 LDAPMessage *msg, const char *field, DOM_SID **sids)
2191 struct berval **values;
2192 BOOL ret;
2193 int count, i;
2195 values = ldap_get_values_len(ads->ld, msg, field);
2197 if (!values)
2198 return 0;
2200 for (i=0; values[i]; i++)
2201 /* nop */ ;
2203 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2204 if (!(*sids)) {
2205 ldap_value_free_len(values);
2206 return 0;
2209 count = 0;
2210 for (i=0; values[i]; i++) {
2211 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2212 if (ret) {
2213 fstring sid;
2214 DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid, &(*sids)[count])));
2215 count++;
2219 ldap_value_free_len(values);
2220 return count;
2224 * pull a SEC_DESC from a ADS result
2225 * @param ads connection to ads server
2226 * @param mem_ctx TALLOC_CTX for allocating sid array
2227 * @param msg Results of search
2228 * @param field Attribute to retrieve
2229 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2230 * @return boolean inidicating success
2232 BOOL ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2233 LDAPMessage *msg, const char *field, SEC_DESC **sd)
2235 struct berval **values;
2236 prs_struct ps;
2237 BOOL ret = False;
2239 values = ldap_get_values_len(ads->ld, msg, field);
2241 if (!values) return False;
2243 if (values[0]) {
2244 prs_init(&ps, values[0]->bv_len, mem_ctx, UNMARSHALL);
2245 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
2246 prs_set_offset(&ps,0);
2248 ret = sec_io_desc("sd", sd, &ps, 1);
2251 ldap_value_free_len(values);
2252 return ret;
2256 * in order to support usernames longer than 21 characters we need to
2257 * use both the sAMAccountName and the userPrincipalName attributes
2258 * It seems that not all users have the userPrincipalName attribute set
2260 * @param ads connection to ads server
2261 * @param mem_ctx TALLOC_CTX for allocating sid array
2262 * @param msg Results of search
2263 * @return the username
2265 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2266 LDAPMessage *msg)
2268 #if 0 /* JERRY */
2269 char *ret, *p;
2271 /* lookup_name() only works on the sAMAccountName to
2272 returning the username portion of userPrincipalName
2273 breaks winbindd_getpwnam() */
2275 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2276 if (ret && (p = strchr_m(ret, '@'))) {
2277 *p = 0;
2278 return ret;
2280 #endif
2281 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2286 * find the update serial number - this is the core of the ldap cache
2287 * @param ads connection to ads server
2288 * @param ads connection to ADS server
2289 * @param usn Pointer to retrieved update serial number
2290 * @return status of search
2292 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2294 const char *attrs[] = {"highestCommittedUSN", NULL};
2295 ADS_STATUS status;
2296 LDAPMessage *res;
2298 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2299 if (!ADS_ERR_OK(status))
2300 return status;
2302 if (ads_count_replies(ads, res) != 1) {
2303 ads_msgfree(ads, res);
2304 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2307 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2308 ads_msgfree(ads, res);
2309 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2312 ads_msgfree(ads, res);
2313 return ADS_SUCCESS;
2316 /* parse a ADS timestring - typical string is
2317 '20020917091222.0Z0' which means 09:12.22 17th September
2318 2002, timezone 0 */
2319 static time_t ads_parse_time(const char *str)
2321 struct tm tm;
2323 ZERO_STRUCT(tm);
2325 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2326 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2327 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2328 return 0;
2330 tm.tm_year -= 1900;
2331 tm.tm_mon -= 1;
2333 return timegm(&tm);
2336 /********************************************************************
2337 ********************************************************************/
2339 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2341 const char *attrs[] = {"currentTime", NULL};
2342 ADS_STATUS status;
2343 LDAPMessage *res;
2344 char *timestr;
2345 TALLOC_CTX *ctx;
2346 ADS_STRUCT *ads_s = ads;
2348 if (!(ctx = talloc_init("ads_current_time"))) {
2349 return ADS_ERROR(LDAP_NO_MEMORY);
2352 /* establish a new ldap tcp session if necessary */
2354 if ( !ads->ld ) {
2355 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2356 ads->server.ldap_server )) == NULL )
2358 goto done;
2360 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2361 status = ads_connect( ads_s );
2362 if ( !ADS_ERR_OK(status))
2363 goto done;
2366 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2367 if (!ADS_ERR_OK(status)) {
2368 goto done;
2371 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2372 if (!timestr) {
2373 ads_msgfree(ads_s, res);
2374 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2375 goto done;
2378 /* but save the time and offset in the original ADS_STRUCT */
2380 ads->config.current_time = ads_parse_time(timestr);
2382 if (ads->config.current_time != 0) {
2383 ads->auth.time_offset = ads->config.current_time - time(NULL);
2384 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2387 ads_msgfree(ads, res);
2389 status = ADS_SUCCESS;
2391 done:
2392 /* free any temporary ads connections */
2393 if ( ads_s != ads ) {
2394 ads_destroy( &ads_s );
2396 talloc_destroy(ctx);
2398 return status;
2401 /********************************************************************
2402 ********************************************************************/
2404 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2406 const char *attrs[] = {"domainFunctionality", NULL};
2407 ADS_STATUS status;
2408 LDAPMessage *res;
2409 ADS_STRUCT *ads_s = ads;
2411 *val = DS_DOMAIN_FUNCTION_2000;
2413 /* establish a new ldap tcp session if necessary */
2415 if ( !ads->ld ) {
2416 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2417 ads->server.ldap_server )) == NULL )
2419 goto done;
2421 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2422 status = ads_connect( ads_s );
2423 if ( !ADS_ERR_OK(status))
2424 goto done;
2427 /* If the attribute does not exist assume it is a Windows 2000
2428 functional domain */
2430 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2431 if (!ADS_ERR_OK(status)) {
2432 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2433 status = ADS_SUCCESS;
2435 goto done;
2438 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2439 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2441 DEBUG(3,("ads_domain_func_level: %d\n", *val));
2444 ads_msgfree(ads, res);
2446 done:
2447 /* free any temporary ads connections */
2448 if ( ads_s != ads ) {
2449 ads_destroy( &ads_s );
2452 return status;
2456 * find the domain sid for our domain
2457 * @param ads connection to ads server
2458 * @param sid Pointer to domain sid
2459 * @return status of search
2461 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2463 const char *attrs[] = {"objectSid", NULL};
2464 LDAPMessage *res;
2465 ADS_STATUS rc;
2467 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2468 attrs, &res);
2469 if (!ADS_ERR_OK(rc)) return rc;
2470 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2471 ads_msgfree(ads, res);
2472 return ADS_ERROR_SYSTEM(ENOENT);
2474 ads_msgfree(ads, res);
2476 return ADS_SUCCESS;
2480 * find our site name
2481 * @param ads connection to ads server
2482 * @param mem_ctx Pointer to talloc context
2483 * @param site_name Pointer to the sitename
2484 * @return status of search
2486 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2488 ADS_STATUS status;
2489 LDAPMessage *res;
2490 const char *dn, *service_name;
2491 const char *attrs[] = { "dsServiceName", NULL };
2493 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2494 if (!ADS_ERR_OK(status)) {
2495 return status;
2498 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2499 if (service_name == NULL) {
2500 ads_msgfree(ads, res);
2501 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2504 ads_msgfree(ads, res);
2506 /* go up three levels */
2507 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2508 if (dn == NULL) {
2509 return ADS_ERROR(LDAP_NO_MEMORY);
2512 *site_name = talloc_strdup(mem_ctx, dn);
2513 if (*site_name == NULL) {
2514 return ADS_ERROR(LDAP_NO_MEMORY);
2517 return status;
2519 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2524 * find the site dn where a machine resides
2525 * @param ads connection to ads server
2526 * @param mem_ctx Pointer to talloc context
2527 * @param computer_name name of the machine
2528 * @param site_name Pointer to the sitename
2529 * @return status of search
2531 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
2533 ADS_STATUS status;
2534 LDAPMessage *res;
2535 const char *parent, *config_context, *filter;
2536 const char *attrs[] = { "configurationNamingContext", NULL };
2537 char *dn;
2539 /* shortcut a query */
2540 if (strequal(computer_name, ads->config.ldap_server_name)) {
2541 return ads_site_dn(ads, mem_ctx, site_dn);
2544 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2545 if (!ADS_ERR_OK(status)) {
2546 return status;
2549 config_context = ads_pull_string(ads, mem_ctx, res, "configurationNamingContext");
2550 if (config_context == NULL) {
2551 ads_msgfree(ads, res);
2552 return ADS_ERROR(LDAP_NO_MEMORY);
2555 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
2556 if (filter == NULL) {
2557 ads_msgfree(ads, res);
2558 return ADS_ERROR(LDAP_NO_MEMORY);
2561 ads_msgfree(ads, res);
2563 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE, filter, NULL, &res);
2564 if (!ADS_ERR_OK(status)) {
2565 return status;
2568 if (ads_count_replies(ads, res) != 1) {
2569 ads_msgfree(ads, res);
2570 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2573 dn = ads_get_dn(ads, res);
2574 if (dn == NULL) {
2575 ads_msgfree(ads, res);
2576 return ADS_ERROR(LDAP_NO_MEMORY);
2579 /* go up three levels */
2580 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
2581 if (parent == NULL) {
2582 ads_msgfree(ads, res);
2583 ads_memfree(ads, dn);
2584 return ADS_ERROR(LDAP_NO_MEMORY);
2587 *site_dn = talloc_strdup(mem_ctx, parent);
2588 if (*site_dn == NULL) {
2589 ads_msgfree(ads, res);
2590 ads_memfree(ads, dn);
2591 ADS_ERROR(LDAP_NO_MEMORY);
2594 ads_memfree(ads, dn);
2595 ads_msgfree(ads, res);
2597 return status;
2601 * get the upn suffixes for a domain
2602 * @param ads connection to ads server
2603 * @param mem_ctx Pointer to talloc context
2604 * @param suffixes Pointer to an array of suffixes
2605 * @param site_name Pointer to the number of suffixes
2606 * @return status of search
2608 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char **suffixes, size_t *num_suffixes)
2610 ADS_STATUS status;
2611 LDAPMessage *res;
2612 const char *config_context, *base;
2613 const char *attrs[] = { "configurationNamingContext", NULL };
2614 const char *attrs2[] = { "uPNSuffixes", NULL };
2616 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2617 if (!ADS_ERR_OK(status)) {
2618 return status;
2621 config_context = ads_pull_string(ads, mem_ctx, res, "configurationNamingContext");
2622 if (config_context == NULL) {
2623 return ADS_ERROR(LDAP_NO_MEMORY);
2626 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
2627 if (base == NULL) {
2628 return ADS_ERROR(LDAP_NO_MEMORY);
2631 status = ads_search_dn(ads, &res, base, attrs2);
2632 if (!ADS_ERR_OK(status)) {
2633 return status;
2636 if (ads_count_replies(ads, res) != 1) {
2637 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2640 suffixes = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
2641 if (suffixes == NULL) {
2642 ads_msgfree(ads, res);
2643 return ADS_ERROR(LDAP_NO_MEMORY);
2646 ads_msgfree(ads, res);
2648 return status;
2652 * pull a DOM_SID from an extended dn string
2653 * @param mem_ctx TALLOC_CTX
2654 * @param flags string type of extended_dn
2655 * @param sid pointer to a DOM_SID
2656 * @return boolean inidicating success
2658 BOOL ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
2659 const char *dn,
2660 enum ads_extended_dn_flags flags,
2661 DOM_SID *sid)
2663 char *p, *q;
2665 if (!dn) {
2666 return False;
2670 * ADS_EXTENDED_DN_HEX_STRING:
2671 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
2673 * ADS_EXTENDED_DN_STRING (only with w2k3):
2674 <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
2677 p = strchr(dn, ';');
2678 if (!p) {
2679 return False;
2682 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
2683 return False;
2686 p += strlen(";<SID=");
2688 q = strchr(p, '>');
2689 if (!q) {
2690 return False;
2693 *q = '\0';
2695 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
2697 switch (flags) {
2699 case ADS_EXTENDED_DN_STRING:
2700 if (!string_to_sid(sid, p)) {
2701 return False;
2703 break;
2704 case ADS_EXTENDED_DN_HEX_STRING: {
2705 pstring buf;
2706 size_t buf_len;
2708 buf_len = strhex_to_str(buf, strlen(p), p);
2709 if (buf_len == 0) {
2710 return False;
2713 if (!sid_parse(buf, buf_len, sid)) {
2714 DEBUG(10,("failed to parse sid\n"));
2715 return False;
2717 break;
2719 default:
2720 DEBUG(10,("unknown extended dn format\n"));
2721 return False;
2724 return True;
2728 * pull an array of DOM_SIDs from a ADS result
2729 * @param ads connection to ads server
2730 * @param mem_ctx TALLOC_CTX for allocating sid array
2731 * @param msg Results of search
2732 * @param field Attribute to retrieve
2733 * @param flags string type of extended_dn
2734 * @param sids pointer to sid array to allocate
2735 * @return the count of SIDs pulled
2737 int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads,
2738 TALLOC_CTX *mem_ctx,
2739 LDAPMessage *msg,
2740 const char *field,
2741 enum ads_extended_dn_flags flags,
2742 DOM_SID **sids)
2744 int i;
2745 size_t dn_count;
2746 char **dn_strings;
2748 if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field,
2749 &dn_count)) == NULL) {
2750 return 0;
2753 (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, dn_count + 1);
2754 if (!(*sids)) {
2755 TALLOC_FREE(dn_strings);
2756 return 0;
2759 for (i=0; i<dn_count; i++) {
2761 if (!ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i],
2762 flags, &(*sids)[i])) {
2763 TALLOC_FREE(*sids);
2764 TALLOC_FREE(dn_strings);
2765 return 0;
2769 TALLOC_FREE(dn_strings);
2771 return dn_count;
2774 /********************************************************************
2775 ********************************************************************/
2777 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2779 LDAPMessage *res = NULL;
2780 ADS_STATUS status;
2781 int count = 0;
2782 char *name = NULL;
2784 status = ads_find_machine_acct(ads, &res, global_myname());
2785 if (!ADS_ERR_OK(status)) {
2786 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
2787 global_myname()));
2788 goto out;
2791 if ( (count = ads_count_replies(ads, res)) != 1 ) {
2792 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
2793 goto out;
2796 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
2797 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
2800 out:
2801 ads_msgfree(ads, res);
2803 return name;
2806 /********************************************************************
2807 ********************************************************************/
2809 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2811 LDAPMessage *res = NULL;
2812 ADS_STATUS status;
2813 int count = 0;
2814 char *name = NULL;
2816 status = ads_find_machine_acct(ads, &res, global_myname());
2817 if (!ADS_ERR_OK(status)) {
2818 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
2819 global_myname()));
2820 goto out;
2823 if ( (count = ads_count_replies(ads, res)) != 1 ) {
2824 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
2825 goto out;
2828 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
2829 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
2832 out:
2833 ads_msgfree(ads, res);
2835 return name;
2838 /********************************************************************
2839 ********************************************************************/
2841 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2843 LDAPMessage *res = NULL;
2844 ADS_STATUS status;
2845 int count = 0;
2846 char *name = NULL;
2848 status = ads_find_machine_acct(ads, &res, global_myname());
2849 if (!ADS_ERR_OK(status)) {
2850 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
2851 global_myname()));
2852 goto out;
2855 if ( (count = ads_count_replies(ads, res)) != 1 ) {
2856 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
2857 goto out;
2860 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
2861 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
2864 out:
2865 ads_msgfree(ads, res);
2867 return name;
2870 #if 0
2872 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
2875 * Join a machine to a realm
2876 * Creates the machine account and sets the machine password
2877 * @param ads connection to ads server
2878 * @param machine name of host to add
2879 * @param org_unit Organizational unit to place machine in
2880 * @return status of join
2882 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
2883 uint32 account_type, const char *org_unit)
2885 ADS_STATUS status;
2886 LDAPMessage *res = NULL;
2887 char *machine;
2889 /* machine name must be lowercase */
2890 machine = SMB_STRDUP(machine_name);
2891 strlower_m(machine);
2894 status = ads_find_machine_acct(ads, (void **)&res, machine);
2895 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
2896 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
2897 status = ads_leave_realm(ads, machine);
2898 if (!ADS_ERR_OK(status)) {
2899 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
2900 machine, ads->config.realm));
2901 return status;
2905 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
2906 if (!ADS_ERR_OK(status)) {
2907 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
2908 SAFE_FREE(machine);
2909 return status;
2912 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
2913 if (!ADS_ERR_OK(status)) {
2914 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
2915 SAFE_FREE(machine);
2916 return status;
2919 SAFE_FREE(machine);
2920 ads_msgfree(ads, res);
2922 return status;
2924 #endif
2927 * Delete a machine from the realm
2928 * @param ads connection to ads server
2929 * @param hostname Machine to remove
2930 * @return status of delete
2932 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
2934 ADS_STATUS status;
2935 void *msg;
2936 LDAPMessage *res;
2937 char *hostnameDN, *host;
2938 int rc;
2939 LDAPControl ldap_control;
2940 LDAPControl * pldap_control[2] = {NULL, NULL};
2942 pldap_control[0] = &ldap_control;
2943 memset(&ldap_control, 0, sizeof(LDAPControl));
2944 ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
2946 /* hostname must be lowercase */
2947 host = SMB_STRDUP(hostname);
2948 strlower_m(host);
2950 status = ads_find_machine_acct(ads, &res, host);
2951 if (!ADS_ERR_OK(status)) {
2952 DEBUG(0, ("Host account for %s does not exist.\n", host));
2953 SAFE_FREE(host);
2954 return status;
2957 msg = ads_first_entry(ads, res);
2958 if (!msg) {
2959 SAFE_FREE(host);
2960 return ADS_ERROR_SYSTEM(ENOENT);
2963 hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
2965 rc = ldap_delete_ext_s(ads->ld, hostnameDN, pldap_control, NULL);
2966 if (rc) {
2967 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
2968 }else {
2969 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
2972 if (rc != LDAP_SUCCESS) {
2973 const char *attrs[] = { "cn", NULL };
2974 LDAPMessage *msg_sub;
2976 /* we only search with scope ONE, we do not expect any further
2977 * objects to be created deeper */
2979 status = ads_do_search_retry(ads, hostnameDN,
2980 LDAP_SCOPE_ONELEVEL,
2981 "(objectclass=*)", attrs, &res);
2983 if (!ADS_ERR_OK(status)) {
2984 SAFE_FREE(host);
2985 ads_memfree(ads, hostnameDN);
2986 return status;
2989 for (msg_sub = ads_first_entry(ads, res); msg_sub;
2990 msg_sub = ads_next_entry(ads, msg_sub)) {
2992 char *dn = NULL;
2994 if ((dn = ads_get_dn(ads, msg_sub)) == NULL) {
2995 SAFE_FREE(host);
2996 ads_memfree(ads, hostnameDN);
2997 return ADS_ERROR(LDAP_NO_MEMORY);
3000 status = ads_del_dn(ads, dn);
3001 if (!ADS_ERR_OK(status)) {
3002 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3003 SAFE_FREE(host);
3004 ads_memfree(ads, dn);
3005 ads_memfree(ads, hostnameDN);
3006 return status;
3009 ads_memfree(ads, dn);
3012 /* there should be no subordinate objects anymore */
3013 status = ads_do_search_retry(ads, hostnameDN,
3014 LDAP_SCOPE_ONELEVEL,
3015 "(objectclass=*)", attrs, &res);
3017 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3018 SAFE_FREE(host);
3019 ads_memfree(ads, hostnameDN);
3020 return status;
3023 /* delete hostnameDN now */
3024 status = ads_del_dn(ads, hostnameDN);
3025 if (!ADS_ERR_OK(status)) {
3026 SAFE_FREE(host);
3027 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3028 ads_memfree(ads, hostnameDN);
3029 return status;
3033 ads_memfree(ads, hostnameDN);
3035 status = ads_find_machine_acct(ads, &res, host);
3036 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3037 DEBUG(3, ("Failed to remove host account.\n"));
3038 SAFE_FREE(host);
3039 return status;
3042 SAFE_FREE(host);
3043 return status;
3046 #endif