r19646: Fix memleak in the default_ou_string handling. Thanks to David Hu
[Samba.git] / source3 / libads / ldap.c
blob927b86fe935c5c75277f35e045d449b381529059
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;
143 try a connection to a given ldap server, returning True and setting the servers IP
144 in the ads struct if successful
146 BOOL ads_try_connect(ADS_STRUCT *ads, const char *server )
148 char *srv;
149 struct cldap_netlogon_reply cldap_reply;
151 if (!server || !*server) {
152 return False;
155 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
156 server, ads->server.realm));
158 /* this copes with inet_ntoa brokenness */
160 srv = SMB_STRDUP(server);
162 ZERO_STRUCT( cldap_reply );
164 if ( !ads_cldap_netlogon( srv, ads->server.realm, &cldap_reply ) ) {
165 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", srv));
166 SAFE_FREE( srv );
167 return False;
170 /* Check the CLDAP reply flags */
172 if ( !(cldap_reply.flags & ADS_LDAP) ) {
173 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
174 srv));
175 SAFE_FREE( srv );
176 return False;
179 /* Fill in the ads->config values */
181 SAFE_FREE(ads->config.realm);
182 SAFE_FREE(ads->config.bind_path);
183 SAFE_FREE(ads->config.ldap_server_name);
184 SAFE_FREE(ads->config.server_site_name);
185 SAFE_FREE(ads->config.client_site_name);
186 SAFE_FREE(ads->server.workgroup);
188 ads->config.flags = cldap_reply.flags;
189 ads->config.ldap_server_name = SMB_STRDUP(cldap_reply.hostname);
190 strupper_m(cldap_reply.domain);
191 ads->config.realm = SMB_STRDUP(cldap_reply.domain);
192 ads->config.bind_path = ads_build_dn(ads->config.realm);
193 if (*cldap_reply.server_site_name) {
194 ads->config.server_site_name =
195 SMB_STRDUP(cldap_reply.server_site_name);
197 if (*cldap_reply.client_site_name) {
198 ads->config.client_site_name =
199 SMB_STRDUP(cldap_reply.client_site_name);
202 ads->server.workgroup = SMB_STRDUP(cldap_reply.netbios_domain);
204 ads->ldap_port = LDAP_PORT;
205 ads->ldap_ip = *interpret_addr2(srv);
206 SAFE_FREE(srv);
208 /* Store our site name. */
209 sitename_store( cldap_reply.client_site_name );
211 return True;
214 /**********************************************************************
215 Try to find an AD dc using our internal name resolution routines
216 Try the realm first and then then workgroup name if netbios is not
217 disabled
218 **********************************************************************/
220 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
222 const char *c_realm;
223 int count, i=0;
224 struct ip_service *ip_list;
225 pstring realm;
226 BOOL got_realm = False;
227 BOOL use_own_domain = False;
228 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
230 /* if the realm and workgroup are both empty, assume they are ours */
232 /* realm */
233 c_realm = ads->server.realm;
235 if ( !c_realm || !*c_realm ) {
236 /* special case where no realm and no workgroup means our own */
237 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
238 use_own_domain = True;
239 c_realm = lp_realm();
243 if (c_realm && *c_realm)
244 got_realm = True;
246 again:
247 /* we need to try once with the realm name and fallback to the
248 netbios domain name if we fail (if netbios has not been disabled */
250 if ( !got_realm && !lp_disable_netbios() ) {
251 c_realm = ads->server.workgroup;
252 if (!c_realm || !*c_realm) {
253 if ( use_own_domain )
254 c_realm = lp_workgroup();
257 if ( !c_realm || !*c_realm ) {
258 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
259 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
263 pstrcpy( realm, c_realm );
265 DEBUG(6,("ads_find_dc: looking for %s '%s'\n",
266 (got_realm ? "realm" : "domain"), realm));
268 status = get_sorted_dc_list(realm, &ip_list, &count, got_realm);
269 if (!NT_STATUS_IS_OK(status)) {
270 /* fall back to netbios if we can */
271 if ( got_realm && !lp_disable_netbios() ) {
272 got_realm = False;
273 goto again;
276 return status;
279 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
280 for ( i=0; i<count; i++ ) {
281 fstring server;
283 fstrcpy( server, inet_ntoa(ip_list[i].ip) );
285 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
286 continue;
288 if (!got_realm) {
289 /* realm in this case is a workgroup name. We need
290 to ignore any IP addresses in the negative connection
291 cache that match ip addresses returned in the ad realm
292 case. It sucks that I have to reproduce the logic above... */
293 c_realm = ads->server.realm;
294 if ( !c_realm || !*c_realm ) {
295 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
296 c_realm = lp_realm();
299 if (c_realm && *c_realm &&
300 !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm, server))) {
301 /* Ensure we add the workgroup name for this
302 IP address as negative too. */
303 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
304 continue;
308 if ( ads_try_connect(ads, server) ) {
309 SAFE_FREE(ip_list);
310 return NT_STATUS_OK;
313 /* keep track of failures */
314 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
317 SAFE_FREE(ip_list);
319 return NT_STATUS_NO_LOGON_SERVERS;
324 * Connect to the LDAP server
325 * @param ads Pointer to an existing ADS_STRUCT
326 * @return status of connection
328 ADS_STATUS ads_connect(ADS_STRUCT *ads)
330 int version = LDAP_VERSION3;
331 ADS_STATUS status;
332 NTSTATUS ntstatus;
334 ads->last_attempt = time(NULL);
335 ads->ld = NULL;
337 /* try with a user specified server */
339 if (ads->server.ldap_server &&
340 ads_try_connect(ads, ads->server.ldap_server)) {
341 goto got_connection;
344 ntstatus = ads_find_dc(ads);
345 if (NT_STATUS_IS_OK(ntstatus)) {
346 goto got_connection;
349 return ADS_ERROR_NT(ntstatus);
351 got_connection:
352 DEBUG(3,("Connected to LDAP server %s\n", inet_ntoa(ads->ldap_ip)));
354 if (!ads->auth.user_name) {
355 /* Must use the userPrincipalName value here or sAMAccountName
356 and not servicePrincipalName; found by Guenther Deschner */
358 asprintf(&ads->auth.user_name, "%s$", global_myname() );
361 if (!ads->auth.realm) {
362 ads->auth.realm = SMB_STRDUP(ads->config.realm);
365 if (!ads->auth.kdc_server) {
366 ads->auth.kdc_server = SMB_STRDUP(inet_ntoa(ads->ldap_ip));
369 #if KRB5_DNS_HACK
370 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
371 to MIT kerberos to work (tridge) */
373 char *env;
374 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
375 setenv(env, ads->auth.kdc_server, 1);
376 free(env);
378 #endif
380 /* If the caller() requested no LDAP bind, then we are done */
382 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
383 return ADS_SUCCESS;
386 /* Otherwise setup the TCP LDAP session */
388 if ( (ads->ld = ldap_open_with_timeout(ads->config.ldap_server_name,
389 LDAP_PORT, lp_ldap_timeout())) == NULL )
391 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
394 /* cache the successful connection for workgroup and realm */
395 if (ads_sitename_match(ads)) {
396 saf_store( ads->server.workgroup, inet_ntoa(ads->ldap_ip));
397 saf_store( ads->server.realm, inet_ntoa(ads->ldap_ip));
400 ldap_set_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
402 status = ADS_ERROR(smb_ldap_start_tls(ads->ld, version));
403 if (!ADS_ERR_OK(status)) {
404 return status;
407 /* fill in the current time and offsets */
409 status = ads_current_time( ads );
410 if ( !ADS_ERR_OK(status) ) {
411 return status;
414 /* Now do the bind */
416 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
417 return ADS_ERROR(ldap_simple_bind_s( ads->ld, NULL, NULL));
420 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
421 return ADS_ERROR(ldap_simple_bind_s( ads->ld, ads->auth.user_name, ads->auth.password));
424 return ads_sasl_bind(ads);
428 Duplicate a struct berval into talloc'ed memory
430 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
432 struct berval *value;
434 if (!in_val) return NULL;
436 value = TALLOC_ZERO_P(ctx, struct berval);
437 if (value == NULL)
438 return NULL;
439 if (in_val->bv_len == 0) return value;
441 value->bv_len = in_val->bv_len;
442 value->bv_val = (char *)TALLOC_MEMDUP(ctx, in_val->bv_val,
443 in_val->bv_len);
444 return value;
448 Make a values list out of an array of (struct berval *)
450 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
451 const struct berval **in_vals)
453 struct berval **values;
454 int i;
456 if (!in_vals) return NULL;
457 for (i=0; in_vals[i]; i++)
458 ; /* count values */
459 values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
460 if (!values) return NULL;
462 for (i=0; in_vals[i]; i++) {
463 values[i] = dup_berval(ctx, in_vals[i]);
465 return values;
469 UTF8-encode a values list out of an array of (char *)
471 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
473 char **values;
474 int i;
476 if (!in_vals) return NULL;
477 for (i=0; in_vals[i]; i++)
478 ; /* count values */
479 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
480 if (!values) return NULL;
482 for (i=0; in_vals[i]; i++) {
483 push_utf8_talloc(ctx, &values[i], in_vals[i]);
485 return values;
489 Pull a (char *) array out of a UTF8-encoded values list
491 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
493 char **values;
494 int i;
496 if (!in_vals) return NULL;
497 for (i=0; in_vals[i]; i++)
498 ; /* count values */
499 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
500 if (!values) return NULL;
502 for (i=0; in_vals[i]; i++) {
503 pull_utf8_talloc(ctx, &values[i], in_vals[i]);
505 return values;
509 * Do a search with paged results. cookie must be null on the first
510 * call, and then returned on each subsequent call. It will be null
511 * again when the entire search is complete
512 * @param ads connection to ads server
513 * @param bind_path Base dn for the search
514 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
515 * @param expr Search expression - specified in local charset
516 * @param attrs Attributes to retrieve - specified in utf8 or ascii
517 * @param res ** which will contain results - free res* with ads_msgfree()
518 * @param count Number of entries retrieved on this page
519 * @param cookie The paged results cookie to be returned on subsequent calls
520 * @return status of search
522 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
523 const char *bind_path,
524 int scope, const char *expr,
525 const char **attrs, void *args,
526 LDAPMessage **res,
527 int *count, struct berval **cookie)
529 int rc, i, version;
530 char *utf8_expr, *utf8_path, **search_attrs;
531 LDAPControl PagedResults, NoReferrals, ExtendedDn, *controls[4], **rcontrols;
532 BerElement *cookie_be = NULL;
533 struct berval *cookie_bv= NULL;
534 BerElement *extdn_be = NULL;
535 struct berval *extdn_bv= NULL;
537 TALLOC_CTX *ctx;
538 ads_control *external_control = (ads_control *) args;
540 *res = NULL;
542 if (!(ctx = talloc_init("ads_do_paged_search_args")))
543 return ADS_ERROR(LDAP_NO_MEMORY);
545 /* 0 means the conversion worked but the result was empty
546 so we only fail if it's -1. In any case, it always
547 at least nulls out the dest */
548 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
549 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
550 rc = LDAP_NO_MEMORY;
551 goto done;
554 if (!attrs || !(*attrs))
555 search_attrs = NULL;
556 else {
557 /* This would be the utf8-encoded version...*/
558 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
559 if (!(str_list_copy(&search_attrs, attrs))) {
560 rc = LDAP_NO_MEMORY;
561 goto done;
566 /* Paged results only available on ldap v3 or later */
567 ldap_get_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
568 if (version < LDAP_VERSION3) {
569 rc = LDAP_NOT_SUPPORTED;
570 goto done;
573 cookie_be = ber_alloc_t(LBER_USE_DER);
574 if (*cookie) {
575 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
576 ber_bvfree(*cookie); /* don't need it from last time */
577 *cookie = NULL;
578 } else {
579 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
581 ber_flatten(cookie_be, &cookie_bv);
582 PagedResults.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID);
583 PagedResults.ldctl_iscritical = (char) 1;
584 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
585 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
587 NoReferrals.ldctl_oid = CONST_DISCARD(char *, ADS_NO_REFERRALS_OID);
588 NoReferrals.ldctl_iscritical = (char) 0;
589 NoReferrals.ldctl_value.bv_len = 0;
590 NoReferrals.ldctl_value.bv_val = CONST_DISCARD(char *, "");
592 if (external_control && strequal(external_control->control, ADS_EXTENDED_DN_OID)) {
594 ExtendedDn.ldctl_oid = CONST_DISCARD(char *, external_control->control);
595 ExtendedDn.ldctl_iscritical = (char) external_control->critical;
597 /* win2k does not accept a ldctl_value beeing passed in */
599 if (external_control->val != 0) {
601 if ((extdn_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
602 rc = LDAP_NO_MEMORY;
603 goto done;
606 if ((ber_printf(extdn_be, "{i}", (ber_int_t) external_control->val)) == -1) {
607 rc = LDAP_NO_MEMORY;
608 goto done;
610 if ((ber_flatten(extdn_be, &extdn_bv)) == -1) {
611 rc = LDAP_NO_MEMORY;
612 goto done;
615 ExtendedDn.ldctl_value.bv_len = extdn_bv->bv_len;
616 ExtendedDn.ldctl_value.bv_val = extdn_bv->bv_val;
618 } else {
619 ExtendedDn.ldctl_value.bv_len = 0;
620 ExtendedDn.ldctl_value.bv_val = NULL;
623 controls[0] = &NoReferrals;
624 controls[1] = &PagedResults;
625 controls[2] = &ExtendedDn;
626 controls[3] = NULL;
628 } else {
629 controls[0] = &NoReferrals;
630 controls[1] = &PagedResults;
631 controls[2] = NULL;
634 /* we need to disable referrals as the openldap libs don't
635 handle them and paged results at the same time. Using them
636 together results in the result record containing the server
637 page control being removed from the result list (tridge/jmcd)
639 leaving this in despite the control that says don't generate
640 referrals, in case the server doesn't support it (jmcd)
642 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
644 rc = ldap_search_with_timeout(ads->ld, utf8_path, scope, utf8_expr,
645 search_attrs, 0, controls,
646 NULL, LDAP_NO_LIMIT,
647 (LDAPMessage **)res);
649 ber_free(cookie_be, 1);
650 ber_bvfree(cookie_bv);
652 if (rc) {
653 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
654 ldap_err2string(rc)));
655 goto done;
658 rc = ldap_parse_result(ads->ld, *res, NULL, NULL, NULL,
659 NULL, &rcontrols, 0);
661 if (!rcontrols) {
662 goto done;
665 for (i=0; rcontrols[i]; i++) {
666 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
667 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
668 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
669 &cookie_bv);
670 /* the berval is the cookie, but must be freed when
671 it is all done */
672 if (cookie_bv->bv_len) /* still more to do */
673 *cookie=ber_bvdup(cookie_bv);
674 else
675 *cookie=NULL;
676 ber_bvfree(cookie_bv);
677 ber_free(cookie_be, 1);
678 break;
681 ldap_controls_free(rcontrols);
683 done:
684 talloc_destroy(ctx);
686 if (extdn_be) {
687 ber_free(extdn_be, 1);
690 if (extdn_bv) {
691 ber_bvfree(extdn_bv);
694 /* if/when we decide to utf8-encode attrs, take out this next line */
695 str_list_free(&search_attrs);
697 return ADS_ERROR(rc);
700 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
701 int scope, const char *expr,
702 const char **attrs, LDAPMessage **res,
703 int *count, struct berval **cookie)
705 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
710 * Get all results for a search. This uses ads_do_paged_search() to return
711 * all entries in a large search.
712 * @param ads connection to ads server
713 * @param bind_path Base dn for the search
714 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
715 * @param expr Search expression
716 * @param attrs Attributes to retrieve
717 * @param res ** which will contain results - free res* with ads_msgfree()
718 * @return status of search
720 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
721 int scope, const char *expr,
722 const char **attrs, void *args,
723 LDAPMessage **res)
725 struct berval *cookie = NULL;
726 int count = 0;
727 ADS_STATUS status;
729 *res = NULL;
730 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
731 &count, &cookie);
733 if (!ADS_ERR_OK(status))
734 return status;
736 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
737 while (cookie) {
738 LDAPMessage *res2 = NULL;
739 ADS_STATUS status2;
740 LDAPMessage *msg, *next;
742 status2 = ads_do_paged_search_args(ads, bind_path, scope, expr,
743 attrs, args, &res2, &count, &cookie);
745 if (!ADS_ERR_OK(status2)) break;
747 /* this relies on the way that ldap_add_result_entry() works internally. I hope
748 that this works on all ldap libs, but I have only tested with openldap */
749 for (msg = ads_first_entry(ads, res2); msg; msg = next) {
750 next = ads_next_entry(ads, msg);
751 ldap_add_result_entry((LDAPMessage **)res, msg);
753 /* note that we do not free res2, as the memory is now
754 part of the main returned list */
756 #else
757 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
758 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
759 #endif
761 return status;
764 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
765 int scope, const char *expr,
766 const char **attrs, LDAPMessage **res)
768 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
772 * Run a function on all results for a search. Uses ads_do_paged_search() and
773 * runs the function as each page is returned, using ads_process_results()
774 * @param ads connection to ads server
775 * @param bind_path Base dn for the search
776 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
777 * @param expr Search expression - specified in local charset
778 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
779 * @param fn Function which takes attr name, values list, and data_area
780 * @param data_area Pointer which is passed to function on each call
781 * @return status of search
783 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
784 int scope, const char *expr, const char **attrs,
785 BOOL(*fn)(char *, void **, void *),
786 void *data_area)
788 struct berval *cookie = NULL;
789 int count = 0;
790 ADS_STATUS status;
791 LDAPMessage *res;
793 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
794 &count, &cookie);
796 if (!ADS_ERR_OK(status)) return status;
798 ads_process_results(ads, res, fn, data_area);
799 ads_msgfree(ads, res);
801 while (cookie) {
802 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
803 &res, &count, &cookie);
805 if (!ADS_ERR_OK(status)) break;
807 ads_process_results(ads, res, fn, data_area);
808 ads_msgfree(ads, res);
811 return status;
815 * Do a search with a timeout.
816 * @param ads connection to ads server
817 * @param bind_path Base dn for the search
818 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
819 * @param expr Search expression
820 * @param attrs Attributes to retrieve
821 * @param res ** which will contain results - free res* with ads_msgfree()
822 * @return status of search
824 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
825 const char *expr,
826 const char **attrs, LDAPMessage **res)
828 int rc;
829 char *utf8_expr, *utf8_path, **search_attrs = NULL;
830 TALLOC_CTX *ctx;
832 *res = NULL;
833 if (!(ctx = talloc_init("ads_do_search"))) {
834 DEBUG(1,("ads_do_search: talloc_init() failed!"));
835 return ADS_ERROR(LDAP_NO_MEMORY);
838 /* 0 means the conversion worked but the result was empty
839 so we only fail if it's negative. In any case, it always
840 at least nulls out the dest */
841 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
842 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
843 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
844 rc = LDAP_NO_MEMORY;
845 goto done;
848 if (!attrs || !(*attrs))
849 search_attrs = NULL;
850 else {
851 /* This would be the utf8-encoded version...*/
852 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
853 if (!(str_list_copy(&search_attrs, attrs)))
855 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
856 rc = LDAP_NO_MEMORY;
857 goto done;
861 /* see the note in ads_do_paged_search - we *must* disable referrals */
862 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
864 rc = ldap_search_with_timeout(ads->ld, utf8_path, scope, utf8_expr,
865 search_attrs, 0, NULL, NULL,
866 LDAP_NO_LIMIT,
867 (LDAPMessage **)res);
869 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
870 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
871 rc = 0;
874 done:
875 talloc_destroy(ctx);
876 /* if/when we decide to utf8-encode attrs, take out this next line */
877 str_list_free(&search_attrs);
878 return ADS_ERROR(rc);
881 * Do a general ADS search
882 * @param ads connection to ads server
883 * @param res ** which will contain results - free res* with ads_msgfree()
884 * @param expr Search expression
885 * @param attrs Attributes to retrieve
886 * @return status of search
888 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
889 const char *expr, const char **attrs)
891 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
892 expr, attrs, res);
896 * Do a search on a specific DistinguishedName
897 * @param ads connection to ads server
898 * @param res ** which will contain results - free res* with ads_msgfree()
899 * @param dn DistinguishName to search
900 * @param attrs Attributes to retrieve
901 * @return status of search
903 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
904 const char *dn, const char **attrs)
906 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
907 attrs, res);
911 * Free up memory from a ads_search
912 * @param ads connection to ads server
913 * @param msg Search results to free
915 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
917 if (!msg) return;
918 ldap_msgfree(msg);
922 * Free up memory from various ads requests
923 * @param ads connection to ads server
924 * @param mem Area to free
926 void ads_memfree(ADS_STRUCT *ads, void *mem)
928 SAFE_FREE(mem);
932 * Get a dn from search results
933 * @param ads connection to ads server
934 * @param msg Search result
935 * @return dn string
937 char *ads_get_dn(ADS_STRUCT *ads, LDAPMessage *msg)
939 char *utf8_dn, *unix_dn;
941 utf8_dn = ldap_get_dn(ads->ld, msg);
943 if (!utf8_dn) {
944 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
945 return NULL;
948 if (pull_utf8_allocate(&unix_dn, utf8_dn) == (size_t)-1) {
949 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
950 utf8_dn ));
951 return NULL;
953 ldap_memfree(utf8_dn);
954 return unix_dn;
958 * Get a canonical dn from search results
959 * @param ads connection to ads server
960 * @param msg Search result
961 * @return dn string
963 char *ads_get_dn_canonical(ADS_STRUCT *ads, LDAPMessage *msg)
965 #ifdef HAVE_LDAP_DN2AD_CANONICAL
966 return ldap_dn2ad_canonical(ads_get_dn(ads, msg));
967 #else
968 return NULL;
969 #endif
973 * Get the parent from a dn
974 * @param dn the dn to return the parent from
975 * @return parent dn string
977 char *ads_parent_dn(const char *dn)
979 char *p;
981 if (dn == NULL) {
982 return NULL;
985 p = strchr(dn, ',');
987 if (p == NULL) {
988 return NULL;
991 return p+1;
995 * Find a machine account given a hostname
996 * @param ads connection to ads server
997 * @param res ** which will contain results - free res* with ads_msgfree()
998 * @param host Hostname to search for
999 * @return status of search
1001 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1002 const char *machine)
1004 ADS_STATUS status;
1005 char *expr;
1006 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1008 *res = NULL;
1010 /* the easiest way to find a machine account anywhere in the tree
1011 is to look for hostname$ */
1012 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1013 DEBUG(1, ("asprintf failed!\n"));
1014 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1017 status = ads_search(ads, res, expr, attrs);
1018 SAFE_FREE(expr);
1019 return status;
1023 * Initialize a list of mods to be used in a modify request
1024 * @param ctx An initialized TALLOC_CTX
1025 * @return allocated ADS_MODLIST
1027 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1029 #define ADS_MODLIST_ALLOC_SIZE 10
1030 LDAPMod **mods;
1032 if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1033 /* -1 is safety to make sure we don't go over the end.
1034 need to reset it to NULL before doing ldap modify */
1035 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1037 return (ADS_MODLIST)mods;
1042 add an attribute to the list, with values list already constructed
1044 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1045 int mod_op, const char *name,
1046 const void *_invals)
1048 const void **invals = (const void **)_invals;
1049 int curmod;
1050 LDAPMod **modlist = (LDAPMod **) *mods;
1051 struct berval **ber_values = NULL;
1052 char **char_values = NULL;
1054 if (!invals) {
1055 mod_op = LDAP_MOD_DELETE;
1056 } else {
1057 if (mod_op & LDAP_MOD_BVALUES)
1058 ber_values = ads_dup_values(ctx,
1059 (const struct berval **)invals);
1060 else
1061 char_values = ads_push_strvals(ctx,
1062 (const char **) invals);
1065 /* find the first empty slot */
1066 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1067 curmod++);
1068 if (modlist[curmod] == (LDAPMod *) -1) {
1069 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
1070 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1071 return ADS_ERROR(LDAP_NO_MEMORY);
1072 memset(&modlist[curmod], 0,
1073 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1074 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1075 *mods = (ADS_MODLIST)modlist;
1078 if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
1079 return ADS_ERROR(LDAP_NO_MEMORY);
1080 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1081 if (mod_op & LDAP_MOD_BVALUES) {
1082 modlist[curmod]->mod_bvalues = ber_values;
1083 } else if (mod_op & LDAP_MOD_DELETE) {
1084 modlist[curmod]->mod_values = NULL;
1085 } else {
1086 modlist[curmod]->mod_values = char_values;
1089 modlist[curmod]->mod_op = mod_op;
1090 return ADS_ERROR(LDAP_SUCCESS);
1094 * Add a single string value to a mod list
1095 * @param ctx An initialized TALLOC_CTX
1096 * @param mods An initialized ADS_MODLIST
1097 * @param name The attribute name to add
1098 * @param val The value to add - NULL means DELETE
1099 * @return ADS STATUS indicating success of add
1101 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1102 const char *name, const char *val)
1104 const char *values[2];
1106 values[0] = val;
1107 values[1] = NULL;
1109 if (!val)
1110 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1111 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1115 * Add an array of string values to a mod list
1116 * @param ctx An initialized TALLOC_CTX
1117 * @param mods An initialized ADS_MODLIST
1118 * @param name The attribute name to add
1119 * @param vals The array of string values to add - NULL means DELETE
1120 * @return ADS STATUS indicating success of add
1122 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1123 const char *name, const char **vals)
1125 if (!vals)
1126 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1127 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1128 name, (const void **) vals);
1131 #if 0
1133 * Add a single ber-encoded value to a mod list
1134 * @param ctx An initialized TALLOC_CTX
1135 * @param mods An initialized ADS_MODLIST
1136 * @param name The attribute name to add
1137 * @param val The value to add - NULL means DELETE
1138 * @return ADS STATUS indicating success of add
1140 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1141 const char *name, const struct berval *val)
1143 const struct berval *values[2];
1145 values[0] = val;
1146 values[1] = NULL;
1147 if (!val)
1148 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1149 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1150 name, (const void **) values);
1152 #endif
1155 * Perform an ldap modify
1156 * @param ads connection to ads server
1157 * @param mod_dn DistinguishedName to modify
1158 * @param mods list of modifications to perform
1159 * @return status of modify
1161 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1163 int ret,i;
1164 char *utf8_dn = NULL;
1166 this control is needed to modify that contains a currently
1167 non-existent attribute (but allowable for the object) to run
1169 LDAPControl PermitModify = {
1170 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
1171 {0, NULL},
1172 (char) 1};
1173 LDAPControl *controls[2];
1175 controls[0] = &PermitModify;
1176 controls[1] = NULL;
1178 if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) {
1179 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1182 /* find the end of the list, marked by NULL or -1 */
1183 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1184 /* make sure the end of the list is NULL */
1185 mods[i] = NULL;
1186 ret = ldap_modify_ext_s(ads->ld, utf8_dn,
1187 (LDAPMod **) mods, controls, NULL);
1188 SAFE_FREE(utf8_dn);
1189 return ADS_ERROR(ret);
1193 * Perform an ldap add
1194 * @param ads connection to ads server
1195 * @param new_dn DistinguishedName to add
1196 * @param mods list of attributes and values for DN
1197 * @return status of add
1199 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1201 int ret, i;
1202 char *utf8_dn = NULL;
1204 if (push_utf8_allocate(&utf8_dn, new_dn) == -1) {
1205 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1206 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1209 /* find the end of the list, marked by NULL or -1 */
1210 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1211 /* make sure the end of the list is NULL */
1212 mods[i] = NULL;
1214 ret = ldap_add_s(ads->ld, utf8_dn, (LDAPMod**)mods);
1215 SAFE_FREE(utf8_dn);
1216 return ADS_ERROR(ret);
1220 * Delete a DistinguishedName
1221 * @param ads connection to ads server
1222 * @param new_dn DistinguishedName to delete
1223 * @return status of delete
1225 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1227 int ret;
1228 char *utf8_dn = NULL;
1229 if (push_utf8_allocate(&utf8_dn, del_dn) == -1) {
1230 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1231 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1234 ret = ldap_delete_s(ads->ld, utf8_dn);
1235 SAFE_FREE(utf8_dn);
1236 return ADS_ERROR(ret);
1240 * Build an org unit string
1241 * if org unit is Computers or blank then assume a container, otherwise
1242 * assume a / separated list of organisational units.
1243 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1244 * @param ads connection to ads server
1245 * @param org_unit Organizational unit
1246 * @return org unit string - caller must free
1248 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1250 char *ret = NULL;
1252 if (!org_unit || !*org_unit) {
1254 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1256 /* samba4 might not yet respond to a wellknownobject-query */
1257 return ret ? ret : SMB_STRDUP("cn=Computers");
1260 if (strequal(org_unit, "Computers")) {
1261 return SMB_STRDUP("cn=Computers");
1264 /* jmcd: removed "\\" from the separation chars, because it is
1265 needed as an escape for chars like '#' which are valid in an
1266 OU name */
1267 return ads_build_path(org_unit, "/", "ou=", 1);
1271 * Get a org unit string for a well-known GUID
1272 * @param ads connection to ads server
1273 * @param wknguid Well known GUID
1274 * @return org unit string - caller must free
1276 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1278 ADS_STATUS status;
1279 LDAPMessage *res = NULL;
1280 char *base, *wkn_dn, *ret = NULL, **wkn_dn_exp, **bind_dn_exp;
1281 const char *attrs[] = {"distinguishedName", NULL};
1282 int new_ln, wkn_ln, bind_ln, i;
1284 if (wknguid == NULL) {
1285 return NULL;
1288 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1289 DEBUG(1, ("asprintf failed!\n"));
1290 return NULL;
1293 status = ads_search_dn(ads, &res, base, attrs);
1294 if (!ADS_ERR_OK(status)) {
1295 DEBUG(1,("Failed while searching for: %s\n", base));
1296 goto out;
1299 if (ads_count_replies(ads, res) != 1) {
1300 goto out;
1303 /* substitute the bind-path from the well-known-guid-search result */
1304 wkn_dn = ads_get_dn(ads, res);
1305 if (!wkn_dn) {
1306 goto out;
1309 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1310 if (!wkn_dn_exp) {
1311 goto out;
1314 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1315 if (!bind_dn_exp) {
1316 goto out;
1319 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1321 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1324 new_ln = wkn_ln - bind_ln;
1326 ret = SMB_STRDUP(wkn_dn_exp[0]);
1327 if (!ret) {
1328 goto out;
1331 for (i=1; i < new_ln; i++) {
1332 char *s = NULL;
1334 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1335 SAFE_FREE(ret);
1336 goto out;
1339 SAFE_FREE(ret);
1340 ret = SMB_STRDUP(s);
1341 free(s);
1342 if (!ret) {
1343 goto out;
1347 out:
1348 SAFE_FREE(base);
1349 ads_msgfree(ads, res);
1350 ads_memfree(ads, wkn_dn);
1351 if (wkn_dn_exp) {
1352 ldap_value_free(wkn_dn_exp);
1354 if (bind_dn_exp) {
1355 ldap_value_free(bind_dn_exp);
1358 return ret;
1362 * Adds (appends) an item to an attribute array, rather then
1363 * replacing the whole list
1364 * @param ctx An initialized TALLOC_CTX
1365 * @param mods An initialized ADS_MODLIST
1366 * @param name name of the ldap attribute to append to
1367 * @param vals an array of values to add
1368 * @return status of addition
1371 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1372 const char *name, const char **vals)
1374 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1375 (const void *) vals);
1379 * Determines the computer account's current KVNO via an LDAP lookup
1380 * @param ads An initialized ADS_STRUCT
1381 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1382 * @return the kvno for the computer account, or -1 in case of a failure.
1385 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *machine_name)
1387 LDAPMessage *res = NULL;
1388 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1389 char *filter;
1390 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1391 char *dn_string = NULL;
1392 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1394 DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name));
1395 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1396 return kvno;
1398 ret = ads_search(ads, &res, filter, attrs);
1399 SAFE_FREE(filter);
1400 if (!ADS_ERR_OK(ret) && ads_count_replies(ads, res)) {
1401 DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name));
1402 ads_msgfree(ads, res);
1403 return kvno;
1406 dn_string = ads_get_dn(ads, res);
1407 if (!dn_string) {
1408 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1409 ads_msgfree(ads, res);
1410 return kvno;
1412 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1413 ads_memfree(ads, dn_string);
1415 /* ---------------------------------------------------------
1416 * 0 is returned as a default KVNO from this point on...
1417 * This is done because Windows 2000 does not support key
1418 * version numbers. Chances are that a failure in the next
1419 * step is simply due to Windows 2000 being used for a
1420 * domain controller. */
1421 kvno = 0;
1423 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1424 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1425 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1426 ads_msgfree(ads, res);
1427 return kvno;
1430 /* Success */
1431 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1432 ads_msgfree(ads, res);
1433 return kvno;
1437 * This clears out all registered spn's for a given hostname
1438 * @param ads An initilaized ADS_STRUCT
1439 * @param machine_name the NetBIOS name of the computer.
1440 * @return 0 upon success, non-zero otherwise.
1443 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1445 TALLOC_CTX *ctx;
1446 LDAPMessage *res = NULL;
1447 ADS_MODLIST mods;
1448 const char *servicePrincipalName[1] = {NULL};
1449 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1450 char *dn_string = NULL;
1452 ret = ads_find_machine_acct(ads, &res, machine_name);
1453 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1454 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1455 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1456 ads_msgfree(ads, res);
1457 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1460 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1461 ctx = talloc_init("ads_clear_service_principal_names");
1462 if (!ctx) {
1463 ads_msgfree(ads, res);
1464 return ADS_ERROR(LDAP_NO_MEMORY);
1467 if (!(mods = ads_init_mods(ctx))) {
1468 talloc_destroy(ctx);
1469 ads_msgfree(ads, res);
1470 return ADS_ERROR(LDAP_NO_MEMORY);
1472 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1473 if (!ADS_ERR_OK(ret)) {
1474 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1475 ads_msgfree(ads, res);
1476 talloc_destroy(ctx);
1477 return ret;
1479 dn_string = ads_get_dn(ads, res);
1480 if (!dn_string) {
1481 talloc_destroy(ctx);
1482 ads_msgfree(ads, res);
1483 return ADS_ERROR(LDAP_NO_MEMORY);
1485 ret = ads_gen_mod(ads, dn_string, mods);
1486 ads_memfree(ads,dn_string);
1487 if (!ADS_ERR_OK(ret)) {
1488 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1489 machine_name));
1490 ads_msgfree(ads, res);
1491 talloc_destroy(ctx);
1492 return ret;
1495 ads_msgfree(ads, res);
1496 talloc_destroy(ctx);
1497 return ret;
1501 * This adds a service principal name to an existing computer account
1502 * (found by hostname) in AD.
1503 * @param ads An initialized ADS_STRUCT
1504 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1505 * @param my_fqdn The fully qualified DNS name of the machine
1506 * @param spn A string of the service principal to add, i.e. 'host'
1507 * @return 0 upon sucess, or non-zero if a failure occurs
1510 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1511 const char *my_fqdn, const char *spn)
1513 ADS_STATUS ret;
1514 TALLOC_CTX *ctx;
1515 LDAPMessage *res = NULL;
1516 char *psp1, *psp2;
1517 ADS_MODLIST mods;
1518 char *dn_string = NULL;
1519 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1521 ret = ads_find_machine_acct(ads, &res, machine_name);
1522 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1523 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1524 machine_name));
1525 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1526 spn, machine_name, ads->config.realm));
1527 ads_msgfree(ads, res);
1528 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1531 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1532 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1533 ads_msgfree(ads, res);
1534 return ADS_ERROR(LDAP_NO_MEMORY);
1537 /* add short name spn */
1539 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1540 talloc_destroy(ctx);
1541 ads_msgfree(ads, res);
1542 return ADS_ERROR(LDAP_NO_MEMORY);
1544 strupper_m(psp1);
1545 strlower_m(&psp1[strlen(spn)]);
1546 servicePrincipalName[0] = psp1;
1548 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1549 psp1, machine_name));
1552 /* add fully qualified spn */
1554 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1555 ret = ADS_ERROR(LDAP_NO_MEMORY);
1556 goto out;
1558 strupper_m(psp2);
1559 strlower_m(&psp2[strlen(spn)]);
1560 servicePrincipalName[1] = psp2;
1562 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1563 psp2, machine_name));
1565 if ( (mods = ads_init_mods(ctx)) == NULL ) {
1566 ret = ADS_ERROR(LDAP_NO_MEMORY);
1567 goto out;
1570 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1571 if (!ADS_ERR_OK(ret)) {
1572 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1573 goto out;
1576 if ( (dn_string = ads_get_dn(ads, res)) == NULL ) {
1577 ret = ADS_ERROR(LDAP_NO_MEMORY);
1578 goto out;
1581 ret = ads_gen_mod(ads, dn_string, mods);
1582 ads_memfree(ads,dn_string);
1583 if (!ADS_ERR_OK(ret)) {
1584 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1585 goto out;
1588 out:
1589 TALLOC_FREE( ctx );
1590 ads_msgfree(ads, res);
1591 return ret;
1595 * adds a machine account to the ADS server
1596 * @param ads An intialized ADS_STRUCT
1597 * @param machine_name - the NetBIOS machine name of this account.
1598 * @param account_type A number indicating the type of account to create
1599 * @param org_unit The LDAP path in which to place this account
1600 * @return 0 upon success, or non-zero otherwise
1603 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1604 const char *org_unit)
1606 ADS_STATUS ret;
1607 char *samAccountName, *controlstr;
1608 TALLOC_CTX *ctx;
1609 ADS_MODLIST mods;
1610 char *new_dn;
1611 const char *objectClass[] = {"top", "person", "organizationalPerson",
1612 "user", "computer", NULL};
1613 LDAPMessage *res = NULL;
1614 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
1615 UF_DONT_EXPIRE_PASSWD |\
1616 UF_ACCOUNTDISABLE );
1618 if (!(ctx = talloc_init("ads_add_machine_acct")))
1619 return ADS_ERROR(LDAP_NO_MEMORY);
1621 ret = ADS_ERROR(LDAP_NO_MEMORY);
1623 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_name, org_unit);
1624 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
1626 if ( !new_dn || !samAccountName ) {
1627 goto done;
1630 #ifndef ENCTYPE_ARCFOUR_HMAC
1631 acct_control |= UF_USE_DES_KEY_ONLY;
1632 #endif
1634 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
1635 goto done;
1638 if (!(mods = ads_init_mods(ctx))) {
1639 goto done;
1642 ads_mod_str(ctx, &mods, "cn", machine_name);
1643 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1644 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1645 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1647 ret = ads_gen_add(ads, new_dn, mods);
1649 done:
1650 ads_msgfree(ads, res);
1651 talloc_destroy(ctx);
1653 return ret;
1657 dump a binary result from ldap
1659 static void dump_binary(const char *field, struct berval **values)
1661 int i, j;
1662 for (i=0; values[i]; i++) {
1663 printf("%s: ", field);
1664 for (j=0; j<values[i]->bv_len; j++) {
1665 printf("%02X", (unsigned char)values[i]->bv_val[j]);
1667 printf("\n");
1671 static void dump_guid(const char *field, struct berval **values)
1673 int i;
1674 UUID_FLAT guid;
1675 for (i=0; values[i]; i++) {
1676 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1677 printf("%s: %s\n", field,
1678 smb_uuid_string_static(smb_uuid_unpack_static(guid)));
1683 dump a sid result from ldap
1685 static void dump_sid(const char *field, struct berval **values)
1687 int i;
1688 for (i=0; values[i]; i++) {
1689 DOM_SID sid;
1690 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1691 printf("%s: %s\n", field, sid_string_static(&sid));
1696 dump ntSecurityDescriptor
1698 static void dump_sd(const char *filed, struct berval **values)
1700 prs_struct ps;
1702 SEC_DESC *psd = 0;
1703 TALLOC_CTX *ctx = 0;
1705 if (!(ctx = talloc_init("sec_io_desc")))
1706 return;
1708 /* prepare data */
1709 prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
1710 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
1711 prs_set_offset(&ps,0);
1713 /* parse secdesc */
1714 if (!sec_io_desc("sd", &psd, &ps, 1)) {
1715 prs_mem_free(&ps);
1716 talloc_destroy(ctx);
1717 return;
1719 if (psd) ads_disp_sd(psd);
1721 prs_mem_free(&ps);
1722 talloc_destroy(ctx);
1726 dump a string result from ldap
1728 static void dump_string(const char *field, char **values)
1730 int i;
1731 for (i=0; values[i]; i++) {
1732 printf("%s: %s\n", field, values[i]);
1737 dump a field from LDAP on stdout
1738 used for debugging
1741 static BOOL ads_dump_field(char *field, void **values, void *data_area)
1743 const struct {
1744 const char *name;
1745 BOOL string;
1746 void (*handler)(const char *, struct berval **);
1747 } handlers[] = {
1748 {"objectGUID", False, dump_guid},
1749 {"netbootGUID", False, dump_guid},
1750 {"nTSecurityDescriptor", False, dump_sd},
1751 {"dnsRecord", False, dump_binary},
1752 {"objectSid", False, dump_sid},
1753 {"tokenGroups", False, dump_sid},
1754 {"tokenGroupsNoGCAcceptable", False, dump_sid},
1755 {"tokengroupsGlobalandUniversal", False, dump_sid},
1756 {"mS-DS-CreatorSID", False, dump_sid},
1757 {NULL, True, NULL}
1759 int i;
1761 if (!field) { /* must be end of an entry */
1762 printf("\n");
1763 return False;
1766 for (i=0; handlers[i].name; i++) {
1767 if (StrCaseCmp(handlers[i].name, field) == 0) {
1768 if (!values) /* first time, indicate string or not */
1769 return handlers[i].string;
1770 handlers[i].handler(field, (struct berval **) values);
1771 break;
1774 if (!handlers[i].name) {
1775 if (!values) /* first time, indicate string conversion */
1776 return True;
1777 dump_string(field, (char **)values);
1779 return False;
1783 * Dump a result from LDAP on stdout
1784 * used for debugging
1785 * @param ads connection to ads server
1786 * @param res Results to dump
1789 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
1791 ads_process_results(ads, res, ads_dump_field, NULL);
1795 * Walk through results, calling a function for each entry found.
1796 * The function receives a field name, a berval * array of values,
1797 * and a data area passed through from the start. The function is
1798 * called once with null for field and values at the end of each
1799 * entry.
1800 * @param ads connection to ads server
1801 * @param res Results to process
1802 * @param fn Function for processing each result
1803 * @param data_area user-defined area to pass to function
1805 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
1806 BOOL(*fn)(char *, void **, void *),
1807 void *data_area)
1809 LDAPMessage *msg;
1810 TALLOC_CTX *ctx;
1812 if (!(ctx = talloc_init("ads_process_results")))
1813 return;
1815 for (msg = ads_first_entry(ads, res); msg;
1816 msg = ads_next_entry(ads, msg)) {
1817 char *utf8_field;
1818 BerElement *b;
1820 for (utf8_field=ldap_first_attribute(ads->ld,
1821 (LDAPMessage *)msg,&b);
1822 utf8_field;
1823 utf8_field=ldap_next_attribute(ads->ld,
1824 (LDAPMessage *)msg,b)) {
1825 struct berval **ber_vals;
1826 char **str_vals, **utf8_vals;
1827 char *field;
1828 BOOL string;
1830 pull_utf8_talloc(ctx, &field, utf8_field);
1831 string = fn(field, NULL, data_area);
1833 if (string) {
1834 utf8_vals = ldap_get_values(ads->ld,
1835 (LDAPMessage *)msg, field);
1836 str_vals = ads_pull_strvals(ctx,
1837 (const char **) utf8_vals);
1838 fn(field, (void **) str_vals, data_area);
1839 ldap_value_free(utf8_vals);
1840 } else {
1841 ber_vals = ldap_get_values_len(ads->ld,
1842 (LDAPMessage *)msg, field);
1843 fn(field, (void **) ber_vals, data_area);
1845 ldap_value_free_len(ber_vals);
1847 ldap_memfree(utf8_field);
1849 ber_free(b, 0);
1850 talloc_free_children(ctx);
1851 fn(NULL, NULL, data_area); /* completed an entry */
1854 talloc_destroy(ctx);
1858 * count how many replies are in a LDAPMessage
1859 * @param ads connection to ads server
1860 * @param res Results to count
1861 * @return number of replies
1863 int ads_count_replies(ADS_STRUCT *ads, void *res)
1865 return ldap_count_entries(ads->ld, (LDAPMessage *)res);
1869 * pull the first entry from a ADS result
1870 * @param ads connection to ads server
1871 * @param res Results of search
1872 * @return first entry from result
1874 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
1876 return ldap_first_entry(ads->ld, res);
1880 * pull the next entry from a ADS result
1881 * @param ads connection to ads server
1882 * @param res Results of search
1883 * @return next entry from result
1885 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
1887 return ldap_next_entry(ads->ld, res);
1891 * pull a single string from a ADS result
1892 * @param ads connection to ads server
1893 * @param mem_ctx TALLOC_CTX to use for allocating result string
1894 * @param msg Results of search
1895 * @param field Attribute to retrieve
1896 * @return Result string in talloc context
1898 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
1899 const char *field)
1901 char **values;
1902 char *ret = NULL;
1903 char *ux_string;
1904 size_t rc;
1906 values = ldap_get_values(ads->ld, msg, field);
1907 if (!values)
1908 return NULL;
1910 if (values[0]) {
1911 rc = pull_utf8_talloc(mem_ctx, &ux_string,
1912 values[0]);
1913 if (rc != (size_t)-1)
1914 ret = ux_string;
1917 ldap_value_free(values);
1918 return ret;
1922 * pull an array of strings from a ADS result
1923 * @param ads connection to ads server
1924 * @param mem_ctx TALLOC_CTX to use for allocating result string
1925 * @param msg Results of search
1926 * @param field Attribute to retrieve
1927 * @return Result strings in talloc context
1929 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
1930 LDAPMessage *msg, const char *field,
1931 size_t *num_values)
1933 char **values;
1934 char **ret = NULL;
1935 int i;
1937 values = ldap_get_values(ads->ld, msg, field);
1938 if (!values)
1939 return NULL;
1941 *num_values = ldap_count_values(values);
1943 ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
1944 if (!ret) {
1945 ldap_value_free(values);
1946 return NULL;
1949 for (i=0;i<*num_values;i++) {
1950 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
1951 ldap_value_free(values);
1952 return NULL;
1955 ret[i] = NULL;
1957 ldap_value_free(values);
1958 return ret;
1962 * pull an array of strings from a ADS result
1963 * (handle large multivalue attributes with range retrieval)
1964 * @param ads connection to ads server
1965 * @param mem_ctx TALLOC_CTX to use for allocating result string
1966 * @param msg Results of search
1967 * @param field Attribute to retrieve
1968 * @param current_strings strings returned by a previous call to this function
1969 * @param next_attribute The next query should ask for this attribute
1970 * @param num_values How many values did we get this time?
1971 * @param more_values Are there more values to get?
1972 * @return Result strings in talloc context
1974 char **ads_pull_strings_range(ADS_STRUCT *ads,
1975 TALLOC_CTX *mem_ctx,
1976 LDAPMessage *msg, const char *field,
1977 char **current_strings,
1978 const char **next_attribute,
1979 size_t *num_strings,
1980 BOOL *more_strings)
1982 char *attr;
1983 char *expected_range_attrib, *range_attr;
1984 BerElement *ptr = NULL;
1985 char **strings;
1986 char **new_strings;
1987 size_t num_new_strings;
1988 unsigned long int range_start;
1989 unsigned long int range_end;
1991 /* we might have been given the whole lot anyway */
1992 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
1993 *more_strings = False;
1994 return strings;
1997 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
1999 /* look for Range result */
2000 for (attr = ldap_first_attribute(ads->ld, (LDAPMessage *)msg, &ptr);
2001 attr;
2002 attr = ldap_next_attribute(ads->ld, (LDAPMessage *)msg, ptr)) {
2003 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2004 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2005 range_attr = attr;
2006 break;
2008 ldap_memfree(attr);
2010 if (!attr) {
2011 ber_free(ptr, 0);
2012 /* nothing here - this field is just empty */
2013 *more_strings = False;
2014 return NULL;
2017 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2018 &range_start, &range_end) == 2) {
2019 *more_strings = True;
2020 } else {
2021 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2022 &range_start) == 1) {
2023 *more_strings = False;
2024 } else {
2025 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2026 range_attr));
2027 ldap_memfree(range_attr);
2028 *more_strings = False;
2029 return NULL;
2033 if ((*num_strings) != range_start) {
2034 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2035 " - aborting range retreival\n",
2036 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2037 ldap_memfree(range_attr);
2038 *more_strings = False;
2039 return NULL;
2042 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2044 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2045 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2046 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2047 range_attr, (unsigned long int)range_end - range_start + 1,
2048 (unsigned long int)num_new_strings));
2049 ldap_memfree(range_attr);
2050 *more_strings = False;
2051 return NULL;
2054 strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2055 *num_strings + num_new_strings);
2057 if (strings == NULL) {
2058 ldap_memfree(range_attr);
2059 *more_strings = False;
2060 return NULL;
2063 if (new_strings && num_new_strings) {
2064 memcpy(&strings[*num_strings], new_strings,
2065 sizeof(*new_strings) * num_new_strings);
2068 (*num_strings) += num_new_strings;
2070 if (*more_strings) {
2071 *next_attribute = talloc_asprintf(mem_ctx,
2072 "%s;range=%d-*",
2073 field,
2074 (int)*num_strings);
2076 if (!*next_attribute) {
2077 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2078 ldap_memfree(range_attr);
2079 *more_strings = False;
2080 return NULL;
2084 ldap_memfree(range_attr);
2086 return strings;
2090 * pull a single uint32 from a ADS result
2091 * @param ads connection to ads server
2092 * @param msg Results of search
2093 * @param field Attribute to retrieve
2094 * @param v Pointer to int to store result
2095 * @return boolean inidicating success
2097 BOOL ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2098 uint32 *v)
2100 char **values;
2102 values = ldap_get_values(ads->ld, msg, field);
2103 if (!values)
2104 return False;
2105 if (!values[0]) {
2106 ldap_value_free(values);
2107 return False;
2110 *v = atoi(values[0]);
2111 ldap_value_free(values);
2112 return True;
2116 * pull a single objectGUID from an ADS result
2117 * @param ads connection to ADS server
2118 * @param msg results of search
2119 * @param guid 37-byte area to receive text guid
2120 * @return boolean indicating success
2122 BOOL ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2124 char **values;
2125 UUID_FLAT flat_guid;
2127 values = ldap_get_values(ads->ld, msg, "objectGUID");
2128 if (!values)
2129 return False;
2131 if (values[0]) {
2132 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2133 smb_uuid_unpack(flat_guid, guid);
2134 ldap_value_free(values);
2135 return True;
2137 ldap_value_free(values);
2138 return False;
2144 * pull a single DOM_SID from a ADS result
2145 * @param ads connection to ads server
2146 * @param msg Results of search
2147 * @param field Attribute to retrieve
2148 * @param sid Pointer to sid to store result
2149 * @return boolean inidicating success
2151 BOOL ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2152 DOM_SID *sid)
2154 struct berval **values;
2155 BOOL ret = False;
2157 values = ldap_get_values_len(ads->ld, msg, field);
2159 if (!values)
2160 return False;
2162 if (values[0])
2163 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2165 ldap_value_free_len(values);
2166 return ret;
2170 * pull an array of DOM_SIDs from a ADS result
2171 * @param ads connection to ads server
2172 * @param mem_ctx TALLOC_CTX for allocating sid array
2173 * @param msg Results of search
2174 * @param field Attribute to retrieve
2175 * @param sids pointer to sid array to allocate
2176 * @return the count of SIDs pulled
2178 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2179 LDAPMessage *msg, const char *field, DOM_SID **sids)
2181 struct berval **values;
2182 BOOL ret;
2183 int count, i;
2185 values = ldap_get_values_len(ads->ld, msg, field);
2187 if (!values)
2188 return 0;
2190 for (i=0; values[i]; i++)
2191 /* nop */ ;
2193 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2194 if (!(*sids)) {
2195 ldap_value_free_len(values);
2196 return 0;
2199 count = 0;
2200 for (i=0; values[i]; i++) {
2201 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2202 if (ret) {
2203 fstring sid;
2204 DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid, &(*sids)[count])));
2205 count++;
2209 ldap_value_free_len(values);
2210 return count;
2214 * pull a SEC_DESC from a ADS result
2215 * @param ads connection to ads server
2216 * @param mem_ctx TALLOC_CTX for allocating sid array
2217 * @param msg Results of search
2218 * @param field Attribute to retrieve
2219 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2220 * @return boolean inidicating success
2222 BOOL ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2223 LDAPMessage *msg, const char *field, SEC_DESC **sd)
2225 struct berval **values;
2226 prs_struct ps;
2227 BOOL ret = False;
2229 values = ldap_get_values_len(ads->ld, msg, field);
2231 if (!values) return False;
2233 if (values[0]) {
2234 prs_init(&ps, values[0]->bv_len, mem_ctx, UNMARSHALL);
2235 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
2236 prs_set_offset(&ps,0);
2238 ret = sec_io_desc("sd", sd, &ps, 1);
2241 ldap_value_free_len(values);
2242 return ret;
2246 * in order to support usernames longer than 21 characters we need to
2247 * use both the sAMAccountName and the userPrincipalName attributes
2248 * It seems that not all users have the userPrincipalName attribute set
2250 * @param ads connection to ads server
2251 * @param mem_ctx TALLOC_CTX for allocating sid array
2252 * @param msg Results of search
2253 * @return the username
2255 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2256 LDAPMessage *msg)
2258 #if 0 /* JERRY */
2259 char *ret, *p;
2261 /* lookup_name() only works on the sAMAccountName to
2262 returning the username portion of userPrincipalName
2263 breaks winbindd_getpwnam() */
2265 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2266 if (ret && (p = strchr_m(ret, '@'))) {
2267 *p = 0;
2268 return ret;
2270 #endif
2271 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2276 * find the update serial number - this is the core of the ldap cache
2277 * @param ads connection to ads server
2278 * @param ads connection to ADS server
2279 * @param usn Pointer to retrieved update serial number
2280 * @return status of search
2282 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2284 const char *attrs[] = {"highestCommittedUSN", NULL};
2285 ADS_STATUS status;
2286 LDAPMessage *res;
2288 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2289 if (!ADS_ERR_OK(status))
2290 return status;
2292 if (ads_count_replies(ads, res) != 1) {
2293 ads_msgfree(ads, res);
2294 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2297 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2298 ads_msgfree(ads, res);
2299 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2302 ads_msgfree(ads, res);
2303 return ADS_SUCCESS;
2306 /* parse a ADS timestring - typical string is
2307 '20020917091222.0Z0' which means 09:12.22 17th September
2308 2002, timezone 0 */
2309 static time_t ads_parse_time(const char *str)
2311 struct tm tm;
2313 ZERO_STRUCT(tm);
2315 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2316 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2317 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2318 return 0;
2320 tm.tm_year -= 1900;
2321 tm.tm_mon -= 1;
2323 return timegm(&tm);
2326 /********************************************************************
2327 ********************************************************************/
2329 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2331 const char *attrs[] = {"currentTime", NULL};
2332 ADS_STATUS status;
2333 LDAPMessage *res;
2334 char *timestr;
2335 TALLOC_CTX *ctx;
2336 ADS_STRUCT *ads_s = ads;
2338 if (!(ctx = talloc_init("ads_current_time"))) {
2339 return ADS_ERROR(LDAP_NO_MEMORY);
2342 /* establish a new ldap tcp session if necessary */
2344 if ( !ads->ld ) {
2345 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2346 ads->server.ldap_server )) == NULL )
2348 goto done;
2350 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2351 status = ads_connect( ads_s );
2352 if ( !ADS_ERR_OK(status))
2353 goto done;
2356 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2357 if (!ADS_ERR_OK(status)) {
2358 goto done;
2361 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2362 if (!timestr) {
2363 ads_msgfree(ads_s, res);
2364 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2365 goto done;
2368 /* but save the time and offset in the original ADS_STRUCT */
2370 ads->config.current_time = ads_parse_time(timestr);
2372 if (ads->config.current_time != 0) {
2373 ads->auth.time_offset = ads->config.current_time - time(NULL);
2374 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2377 ads_msgfree(ads, res);
2379 status = ADS_SUCCESS;
2381 done:
2382 /* free any temporary ads connections */
2383 if ( ads_s != ads ) {
2384 ads_destroy( &ads_s );
2386 talloc_destroy(ctx);
2388 return status;
2391 /********************************************************************
2392 ********************************************************************/
2394 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2396 const char *attrs[] = {"domainFunctionality", NULL};
2397 ADS_STATUS status;
2398 LDAPMessage *res;
2399 ADS_STRUCT *ads_s = ads;
2401 *val = DS_DOMAIN_FUNCTION_2000;
2403 /* establish a new ldap tcp session if necessary */
2405 if ( !ads->ld ) {
2406 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2407 ads->server.ldap_server )) == NULL )
2409 goto done;
2411 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2412 status = ads_connect( ads_s );
2413 if ( !ADS_ERR_OK(status))
2414 goto done;
2417 /* If the attribute does not exist assume it is a Windows 2000
2418 functional domain */
2420 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2421 if (!ADS_ERR_OK(status)) {
2422 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2423 status = ADS_SUCCESS;
2425 goto done;
2428 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2429 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2431 DEBUG(3,("ads_domain_func_level: %d\n", *val));
2434 ads_msgfree(ads, res);
2436 done:
2437 /* free any temporary ads connections */
2438 if ( ads_s != ads ) {
2439 ads_destroy( &ads_s );
2442 return status;
2446 * find the domain sid for our domain
2447 * @param ads connection to ads server
2448 * @param sid Pointer to domain sid
2449 * @return status of search
2451 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2453 const char *attrs[] = {"objectSid", NULL};
2454 LDAPMessage *res;
2455 ADS_STATUS rc;
2457 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2458 attrs, &res);
2459 if (!ADS_ERR_OK(rc)) return rc;
2460 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2461 ads_msgfree(ads, res);
2462 return ADS_ERROR_SYSTEM(ENOENT);
2464 ads_msgfree(ads, res);
2466 return ADS_SUCCESS;
2470 * find our site name
2471 * @param ads connection to ads server
2472 * @param mem_ctx Pointer to talloc context
2473 * @param site_name Pointer to the sitename
2474 * @return status of search
2476 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2478 ADS_STATUS status;
2479 LDAPMessage *res;
2480 const char *dn, *service_name;
2481 const char *attrs[] = { "dsServiceName", NULL };
2483 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2484 if (!ADS_ERR_OK(status)) {
2485 return status;
2488 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2489 if (service_name == NULL) {
2490 ads_msgfree(ads, res);
2491 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2494 ads_msgfree(ads, res);
2496 /* go up three levels */
2497 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2498 if (dn == NULL) {
2499 return ADS_ERROR(LDAP_NO_MEMORY);
2502 *site_name = talloc_strdup(mem_ctx, dn);
2503 if (*site_name == NULL) {
2504 return ADS_ERROR(LDAP_NO_MEMORY);
2507 return status;
2509 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2514 * find the site dn where a machine resides
2515 * @param ads connection to ads server
2516 * @param mem_ctx Pointer to talloc context
2517 * @param computer_name name of the machine
2518 * @param site_name Pointer to the sitename
2519 * @return status of search
2521 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
2523 ADS_STATUS status;
2524 LDAPMessage *res;
2525 const char *parent, *config_context, *filter;
2526 const char *attrs[] = { "configurationNamingContext", NULL };
2527 char *dn;
2529 /* shortcut a query */
2530 if (strequal(computer_name, ads->config.ldap_server_name)) {
2531 return ads_site_dn(ads, mem_ctx, site_dn);
2534 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2535 if (!ADS_ERR_OK(status)) {
2536 return status;
2539 config_context = ads_pull_string(ads, mem_ctx, res, "configurationNamingContext");
2540 if (config_context == NULL) {
2541 ads_msgfree(ads, res);
2542 return ADS_ERROR(LDAP_NO_MEMORY);
2545 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
2546 if (filter == NULL) {
2547 ads_msgfree(ads, res);
2548 return ADS_ERROR(LDAP_NO_MEMORY);
2551 ads_msgfree(ads, res);
2553 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE, filter, NULL, &res);
2554 if (!ADS_ERR_OK(status)) {
2555 return status;
2558 if (ads_count_replies(ads, res) != 1) {
2559 ads_msgfree(ads, res);
2560 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2563 dn = ads_get_dn(ads, res);
2564 if (dn == NULL) {
2565 ads_msgfree(ads, res);
2566 return ADS_ERROR(LDAP_NO_MEMORY);
2569 /* go up three levels */
2570 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
2571 if (parent == NULL) {
2572 ads_msgfree(ads, res);
2573 ads_memfree(ads, dn);
2574 return ADS_ERROR(LDAP_NO_MEMORY);
2577 *site_dn = talloc_strdup(mem_ctx, parent);
2578 if (*site_dn == NULL) {
2579 ads_msgfree(ads, res);
2580 ads_memfree(ads, dn);
2581 ADS_ERROR(LDAP_NO_MEMORY);
2584 ads_memfree(ads, dn);
2585 ads_msgfree(ads, res);
2587 return status;
2591 * get the upn suffixes for a domain
2592 * @param ads connection to ads server
2593 * @param mem_ctx Pointer to talloc context
2594 * @param suffixes Pointer to an array of suffixes
2595 * @param site_name Pointer to the number of suffixes
2596 * @return status of search
2598 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char **suffixes, size_t *num_suffixes)
2600 ADS_STATUS status;
2601 LDAPMessage *res;
2602 const char *config_context, *base;
2603 const char *attrs[] = { "configurationNamingContext", NULL };
2604 const char *attrs2[] = { "uPNSuffixes", NULL };
2606 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2607 if (!ADS_ERR_OK(status)) {
2608 return status;
2611 config_context = ads_pull_string(ads, mem_ctx, res, "configurationNamingContext");
2612 if (config_context == NULL) {
2613 return ADS_ERROR(LDAP_NO_MEMORY);
2616 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
2617 if (base == NULL) {
2618 return ADS_ERROR(LDAP_NO_MEMORY);
2621 status = ads_search_dn(ads, &res, base, attrs2);
2622 if (!ADS_ERR_OK(status)) {
2623 return status;
2626 if (ads_count_replies(ads, res) != 1) {
2627 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2630 suffixes = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
2631 if (suffixes == NULL) {
2632 ads_msgfree(ads, res);
2633 return ADS_ERROR(LDAP_NO_MEMORY);
2636 ads_msgfree(ads, res);
2638 return status;
2642 * pull a DOM_SID from an extended dn string
2643 * @param mem_ctx TALLOC_CTX
2644 * @param flags string type of extended_dn
2645 * @param sid pointer to a DOM_SID
2646 * @return boolean inidicating success
2648 BOOL ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
2649 const char *dn,
2650 enum ads_extended_dn_flags flags,
2651 DOM_SID *sid)
2653 char *p, *q;
2655 if (!dn) {
2656 return False;
2660 * ADS_EXTENDED_DN_HEX_STRING:
2661 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
2663 * ADS_EXTENDED_DN_STRING (only with w2k3):
2664 <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
2667 p = strchr(dn, ';');
2668 if (!p) {
2669 return False;
2672 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
2673 return False;
2676 p += strlen(";<SID=");
2678 q = strchr(p, '>');
2679 if (!q) {
2680 return False;
2683 *q = '\0';
2685 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
2687 switch (flags) {
2689 case ADS_EXTENDED_DN_STRING:
2690 if (!string_to_sid(sid, p)) {
2691 return False;
2693 break;
2694 case ADS_EXTENDED_DN_HEX_STRING: {
2695 pstring buf;
2696 size_t buf_len;
2698 buf_len = strhex_to_str(buf, strlen(p), p);
2699 if (buf_len == 0) {
2700 return False;
2703 if (!sid_parse(buf, buf_len, sid)) {
2704 DEBUG(10,("failed to parse sid\n"));
2705 return False;
2707 break;
2709 default:
2710 DEBUG(10,("unknown extended dn format\n"));
2711 return False;
2714 return True;
2718 * pull an array of DOM_SIDs from a ADS result
2719 * @param ads connection to ads server
2720 * @param mem_ctx TALLOC_CTX for allocating sid array
2721 * @param msg Results of search
2722 * @param field Attribute to retrieve
2723 * @param flags string type of extended_dn
2724 * @param sids pointer to sid array to allocate
2725 * @return the count of SIDs pulled
2727 int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads,
2728 TALLOC_CTX *mem_ctx,
2729 LDAPMessage *msg,
2730 const char *field,
2731 enum ads_extended_dn_flags flags,
2732 DOM_SID **sids)
2734 int i;
2735 size_t dn_count;
2736 char **dn_strings;
2738 if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field,
2739 &dn_count)) == NULL) {
2740 return 0;
2743 (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, dn_count + 1);
2744 if (!(*sids)) {
2745 TALLOC_FREE(dn_strings);
2746 return 0;
2749 for (i=0; i<dn_count; i++) {
2751 if (!ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i],
2752 flags, &(*sids)[i])) {
2753 TALLOC_FREE(*sids);
2754 TALLOC_FREE(dn_strings);
2755 return 0;
2759 TALLOC_FREE(dn_strings);
2761 return dn_count;
2764 /********************************************************************
2765 ********************************************************************/
2767 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2769 LDAPMessage *res = NULL;
2770 ADS_STATUS status;
2771 int count = 0;
2772 char *name = NULL;
2774 status = ads_find_machine_acct(ads, &res, global_myname());
2775 if (!ADS_ERR_OK(status)) {
2776 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
2777 global_myname()));
2778 goto out;
2781 if ( (count = ads_count_replies(ads, res)) != 1 ) {
2782 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
2783 goto out;
2786 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
2787 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
2790 out:
2791 ads_msgfree(ads, res);
2793 return name;
2796 /********************************************************************
2797 ********************************************************************/
2799 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2801 LDAPMessage *res = NULL;
2802 ADS_STATUS status;
2803 int count = 0;
2804 char *name = NULL;
2806 status = ads_find_machine_acct(ads, &res, global_myname());
2807 if (!ADS_ERR_OK(status)) {
2808 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
2809 global_myname()));
2810 goto out;
2813 if ( (count = ads_count_replies(ads, res)) != 1 ) {
2814 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
2815 goto out;
2818 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
2819 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
2822 out:
2823 ads_msgfree(ads, res);
2825 return name;
2828 /********************************************************************
2829 ********************************************************************/
2831 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2833 LDAPMessage *res = NULL;
2834 ADS_STATUS status;
2835 int count = 0;
2836 char *name = NULL;
2838 status = ads_find_machine_acct(ads, &res, global_myname());
2839 if (!ADS_ERR_OK(status)) {
2840 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
2841 global_myname()));
2842 goto out;
2845 if ( (count = ads_count_replies(ads, res)) != 1 ) {
2846 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
2847 goto out;
2850 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
2851 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
2854 out:
2855 ads_msgfree(ads, res);
2857 return name;
2860 #if 0
2862 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
2865 * Join a machine to a realm
2866 * Creates the machine account and sets the machine password
2867 * @param ads connection to ads server
2868 * @param machine name of host to add
2869 * @param org_unit Organizational unit to place machine in
2870 * @return status of join
2872 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
2873 uint32 account_type, const char *org_unit)
2875 ADS_STATUS status;
2876 LDAPMessage *res = NULL;
2877 char *machine;
2879 /* machine name must be lowercase */
2880 machine = SMB_STRDUP(machine_name);
2881 strlower_m(machine);
2884 status = ads_find_machine_acct(ads, (void **)&res, machine);
2885 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
2886 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
2887 status = ads_leave_realm(ads, machine);
2888 if (!ADS_ERR_OK(status)) {
2889 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
2890 machine, ads->config.realm));
2891 return status;
2895 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
2896 if (!ADS_ERR_OK(status)) {
2897 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
2898 SAFE_FREE(machine);
2899 return status;
2902 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
2903 if (!ADS_ERR_OK(status)) {
2904 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
2905 SAFE_FREE(machine);
2906 return status;
2909 SAFE_FREE(machine);
2910 ads_msgfree(ads, res);
2912 return status;
2914 #endif
2916 #ifdef HAVE_LDAP
2919 * Delete a machine from the realm
2920 * @param ads connection to ads server
2921 * @param hostname Machine to remove
2922 * @return status of delete
2924 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
2926 ADS_STATUS status;
2927 void *msg;
2928 LDAPMessage *res;
2929 char *hostnameDN, *host;
2930 int rc;
2931 LDAPControl ldap_control;
2932 LDAPControl * pldap_control[2] = {NULL, NULL};
2934 pldap_control[0] = &ldap_control;
2935 memset(&ldap_control, 0, sizeof(LDAPControl));
2936 ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
2938 /* hostname must be lowercase */
2939 host = SMB_STRDUP(hostname);
2940 strlower_m(host);
2942 status = ads_find_machine_acct(ads, &res, host);
2943 if (!ADS_ERR_OK(status)) {
2944 DEBUG(0, ("Host account for %s does not exist.\n", host));
2945 SAFE_FREE(host);
2946 return status;
2949 msg = ads_first_entry(ads, res);
2950 if (!msg) {
2951 SAFE_FREE(host);
2952 return ADS_ERROR_SYSTEM(ENOENT);
2955 hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
2957 rc = ldap_delete_ext_s(ads->ld, hostnameDN, pldap_control, NULL);
2958 if (rc) {
2959 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
2960 }else {
2961 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
2964 if (rc != LDAP_SUCCESS) {
2965 const char *attrs[] = { "cn", NULL };
2966 LDAPMessage *msg_sub;
2968 /* we only search with scope ONE, we do not expect any further
2969 * objects to be created deeper */
2971 status = ads_do_search_retry(ads, hostnameDN,
2972 LDAP_SCOPE_ONELEVEL,
2973 "(objectclass=*)", attrs, &res);
2975 if (!ADS_ERR_OK(status)) {
2976 SAFE_FREE(host);
2977 ads_memfree(ads, hostnameDN);
2978 return status;
2981 for (msg_sub = ads_first_entry(ads, res); msg_sub;
2982 msg_sub = ads_next_entry(ads, msg_sub)) {
2984 char *dn = NULL;
2986 if ((dn = ads_get_dn(ads, msg_sub)) == NULL) {
2987 SAFE_FREE(host);
2988 ads_memfree(ads, hostnameDN);
2989 return ADS_ERROR(LDAP_NO_MEMORY);
2992 status = ads_del_dn(ads, dn);
2993 if (!ADS_ERR_OK(status)) {
2994 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
2995 SAFE_FREE(host);
2996 ads_memfree(ads, dn);
2997 ads_memfree(ads, hostnameDN);
2998 return status;
3001 ads_memfree(ads, dn);
3004 /* there should be no subordinate objects anymore */
3005 status = ads_do_search_retry(ads, hostnameDN,
3006 LDAP_SCOPE_ONELEVEL,
3007 "(objectclass=*)", attrs, &res);
3009 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3010 SAFE_FREE(host);
3011 ads_memfree(ads, hostnameDN);
3012 return status;
3015 /* delete hostnameDN now */
3016 status = ads_del_dn(ads, hostnameDN);
3017 if (!ADS_ERR_OK(status)) {
3018 SAFE_FREE(host);
3019 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3020 ads_memfree(ads, hostnameDN);
3021 return status;
3025 ads_memfree(ads, hostnameDN);
3027 status = ads_find_machine_acct(ads, &res, host);
3028 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3029 DEBUG(3, ("Failed to remove host account.\n"));
3030 SAFE_FREE(host);
3031 return status;
3034 SAFE_FREE(host);
3035 return status;
3037 #endif
3039 #endif