mount.cifs: check access of credential files before opening
[Samba.git] / source / libads / ldap.c
blob74798943f8468740fa9c01d6b5bba4f10f1e17eb
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;
117 * A bug in OpenLDAP means ldap_search_ext_s can return
118 * LDAP_SUCCESS but with a NULL res pointer. Cope with
119 * this. See bug #6279 for details. JRA.
122 if (*res == NULL) {
123 return LDAP_TIMELIMIT_EXCEEDED;
126 return result;
129 /**********************************************
130 Do client and server sitename match ?
131 **********************************************/
133 BOOL ads_sitename_match(ADS_STRUCT *ads)
135 if (ads->config.server_site_name == NULL &&
136 ads->config.client_site_name == NULL ) {
137 DEBUG(10,("ads_sitename_match: both null\n"));
138 return True;
140 if (ads->config.server_site_name &&
141 ads->config.client_site_name &&
142 strequal(ads->config.server_site_name,
143 ads->config.client_site_name)) {
144 DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
145 return True;
147 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
148 ads->config.server_site_name ? ads->config.server_site_name : "NULL",
149 ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
150 return False;
153 /**********************************************
154 Is this the closest DC ?
155 **********************************************/
157 BOOL ads_closest_dc(ADS_STRUCT *ads)
159 if (ads->config.flags & ADS_CLOSEST) {
160 DEBUG(10,("ads_closest_dc: ADS_CLOSEST flag set\n"));
161 return True;
164 /* not sure if this can ever happen */
165 if (ads_sitename_match(ads)) {
166 DEBUG(10,("ads_closest_dc: ADS_CLOSEST flag not set but sites match\n"));
167 return True;
170 if (ads->config.client_site_name == NULL) {
171 DEBUG(10,("ads_closest_dc: client belongs to no site\n"));
172 return True;
175 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
176 ads->config.ldap_server_name));
178 return False;
183 try a connection to a given ldap server, returning True and setting the servers IP
184 in the ads struct if successful
186 BOOL ads_try_connect(ADS_STRUCT *ads, const char *server )
188 char *srv;
189 struct cldap_netlogon_reply cldap_reply;
191 if (!server || !*server) {
192 return False;
195 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
196 server, ads->server.realm));
198 /* this copes with inet_ntoa brokenness */
200 srv = SMB_STRDUP(server);
202 ZERO_STRUCT( cldap_reply );
204 if ( !ads_cldap_netlogon( srv, ads->server.realm, &cldap_reply ) ) {
205 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", srv));
206 SAFE_FREE( srv );
207 return False;
210 /* Check the CLDAP reply flags */
212 if ( !(cldap_reply.flags & ADS_LDAP) ) {
213 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
214 srv));
215 SAFE_FREE( srv );
216 return False;
219 /* Fill in the ads->config values */
221 SAFE_FREE(ads->config.realm);
222 SAFE_FREE(ads->config.bind_path);
223 SAFE_FREE(ads->config.ldap_server_name);
224 SAFE_FREE(ads->config.server_site_name);
225 SAFE_FREE(ads->config.client_site_name);
226 SAFE_FREE(ads->server.workgroup);
228 ads->config.flags = cldap_reply.flags;
229 ads->config.ldap_server_name = SMB_STRDUP(cldap_reply.hostname);
230 strupper_m(cldap_reply.domain);
231 ads->config.realm = SMB_STRDUP(cldap_reply.domain);
232 ads->config.bind_path = ads_build_dn(ads->config.realm);
233 if (*cldap_reply.server_site_name) {
234 ads->config.server_site_name =
235 SMB_STRDUP(cldap_reply.server_site_name);
237 if (*cldap_reply.client_site_name) {
238 ads->config.client_site_name =
239 SMB_STRDUP(cldap_reply.client_site_name);
242 ads->server.workgroup = SMB_STRDUP(cldap_reply.netbios_domain);
244 ads->ldap_port = LDAP_PORT;
245 ads->ldap_ip = *interpret_addr2(srv);
246 SAFE_FREE(srv);
248 /* Store our site name. */
249 sitename_store( cldap_reply.domain, cldap_reply.client_site_name );
251 return True;
254 /**********************************************************************
255 Try to find an AD dc using our internal name resolution routines
256 Try the realm first and then then workgroup name if netbios is not
257 disabled
258 **********************************************************************/
260 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
262 const char *c_domain;
263 const char *c_realm;
264 int count, i=0;
265 struct ip_service *ip_list;
266 pstring domain;
267 pstring realm;
268 BOOL got_realm = False;
269 BOOL use_own_domain = False;
270 char *sitename;
271 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
273 /* if the realm and workgroup are both empty, assume they are ours */
275 /* realm */
276 c_realm = ads->server.realm;
278 if ( !c_realm || !*c_realm ) {
279 /* special case where no realm and no workgroup means our own */
280 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
281 use_own_domain = True;
282 c_realm = lp_realm();
286 if (c_realm && *c_realm)
287 got_realm = True;
289 again:
291 /* we need to try once with the realm name and fallback to the
292 netbios domain name if we fail (if netbios has not been disabled */
294 if ( !got_realm && !lp_disable_netbios() ) {
295 c_realm = ads->server.workgroup;
296 if (!c_realm || !*c_realm) {
297 if ( use_own_domain )
298 c_realm = lp_workgroup();
302 if ( !c_realm || !*c_realm ) {
303 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
304 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
307 if ( use_own_domain ) {
308 c_domain = lp_workgroup();
309 } else {
310 c_domain = ads->server.workgroup;
313 pstrcpy( domain, c_domain );
314 pstrcpy( realm, c_realm );
317 * In case of LDAP we use get_dc_name() as that
318 * creates the custom krb5.conf file
320 if (!(ads->auth.flags & ADS_AUTH_NO_BIND)) {
321 fstring srv_name;
322 struct in_addr ip_out;
324 DEBUG(6,("ads_find_dc: (ldap) looking for %s '%s'\n",
325 (got_realm ? "realm" : "domain"), realm));
327 if (get_dc_name(domain, realm, srv_name, &ip_out)) {
329 * we call ads_try_connect() to fill in the
330 * ads->config details
332 if (ads_try_connect(ads, srv_name)) {
333 return NT_STATUS_OK;
337 return NT_STATUS_NO_LOGON_SERVERS;
340 sitename = sitename_fetch(realm);
342 DEBUG(6,("ads_find_dc: (cldap) looking for %s '%s'\n",
343 (got_realm ? "realm" : "domain"), realm));
345 status = get_sorted_dc_list(realm, sitename, &ip_list, &count, got_realm);
346 if (!NT_STATUS_IS_OK(status)) {
347 /* fall back to netbios if we can */
348 if ( got_realm && !lp_disable_netbios() ) {
349 got_realm = False;
350 goto again;
353 SAFE_FREE(sitename);
354 return status;
357 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
358 for ( i=0; i<count; i++ ) {
359 fstring server;
361 fstrcpy( server, inet_ntoa(ip_list[i].ip) );
363 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
364 continue;
366 if (!got_realm) {
367 /* realm in this case is a workgroup name. We need
368 to ignore any IP addresses in the negative connection
369 cache that match ip addresses returned in the ad realm
370 case. It sucks that I have to reproduce the logic above... */
371 c_realm = ads->server.realm;
372 if ( !c_realm || !*c_realm ) {
373 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
374 c_realm = lp_realm();
377 if (c_realm && *c_realm &&
378 !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm, server))) {
379 /* Ensure we add the workgroup name for this
380 IP address as negative too. */
381 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
382 continue;
386 if ( ads_try_connect(ads, server) ) {
387 SAFE_FREE(ip_list);
388 SAFE_FREE(sitename);
389 return NT_STATUS_OK;
392 /* keep track of failures */
393 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
396 SAFE_FREE(ip_list);
398 /* In case we failed to contact one of our closest DC on our site we
399 * need to try to find another DC, retry with a site-less SRV DNS query
400 * - Guenther */
402 if (sitename) {
403 DEBUG(1,("ads_find_dc: failed to find a valid DC on our site (%s), "
404 "trying to find another DC\n", sitename));
405 SAFE_FREE(sitename);
406 namecache_delete(realm, 0x1C);
407 goto again;
410 return NT_STATUS_NO_LOGON_SERVERS;
415 * Connect to the LDAP server
416 * @param ads Pointer to an existing ADS_STRUCT
417 * @return status of connection
419 ADS_STATUS ads_connect(ADS_STRUCT *ads)
421 int version = LDAP_VERSION3;
422 ADS_STATUS status;
423 NTSTATUS ntstatus;
425 ads->last_attempt = time(NULL);
426 ads->ld = NULL;
428 /* try with a user specified server */
430 if (ads->server.ldap_server &&
431 ads_try_connect(ads, ads->server.ldap_server)) {
432 goto got_connection;
435 ntstatus = ads_find_dc(ads);
436 if (NT_STATUS_IS_OK(ntstatus)) {
437 goto got_connection;
440 return ADS_ERROR_NT(ntstatus);
442 got_connection:
443 DEBUG(3,("Connected to LDAP server %s\n", inet_ntoa(ads->ldap_ip)));
445 if (!ads->auth.user_name) {
446 /* Must use the userPrincipalName value here or sAMAccountName
447 and not servicePrincipalName; found by Guenther Deschner */
449 asprintf(&ads->auth.user_name, "%s$", global_myname() );
452 if (!ads->auth.realm) {
453 ads->auth.realm = SMB_STRDUP(ads->config.realm);
456 if (!ads->auth.kdc_server) {
457 ads->auth.kdc_server = SMB_STRDUP(inet_ntoa(ads->ldap_ip));
460 #if KRB5_DNS_HACK
461 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
462 to MIT kerberos to work (tridge) */
464 char *env;
465 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
466 setenv(env, ads->auth.kdc_server, 1);
467 free(env);
469 #endif
471 /* If the caller() requested no LDAP bind, then we are done */
473 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
474 return ADS_SUCCESS;
477 /* Otherwise setup the TCP LDAP session */
479 if ( (ads->ld = ldap_open_with_timeout(ads->config.ldap_server_name,
480 LDAP_PORT, lp_ldap_timeout())) == NULL )
482 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
485 /* cache the successful connection for workgroup and realm */
486 if (ads_closest_dc(ads)) {
487 saf_store( ads->server.workgroup, ads->config.ldap_server_name);
488 saf_store( ads->server.realm, ads->config.ldap_server_name);
491 ldap_set_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
493 status = ADS_ERROR(smb_ldap_start_tls(ads->ld, version));
494 if (!ADS_ERR_OK(status)) {
495 return status;
498 /* fill in the current time and offsets */
500 status = ads_current_time( ads );
501 if ( !ADS_ERR_OK(status) ) {
502 return status;
505 /* Now do the bind */
507 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
508 return ADS_ERROR(ldap_simple_bind_s( ads->ld, NULL, NULL));
511 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
512 return ADS_ERROR(ldap_simple_bind_s( ads->ld, ads->auth.user_name, ads->auth.password));
515 return ads_sasl_bind(ads);
519 Duplicate a struct berval into talloc'ed memory
521 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
523 struct berval *value;
525 if (!in_val) return NULL;
527 value = TALLOC_ZERO_P(ctx, struct berval);
528 if (value == NULL)
529 return NULL;
530 if (in_val->bv_len == 0) return value;
532 value->bv_len = in_val->bv_len;
533 value->bv_val = (char *)TALLOC_MEMDUP(ctx, in_val->bv_val,
534 in_val->bv_len);
535 return value;
539 Make a values list out of an array of (struct berval *)
541 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
542 const struct berval **in_vals)
544 struct berval **values;
545 int i;
547 if (!in_vals) return NULL;
548 for (i=0; in_vals[i]; i++)
549 ; /* count values */
550 values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
551 if (!values) return NULL;
553 for (i=0; in_vals[i]; i++) {
554 values[i] = dup_berval(ctx, in_vals[i]);
556 return values;
560 UTF8-encode a values list out of an array of (char *)
562 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
564 char **values;
565 int i;
567 if (!in_vals) return NULL;
568 for (i=0; in_vals[i]; i++)
569 ; /* count values */
570 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
571 if (!values) return NULL;
573 for (i=0; in_vals[i]; i++) {
574 push_utf8_talloc(ctx, &values[i], in_vals[i]);
576 return values;
580 Pull a (char *) array out of a UTF8-encoded values list
582 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
584 char **values;
585 int i;
587 if (!in_vals) return NULL;
588 for (i=0; in_vals[i]; i++)
589 ; /* count values */
590 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
591 if (!values) return NULL;
593 for (i=0; in_vals[i]; i++) {
594 pull_utf8_talloc(ctx, &values[i], in_vals[i]);
596 return values;
600 * Do a search with paged results. cookie must be null on the first
601 * call, and then returned on each subsequent call. It will be null
602 * again when the entire search is complete
603 * @param ads connection to ads server
604 * @param bind_path Base dn for the search
605 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
606 * @param expr Search expression - specified in local charset
607 * @param attrs Attributes to retrieve - specified in utf8 or ascii
608 * @param res ** which will contain results - free res* with ads_msgfree()
609 * @param count Number of entries retrieved on this page
610 * @param cookie The paged results cookie to be returned on subsequent calls
611 * @return status of search
613 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
614 const char *bind_path,
615 int scope, const char *expr,
616 const char **attrs, void *args,
617 LDAPMessage **res,
618 int *count, struct berval **cookie)
620 int rc, i, version;
621 char *utf8_expr, *utf8_path, **search_attrs;
622 LDAPControl PagedResults, NoReferrals, ExtendedDn, *controls[4], **rcontrols;
623 BerElement *cookie_be = NULL;
624 struct berval *cookie_bv= NULL;
625 BerElement *extdn_be = NULL;
626 struct berval *extdn_bv= NULL;
628 TALLOC_CTX *ctx;
629 ads_control *external_control = (ads_control *) args;
631 *res = NULL;
633 if (!(ctx = talloc_init("ads_do_paged_search_args")))
634 return ADS_ERROR(LDAP_NO_MEMORY);
636 /* 0 means the conversion worked but the result was empty
637 so we only fail if it's -1. In any case, it always
638 at least nulls out the dest */
639 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
640 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
641 rc = LDAP_NO_MEMORY;
642 goto done;
645 if (!attrs || !(*attrs))
646 search_attrs = NULL;
647 else {
648 /* This would be the utf8-encoded version...*/
649 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
650 if (!(str_list_copy(&search_attrs, attrs))) {
651 rc = LDAP_NO_MEMORY;
652 goto done;
657 /* Paged results only available on ldap v3 or later */
658 ldap_get_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
659 if (version < LDAP_VERSION3) {
660 rc = LDAP_NOT_SUPPORTED;
661 goto done;
664 cookie_be = ber_alloc_t(LBER_USE_DER);
665 if (*cookie) {
666 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
667 ber_bvfree(*cookie); /* don't need it from last time */
668 *cookie = NULL;
669 } else {
670 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
672 ber_flatten(cookie_be, &cookie_bv);
673 PagedResults.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID);
674 PagedResults.ldctl_iscritical = (char) 1;
675 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
676 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
678 NoReferrals.ldctl_oid = CONST_DISCARD(char *, ADS_NO_REFERRALS_OID);
679 NoReferrals.ldctl_iscritical = (char) 0;
680 NoReferrals.ldctl_value.bv_len = 0;
681 NoReferrals.ldctl_value.bv_val = CONST_DISCARD(char *, "");
683 if (external_control && strequal(external_control->control, ADS_EXTENDED_DN_OID)) {
685 ExtendedDn.ldctl_oid = CONST_DISCARD(char *, external_control->control);
686 ExtendedDn.ldctl_iscritical = (char) external_control->critical;
688 /* win2k does not accept a ldctl_value beeing passed in */
690 if (external_control->val != 0) {
692 if ((extdn_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
693 rc = LDAP_NO_MEMORY;
694 goto done;
697 if ((ber_printf(extdn_be, "{i}", (ber_int_t) external_control->val)) == -1) {
698 rc = LDAP_NO_MEMORY;
699 goto done;
701 if ((ber_flatten(extdn_be, &extdn_bv)) == -1) {
702 rc = LDAP_NO_MEMORY;
703 goto done;
706 ExtendedDn.ldctl_value.bv_len = extdn_bv->bv_len;
707 ExtendedDn.ldctl_value.bv_val = extdn_bv->bv_val;
709 } else {
710 ExtendedDn.ldctl_value.bv_len = 0;
711 ExtendedDn.ldctl_value.bv_val = NULL;
714 controls[0] = &NoReferrals;
715 controls[1] = &PagedResults;
716 controls[2] = &ExtendedDn;
717 controls[3] = NULL;
719 } else {
720 controls[0] = &NoReferrals;
721 controls[1] = &PagedResults;
722 controls[2] = NULL;
725 /* we need to disable referrals as the openldap libs don't
726 handle them and paged results at the same time. Using them
727 together results in the result record containing the server
728 page control being removed from the result list (tridge/jmcd)
730 leaving this in despite the control that says don't generate
731 referrals, in case the server doesn't support it (jmcd)
733 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
735 rc = ldap_search_with_timeout(ads->ld, utf8_path, scope, utf8_expr,
736 search_attrs, 0, controls,
737 NULL, LDAP_NO_LIMIT,
738 (LDAPMessage **)res);
740 ber_free(cookie_be, 1);
741 ber_bvfree(cookie_bv);
743 if (rc) {
744 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
745 ldap_err2string(rc)));
746 goto done;
749 rc = ldap_parse_result(ads->ld, *res, NULL, NULL, NULL,
750 NULL, &rcontrols, 0);
752 if (!rcontrols) {
753 goto done;
756 for (i=0; rcontrols[i]; i++) {
757 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
758 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
759 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
760 &cookie_bv);
761 /* the berval is the cookie, but must be freed when
762 it is all done */
763 if (cookie_bv->bv_len) /* still more to do */
764 *cookie=ber_bvdup(cookie_bv);
765 else
766 *cookie=NULL;
767 ber_bvfree(cookie_bv);
768 ber_free(cookie_be, 1);
769 break;
772 ldap_controls_free(rcontrols);
774 done:
775 talloc_destroy(ctx);
777 if (extdn_be) {
778 ber_free(extdn_be, 1);
781 if (extdn_bv) {
782 ber_bvfree(extdn_bv);
785 /* if/when we decide to utf8-encode attrs, take out this next line */
786 str_list_free(&search_attrs);
788 return ADS_ERROR(rc);
791 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
792 int scope, const char *expr,
793 const char **attrs, LDAPMessage **res,
794 int *count, struct berval **cookie)
796 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
801 * Get all results for a search. This uses ads_do_paged_search() to return
802 * all entries in a large search.
803 * @param ads connection to ads server
804 * @param bind_path Base dn for the search
805 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
806 * @param expr Search expression
807 * @param attrs Attributes to retrieve
808 * @param res ** which will contain results - free res* with ads_msgfree()
809 * @return status of search
811 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
812 int scope, const char *expr,
813 const char **attrs, void *args,
814 LDAPMessage **res)
816 struct berval *cookie = NULL;
817 int count = 0;
818 ADS_STATUS status;
820 *res = NULL;
821 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
822 &count, &cookie);
824 if (!ADS_ERR_OK(status))
825 return status;
827 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
828 while (cookie) {
829 LDAPMessage *res2 = NULL;
830 ADS_STATUS status2;
831 LDAPMessage *msg, *next;
833 status2 = ads_do_paged_search_args(ads, bind_path, scope, expr,
834 attrs, args, &res2, &count, &cookie);
836 if (!ADS_ERR_OK(status2)) break;
838 /* this relies on the way that ldap_add_result_entry() works internally. I hope
839 that this works on all ldap libs, but I have only tested with openldap */
840 for (msg = ads_first_message(ads, res2); msg; msg = next) {
841 next = ads_next_message(ads, msg);
842 ldap_add_result_entry((LDAPMessage **)res, msg);
844 /* note that we do not free res2, as the memory is now
845 part of the main returned list */
847 #else
848 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
849 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
850 #endif
852 return status;
855 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
856 int scope, const char *expr,
857 const char **attrs, LDAPMessage **res)
859 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
863 * Run a function on all results for a search. Uses ads_do_paged_search() and
864 * runs the function as each page is returned, using ads_process_results()
865 * @param ads connection to ads server
866 * @param bind_path Base dn for the search
867 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
868 * @param expr Search expression - specified in local charset
869 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
870 * @param fn Function which takes attr name, values list, and data_area
871 * @param data_area Pointer which is passed to function on each call
872 * @return status of search
874 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
875 int scope, const char *expr, const char **attrs,
876 BOOL(*fn)(char *, void **, void *),
877 void *data_area)
879 struct berval *cookie = NULL;
880 int count = 0;
881 ADS_STATUS status;
882 LDAPMessage *res;
884 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
885 &count, &cookie);
887 if (!ADS_ERR_OK(status)) return status;
889 ads_process_results(ads, res, fn, data_area);
890 ads_msgfree(ads, res);
892 while (cookie) {
893 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
894 &res, &count, &cookie);
896 if (!ADS_ERR_OK(status)) break;
898 ads_process_results(ads, res, fn, data_area);
899 ads_msgfree(ads, res);
902 return status;
906 * Do a search with a timeout.
907 * @param ads connection to ads server
908 * @param bind_path Base dn for the search
909 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
910 * @param expr Search expression
911 * @param attrs Attributes to retrieve
912 * @param res ** which will contain results - free res* with ads_msgfree()
913 * @return status of search
915 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
916 const char *expr,
917 const char **attrs, LDAPMessage **res)
919 int rc;
920 char *utf8_expr, *utf8_path, **search_attrs = NULL;
921 TALLOC_CTX *ctx;
923 *res = NULL;
924 if (!(ctx = talloc_init("ads_do_search"))) {
925 DEBUG(1,("ads_do_search: talloc_init() failed!"));
926 return ADS_ERROR(LDAP_NO_MEMORY);
929 /* 0 means the conversion worked but the result was empty
930 so we only fail if it's negative. In any case, it always
931 at least nulls out the dest */
932 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
933 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
934 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
935 rc = LDAP_NO_MEMORY;
936 goto done;
939 if (!attrs || !(*attrs))
940 search_attrs = NULL;
941 else {
942 /* This would be the utf8-encoded version...*/
943 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
944 if (!(str_list_copy(&search_attrs, attrs)))
946 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
947 rc = LDAP_NO_MEMORY;
948 goto done;
952 /* see the note in ads_do_paged_search - we *must* disable referrals */
953 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
955 rc = ldap_search_with_timeout(ads->ld, utf8_path, scope, utf8_expr,
956 search_attrs, 0, NULL, NULL,
957 LDAP_NO_LIMIT,
958 (LDAPMessage **)res);
960 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
961 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
962 rc = 0;
965 done:
966 talloc_destroy(ctx);
967 /* if/when we decide to utf8-encode attrs, take out this next line */
968 str_list_free(&search_attrs);
969 return ADS_ERROR(rc);
972 * Do a general ADS search
973 * @param ads connection to ads server
974 * @param res ** which will contain results - free res* with ads_msgfree()
975 * @param expr Search expression
976 * @param attrs Attributes to retrieve
977 * @return status of search
979 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
980 const char *expr, const char **attrs)
982 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
983 expr, attrs, res);
987 * Do a search on a specific DistinguishedName
988 * @param ads connection to ads server
989 * @param res ** which will contain results - free res* with ads_msgfree()
990 * @param dn DistinguishName to search
991 * @param attrs Attributes to retrieve
992 * @return status of search
994 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
995 const char *dn, const char **attrs)
997 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
998 attrs, res);
1002 * Free up memory from a ads_search
1003 * @param ads connection to ads server
1004 * @param msg Search results to free
1006 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1008 if (!msg) return;
1009 ldap_msgfree(msg);
1013 * Free up memory from various ads requests
1014 * @param ads connection to ads server
1015 * @param mem Area to free
1017 void ads_memfree(ADS_STRUCT *ads, void *mem)
1019 SAFE_FREE(mem);
1023 * Get a dn from search results
1024 * @param ads connection to ads server
1025 * @param msg Search result
1026 * @return dn string
1028 char *ads_get_dn(ADS_STRUCT *ads, LDAPMessage *msg)
1030 char *utf8_dn, *unix_dn;
1032 utf8_dn = ldap_get_dn(ads->ld, msg);
1034 if (!utf8_dn) {
1035 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1036 return NULL;
1039 if (pull_utf8_allocate(&unix_dn, utf8_dn) == (size_t)-1) {
1040 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1041 utf8_dn ));
1042 return NULL;
1044 ldap_memfree(utf8_dn);
1045 return unix_dn;
1049 * Get the parent from a dn
1050 * @param dn the dn to return the parent from
1051 * @return parent dn string
1053 char *ads_parent_dn(const char *dn)
1055 char *p;
1057 if (dn == NULL) {
1058 return NULL;
1061 p = strchr(dn, ',');
1063 if (p == NULL) {
1064 return NULL;
1067 return p+1;
1071 * Find a machine account given a hostname
1072 * @param ads connection to ads server
1073 * @param res ** which will contain results - free res* with ads_msgfree()
1074 * @param host Hostname to search for
1075 * @return status of search
1077 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1078 const char *machine)
1080 ADS_STATUS status;
1081 char *expr;
1082 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1084 *res = NULL;
1086 /* the easiest way to find a machine account anywhere in the tree
1087 is to look for hostname$ */
1088 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1089 DEBUG(1, ("asprintf failed!\n"));
1090 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1093 status = ads_search(ads, res, expr, attrs);
1094 SAFE_FREE(expr);
1095 return status;
1099 * Initialize a list of mods to be used in a modify request
1100 * @param ctx An initialized TALLOC_CTX
1101 * @return allocated ADS_MODLIST
1103 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1105 #define ADS_MODLIST_ALLOC_SIZE 10
1106 LDAPMod **mods;
1108 if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1109 /* -1 is safety to make sure we don't go over the end.
1110 need to reset it to NULL before doing ldap modify */
1111 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1113 return (ADS_MODLIST)mods;
1118 add an attribute to the list, with values list already constructed
1120 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1121 int mod_op, const char *name,
1122 const void *_invals)
1124 const void **invals = (const void **)_invals;
1125 int curmod;
1126 LDAPMod **modlist = (LDAPMod **) *mods;
1127 struct berval **ber_values = NULL;
1128 char **char_values = NULL;
1130 if (!invals) {
1131 mod_op = LDAP_MOD_DELETE;
1132 } else {
1133 if (mod_op & LDAP_MOD_BVALUES)
1134 ber_values = ads_dup_values(ctx,
1135 (const struct berval **)invals);
1136 else
1137 char_values = ads_push_strvals(ctx,
1138 (const char **) invals);
1141 /* find the first empty slot */
1142 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1143 curmod++);
1144 if (modlist[curmod] == (LDAPMod *) -1) {
1145 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
1146 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1147 return ADS_ERROR(LDAP_NO_MEMORY);
1148 memset(&modlist[curmod], 0,
1149 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1150 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1151 *mods = (ADS_MODLIST)modlist;
1154 if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
1155 return ADS_ERROR(LDAP_NO_MEMORY);
1156 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1157 if (mod_op & LDAP_MOD_BVALUES) {
1158 modlist[curmod]->mod_bvalues = ber_values;
1159 } else if (mod_op & LDAP_MOD_DELETE) {
1160 modlist[curmod]->mod_values = NULL;
1161 } else {
1162 modlist[curmod]->mod_values = char_values;
1165 modlist[curmod]->mod_op = mod_op;
1166 return ADS_ERROR(LDAP_SUCCESS);
1170 * Add a single string value to a mod list
1171 * @param ctx An initialized TALLOC_CTX
1172 * @param mods An initialized ADS_MODLIST
1173 * @param name The attribute name to add
1174 * @param val The value to add - NULL means DELETE
1175 * @return ADS STATUS indicating success of add
1177 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1178 const char *name, const char *val)
1180 const char *values[2];
1182 values[0] = val;
1183 values[1] = NULL;
1185 if (!val)
1186 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1187 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1191 * Add an array of string values to a mod list
1192 * @param ctx An initialized TALLOC_CTX
1193 * @param mods An initialized ADS_MODLIST
1194 * @param name The attribute name to add
1195 * @param vals The array of string values to add - NULL means DELETE
1196 * @return ADS STATUS indicating success of add
1198 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1199 const char *name, const char **vals)
1201 if (!vals)
1202 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1203 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1204 name, (const void **) vals);
1207 #if 0
1209 * Add a single ber-encoded value to a mod list
1210 * @param ctx An initialized TALLOC_CTX
1211 * @param mods An initialized ADS_MODLIST
1212 * @param name The attribute name to add
1213 * @param val The value to add - NULL means DELETE
1214 * @return ADS STATUS indicating success of add
1216 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1217 const char *name, const struct berval *val)
1219 const struct berval *values[2];
1221 values[0] = val;
1222 values[1] = NULL;
1223 if (!val)
1224 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1225 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1226 name, (const void **) values);
1228 #endif
1231 * Perform an ldap modify
1232 * @param ads connection to ads server
1233 * @param mod_dn DistinguishedName to modify
1234 * @param mods list of modifications to perform
1235 * @return status of modify
1237 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1239 int ret,i;
1240 char *utf8_dn = NULL;
1242 this control is needed to modify that contains a currently
1243 non-existent attribute (but allowable for the object) to run
1245 LDAPControl PermitModify = {
1246 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
1247 {0, NULL},
1248 (char) 1};
1249 LDAPControl *controls[2];
1251 controls[0] = &PermitModify;
1252 controls[1] = NULL;
1254 if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) {
1255 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1258 /* find the end of the list, marked by NULL or -1 */
1259 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1260 /* make sure the end of the list is NULL */
1261 mods[i] = NULL;
1262 ret = ldap_modify_ext_s(ads->ld, utf8_dn,
1263 (LDAPMod **) mods, controls, NULL);
1264 SAFE_FREE(utf8_dn);
1265 return ADS_ERROR(ret);
1269 * Perform an ldap add
1270 * @param ads connection to ads server
1271 * @param new_dn DistinguishedName to add
1272 * @param mods list of attributes and values for DN
1273 * @return status of add
1275 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1277 int ret, i;
1278 char *utf8_dn = NULL;
1280 if (push_utf8_allocate(&utf8_dn, new_dn) == -1) {
1281 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1282 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1285 /* find the end of the list, marked by NULL or -1 */
1286 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1287 /* make sure the end of the list is NULL */
1288 mods[i] = NULL;
1290 ret = ldap_add_s(ads->ld, utf8_dn, (LDAPMod**)mods);
1291 SAFE_FREE(utf8_dn);
1292 return ADS_ERROR(ret);
1296 * Delete a DistinguishedName
1297 * @param ads connection to ads server
1298 * @param new_dn DistinguishedName to delete
1299 * @return status of delete
1301 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1303 int ret;
1304 char *utf8_dn = NULL;
1305 if (push_utf8_allocate(&utf8_dn, del_dn) == -1) {
1306 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1307 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1310 ret = ldap_delete_s(ads->ld, utf8_dn);
1311 SAFE_FREE(utf8_dn);
1312 return ADS_ERROR(ret);
1316 * Build an org unit string
1317 * if org unit is Computers or blank then assume a container, otherwise
1318 * assume a / separated list of organisational units.
1319 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1320 * @param ads connection to ads server
1321 * @param org_unit Organizational unit
1322 * @return org unit string - caller must free
1324 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1326 char *ret = NULL;
1328 if (!org_unit || !*org_unit) {
1330 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1332 /* samba4 might not yet respond to a wellknownobject-query */
1333 return ret ? ret : SMB_STRDUP("cn=Computers");
1336 if (strequal(org_unit, "Computers")) {
1337 return SMB_STRDUP("cn=Computers");
1340 /* jmcd: removed "\\" from the separation chars, because it is
1341 needed as an escape for chars like '#' which are valid in an
1342 OU name */
1343 return ads_build_path(org_unit, "/", "ou=", 1);
1347 * Get a org unit string for a well-known GUID
1348 * @param ads connection to ads server
1349 * @param wknguid Well known GUID
1350 * @return org unit string - caller must free
1352 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1354 ADS_STATUS status;
1355 LDAPMessage *res = NULL;
1356 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1357 **bind_dn_exp = NULL;
1358 const char *attrs[] = {"distinguishedName", NULL};
1359 int new_ln, wkn_ln, bind_ln, i;
1361 if (wknguid == NULL) {
1362 return NULL;
1365 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1366 DEBUG(1, ("asprintf failed!\n"));
1367 return NULL;
1370 status = ads_search_dn(ads, &res, base, attrs);
1371 if (!ADS_ERR_OK(status)) {
1372 DEBUG(1,("Failed while searching for: %s\n", base));
1373 goto out;
1376 if (ads_count_replies(ads, res) != 1) {
1377 goto out;
1380 /* substitute the bind-path from the well-known-guid-search result */
1381 wkn_dn = ads_get_dn(ads, res);
1382 if (!wkn_dn) {
1383 goto out;
1386 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1387 if (!wkn_dn_exp) {
1388 goto out;
1391 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1392 if (!bind_dn_exp) {
1393 goto out;
1396 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1398 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1401 new_ln = wkn_ln - bind_ln;
1403 ret = SMB_STRDUP(wkn_dn_exp[0]);
1404 if (!ret) {
1405 goto out;
1408 for (i=1; i < new_ln; i++) {
1409 char *s = NULL;
1411 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1412 SAFE_FREE(ret);
1413 goto out;
1416 SAFE_FREE(ret);
1417 ret = SMB_STRDUP(s);
1418 free(s);
1419 if (!ret) {
1420 goto out;
1424 out:
1425 SAFE_FREE(base);
1426 ads_msgfree(ads, res);
1427 ads_memfree(ads, wkn_dn);
1428 if (wkn_dn_exp) {
1429 ldap_value_free(wkn_dn_exp);
1431 if (bind_dn_exp) {
1432 ldap_value_free(bind_dn_exp);
1435 return ret;
1439 * Adds (appends) an item to an attribute array, rather then
1440 * replacing the whole list
1441 * @param ctx An initialized TALLOC_CTX
1442 * @param mods An initialized ADS_MODLIST
1443 * @param name name of the ldap attribute to append to
1444 * @param vals an array of values to add
1445 * @return status of addition
1448 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1449 const char *name, const char **vals)
1451 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1452 (const void *) vals);
1456 * Determines the computer account's current KVNO via an LDAP lookup
1457 * @param ads An initialized ADS_STRUCT
1458 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1459 * @return the kvno for the computer account, or -1 in case of a failure.
1462 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *machine_name)
1464 LDAPMessage *res = NULL;
1465 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1466 char *filter;
1467 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1468 char *dn_string = NULL;
1469 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1471 DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name));
1472 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1473 return kvno;
1475 ret = ads_search(ads, &res, filter, attrs);
1476 SAFE_FREE(filter);
1477 if (!ADS_ERR_OK(ret) && ads_count_replies(ads, res)) {
1478 DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name));
1479 ads_msgfree(ads, res);
1480 return kvno;
1483 dn_string = ads_get_dn(ads, res);
1484 if (!dn_string) {
1485 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1486 ads_msgfree(ads, res);
1487 return kvno;
1489 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1490 ads_memfree(ads, dn_string);
1492 /* ---------------------------------------------------------
1493 * 0 is returned as a default KVNO from this point on...
1494 * This is done because Windows 2000 does not support key
1495 * version numbers. Chances are that a failure in the next
1496 * step is simply due to Windows 2000 being used for a
1497 * domain controller. */
1498 kvno = 0;
1500 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1501 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1502 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1503 ads_msgfree(ads, res);
1504 return kvno;
1507 /* Success */
1508 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1509 ads_msgfree(ads, res);
1510 return kvno;
1514 * This clears out all registered spn's for a given hostname
1515 * @param ads An initilaized ADS_STRUCT
1516 * @param machine_name the NetBIOS name of the computer.
1517 * @return 0 upon success, non-zero otherwise.
1520 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1522 TALLOC_CTX *ctx;
1523 LDAPMessage *res = NULL;
1524 ADS_MODLIST mods;
1525 const char *servicePrincipalName[1] = {NULL};
1526 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1527 char *dn_string = NULL;
1529 ret = ads_find_machine_acct(ads, &res, machine_name);
1530 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1531 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1532 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1533 ads_msgfree(ads, res);
1534 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1537 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1538 ctx = talloc_init("ads_clear_service_principal_names");
1539 if (!ctx) {
1540 ads_msgfree(ads, res);
1541 return ADS_ERROR(LDAP_NO_MEMORY);
1544 if (!(mods = ads_init_mods(ctx))) {
1545 talloc_destroy(ctx);
1546 ads_msgfree(ads, res);
1547 return ADS_ERROR(LDAP_NO_MEMORY);
1549 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1550 if (!ADS_ERR_OK(ret)) {
1551 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1552 ads_msgfree(ads, res);
1553 talloc_destroy(ctx);
1554 return ret;
1556 dn_string = ads_get_dn(ads, res);
1557 if (!dn_string) {
1558 talloc_destroy(ctx);
1559 ads_msgfree(ads, res);
1560 return ADS_ERROR(LDAP_NO_MEMORY);
1562 ret = ads_gen_mod(ads, dn_string, mods);
1563 ads_memfree(ads,dn_string);
1564 if (!ADS_ERR_OK(ret)) {
1565 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1566 machine_name));
1567 ads_msgfree(ads, res);
1568 talloc_destroy(ctx);
1569 return ret;
1572 ads_msgfree(ads, res);
1573 talloc_destroy(ctx);
1574 return ret;
1578 * This adds a service principal name to an existing computer account
1579 * (found by hostname) in AD.
1580 * @param ads An initialized ADS_STRUCT
1581 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1582 * @param my_fqdn The fully qualified DNS name of the machine
1583 * @param spn A string of the service principal to add, i.e. 'host'
1584 * @return 0 upon sucess, or non-zero if a failure occurs
1587 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1588 const char *my_fqdn, const char *spn)
1590 ADS_STATUS ret;
1591 TALLOC_CTX *ctx;
1592 LDAPMessage *res = NULL;
1593 char *psp1, *psp2;
1594 ADS_MODLIST mods;
1595 char *dn_string = NULL;
1596 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1598 ret = ads_find_machine_acct(ads, &res, machine_name);
1599 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1600 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1601 machine_name));
1602 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1603 spn, machine_name, ads->config.realm));
1604 ads_msgfree(ads, res);
1605 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1608 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1609 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1610 ads_msgfree(ads, res);
1611 return ADS_ERROR(LDAP_NO_MEMORY);
1614 /* add short name spn */
1616 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1617 talloc_destroy(ctx);
1618 ads_msgfree(ads, res);
1619 return ADS_ERROR(LDAP_NO_MEMORY);
1621 strupper_m(psp1);
1622 strlower_m(&psp1[strlen(spn)]);
1623 servicePrincipalName[0] = psp1;
1625 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1626 psp1, machine_name));
1629 /* add fully qualified spn */
1631 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1632 ret = ADS_ERROR(LDAP_NO_MEMORY);
1633 goto out;
1635 strupper_m(psp2);
1636 strlower_m(&psp2[strlen(spn)]);
1637 servicePrincipalName[1] = psp2;
1639 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1640 psp2, machine_name));
1642 if ( (mods = ads_init_mods(ctx)) == NULL ) {
1643 ret = ADS_ERROR(LDAP_NO_MEMORY);
1644 goto out;
1647 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1648 if (!ADS_ERR_OK(ret)) {
1649 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1650 goto out;
1653 if ( (dn_string = ads_get_dn(ads, res)) == NULL ) {
1654 ret = ADS_ERROR(LDAP_NO_MEMORY);
1655 goto out;
1658 ret = ads_gen_mod(ads, dn_string, mods);
1659 ads_memfree(ads,dn_string);
1660 if (!ADS_ERR_OK(ret)) {
1661 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1662 goto out;
1665 out:
1666 TALLOC_FREE( ctx );
1667 ads_msgfree(ads, res);
1668 return ret;
1672 * adds a machine account to the ADS server
1673 * @param ads An intialized ADS_STRUCT
1674 * @param machine_name - the NetBIOS machine name of this account.
1675 * @param account_type A number indicating the type of account to create
1676 * @param org_unit The LDAP path in which to place this account
1677 * @return 0 upon success, or non-zero otherwise
1680 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1681 const char *org_unit)
1683 ADS_STATUS ret;
1684 char *samAccountName, *controlstr;
1685 TALLOC_CTX *ctx;
1686 ADS_MODLIST mods;
1687 char *machine_escaped = NULL;
1688 char *new_dn;
1689 const char *objectClass[] = {"top", "person", "organizationalPerson",
1690 "user", "computer", NULL};
1691 LDAPMessage *res = NULL;
1692 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
1693 UF_DONT_EXPIRE_PASSWD |\
1694 UF_ACCOUNTDISABLE );
1696 if (!(ctx = talloc_init("ads_add_machine_acct")))
1697 return ADS_ERROR(LDAP_NO_MEMORY);
1699 ret = ADS_ERROR(LDAP_NO_MEMORY);
1701 machine_escaped = escape_rdn_val_string_alloc(machine_name);
1702 if (!machine_escaped) {
1703 goto done;
1706 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
1707 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
1709 if ( !new_dn || !samAccountName ) {
1710 goto done;
1713 #ifndef ENCTYPE_ARCFOUR_HMAC
1714 acct_control |= UF_USE_DES_KEY_ONLY;
1715 #endif
1717 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
1718 goto done;
1721 if (!(mods = ads_init_mods(ctx))) {
1722 goto done;
1725 ads_mod_str(ctx, &mods, "cn", machine_name);
1726 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1727 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1728 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1730 ret = ads_gen_add(ads, new_dn, mods);
1732 done:
1733 SAFE_FREE(machine_escaped);
1734 ads_msgfree(ads, res);
1735 talloc_destroy(ctx);
1737 return ret;
1741 dump a binary result from ldap
1743 static void dump_binary(const char *field, struct berval **values)
1745 int i, j;
1746 for (i=0; values[i]; i++) {
1747 printf("%s: ", field);
1748 for (j=0; j<values[i]->bv_len; j++) {
1749 printf("%02X", (unsigned char)values[i]->bv_val[j]);
1751 printf("\n");
1755 static void dump_guid(const char *field, struct berval **values)
1757 int i;
1758 UUID_FLAT guid;
1759 for (i=0; values[i]; i++) {
1760 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1761 printf("%s: %s\n", field,
1762 smb_uuid_string_static(smb_uuid_unpack_static(guid)));
1767 dump a sid result from ldap
1769 static void dump_sid(const char *field, struct berval **values)
1771 int i;
1772 for (i=0; values[i]; i++) {
1773 DOM_SID sid;
1774 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1775 printf("%s: %s\n", field, sid_string_static(&sid));
1780 dump ntSecurityDescriptor
1782 static void dump_sd(const char *filed, struct berval **values)
1784 prs_struct ps;
1786 SEC_DESC *psd = 0;
1787 TALLOC_CTX *ctx = 0;
1789 if (!(ctx = talloc_init("sec_io_desc")))
1790 return;
1792 /* prepare data */
1793 prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
1794 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
1795 prs_set_offset(&ps,0);
1797 /* parse secdesc */
1798 if (!sec_io_desc("sd", &psd, &ps, 1)) {
1799 prs_mem_free(&ps);
1800 talloc_destroy(ctx);
1801 return;
1803 if (psd) ads_disp_sd(psd);
1805 prs_mem_free(&ps);
1806 talloc_destroy(ctx);
1810 dump a string result from ldap
1812 static void dump_string(const char *field, char **values)
1814 int i;
1815 for (i=0; values[i]; i++) {
1816 printf("%s: %s\n", field, values[i]);
1821 dump a field from LDAP on stdout
1822 used for debugging
1825 static BOOL ads_dump_field(char *field, void **values, void *data_area)
1827 const struct {
1828 const char *name;
1829 BOOL string;
1830 void (*handler)(const char *, struct berval **);
1831 } handlers[] = {
1832 {"objectGUID", False, dump_guid},
1833 {"netbootGUID", False, dump_guid},
1834 {"nTSecurityDescriptor", False, dump_sd},
1835 {"dnsRecord", False, dump_binary},
1836 {"objectSid", False, dump_sid},
1837 {"tokenGroups", False, dump_sid},
1838 {"tokenGroupsNoGCAcceptable", False, dump_sid},
1839 {"tokengroupsGlobalandUniversal", False, dump_sid},
1840 {"mS-DS-CreatorSID", False, dump_sid},
1841 {NULL, True, NULL}
1843 int i;
1845 if (!field) { /* must be end of an entry */
1846 printf("\n");
1847 return False;
1850 for (i=0; handlers[i].name; i++) {
1851 if (StrCaseCmp(handlers[i].name, field) == 0) {
1852 if (!values) /* first time, indicate string or not */
1853 return handlers[i].string;
1854 handlers[i].handler(field, (struct berval **) values);
1855 break;
1858 if (!handlers[i].name) {
1859 if (!values) /* first time, indicate string conversion */
1860 return True;
1861 dump_string(field, (char **)values);
1863 return False;
1867 * Dump a result from LDAP on stdout
1868 * used for debugging
1869 * @param ads connection to ads server
1870 * @param res Results to dump
1873 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
1875 ads_process_results(ads, res, ads_dump_field, NULL);
1879 * Walk through results, calling a function for each entry found.
1880 * The function receives a field name, a berval * array of values,
1881 * and a data area passed through from the start. The function is
1882 * called once with null for field and values at the end of each
1883 * entry.
1884 * @param ads connection to ads server
1885 * @param res Results to process
1886 * @param fn Function for processing each result
1887 * @param data_area user-defined area to pass to function
1889 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
1890 BOOL(*fn)(char *, void **, void *),
1891 void *data_area)
1893 LDAPMessage *msg;
1894 TALLOC_CTX *ctx;
1896 if (!(ctx = talloc_init("ads_process_results")))
1897 return;
1899 for (msg = ads_first_entry(ads, res); msg;
1900 msg = ads_next_entry(ads, msg)) {
1901 char *utf8_field;
1902 BerElement *b;
1904 for (utf8_field=ldap_first_attribute(ads->ld,
1905 (LDAPMessage *)msg,&b);
1906 utf8_field;
1907 utf8_field=ldap_next_attribute(ads->ld,
1908 (LDAPMessage *)msg,b)) {
1909 struct berval **ber_vals;
1910 char **str_vals, **utf8_vals;
1911 char *field;
1912 BOOL string;
1914 pull_utf8_talloc(ctx, &field, utf8_field);
1915 string = fn(field, NULL, data_area);
1917 if (string) {
1918 utf8_vals = ldap_get_values(ads->ld,
1919 (LDAPMessage *)msg, field);
1920 str_vals = ads_pull_strvals(ctx,
1921 (const char **) utf8_vals);
1922 fn(field, (void **) str_vals, data_area);
1923 ldap_value_free(utf8_vals);
1924 } else {
1925 ber_vals = ldap_get_values_len(ads->ld,
1926 (LDAPMessage *)msg, field);
1927 fn(field, (void **) ber_vals, data_area);
1929 ldap_value_free_len(ber_vals);
1931 ldap_memfree(utf8_field);
1933 ber_free(b, 0);
1934 talloc_free_children(ctx);
1935 fn(NULL, NULL, data_area); /* completed an entry */
1938 talloc_destroy(ctx);
1942 * count how many replies are in a LDAPMessage
1943 * @param ads connection to ads server
1944 * @param res Results to count
1945 * @return number of replies
1947 int ads_count_replies(ADS_STRUCT *ads, void *res)
1949 return ldap_count_entries(ads->ld, (LDAPMessage *)res);
1953 * pull the first entry from a ADS result
1954 * @param ads connection to ads server
1955 * @param res Results of search
1956 * @return first entry from result
1958 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
1960 return ldap_first_entry(ads->ld, res);
1964 * pull the next entry from a ADS result
1965 * @param ads connection to ads server
1966 * @param res Results of search
1967 * @return next entry from result
1969 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
1971 return ldap_next_entry(ads->ld, res);
1975 * pull the first message from a ADS result
1976 * @param ads connection to ads server
1977 * @param res Results of search
1978 * @return first message from result
1980 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
1982 return ldap_first_message(ads->ld, res);
1986 * pull the next message from a ADS result
1987 * @param ads connection to ads server
1988 * @param res Results of search
1989 * @return next message from result
1991 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
1993 return ldap_next_message(ads->ld, res);
1997 * pull a single string from a ADS result
1998 * @param ads connection to ads server
1999 * @param mem_ctx TALLOC_CTX to use for allocating result string
2000 * @param msg Results of search
2001 * @param field Attribute to retrieve
2002 * @return Result string in talloc context
2004 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2005 const char *field)
2007 char **values;
2008 char *ret = NULL;
2009 char *ux_string;
2010 size_t rc;
2012 values = ldap_get_values(ads->ld, msg, field);
2013 if (!values)
2014 return NULL;
2016 if (values[0]) {
2017 rc = pull_utf8_talloc(mem_ctx, &ux_string,
2018 values[0]);
2019 if (rc != (size_t)-1)
2020 ret = ux_string;
2023 ldap_value_free(values);
2024 return ret;
2028 * pull an array of strings from a ADS result
2029 * @param ads connection to ads server
2030 * @param mem_ctx TALLOC_CTX to use for allocating result string
2031 * @param msg Results of search
2032 * @param field Attribute to retrieve
2033 * @return Result strings in talloc context
2035 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2036 LDAPMessage *msg, const char *field,
2037 size_t *num_values)
2039 char **values;
2040 char **ret = NULL;
2041 int i;
2043 values = ldap_get_values(ads->ld, msg, field);
2044 if (!values)
2045 return NULL;
2047 *num_values = ldap_count_values(values);
2049 ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
2050 if (!ret) {
2051 ldap_value_free(values);
2052 return NULL;
2055 for (i=0;i<*num_values;i++) {
2056 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
2057 ldap_value_free(values);
2058 return NULL;
2061 ret[i] = NULL;
2063 ldap_value_free(values);
2064 return ret;
2068 * pull an array of strings from a ADS result
2069 * (handle large multivalue attributes with range retrieval)
2070 * @param ads connection to ads server
2071 * @param mem_ctx TALLOC_CTX to use for allocating result string
2072 * @param msg Results of search
2073 * @param field Attribute to retrieve
2074 * @param current_strings strings returned by a previous call to this function
2075 * @param next_attribute The next query should ask for this attribute
2076 * @param num_values How many values did we get this time?
2077 * @param more_values Are there more values to get?
2078 * @return Result strings in talloc context
2080 char **ads_pull_strings_range(ADS_STRUCT *ads,
2081 TALLOC_CTX *mem_ctx,
2082 LDAPMessage *msg, const char *field,
2083 char **current_strings,
2084 const char **next_attribute,
2085 size_t *num_strings,
2086 BOOL *more_strings)
2088 char *attr;
2089 char *expected_range_attrib, *range_attr;
2090 BerElement *ptr = NULL;
2091 char **strings;
2092 char **new_strings;
2093 size_t num_new_strings;
2094 unsigned long int range_start;
2095 unsigned long int range_end;
2097 /* we might have been given the whole lot anyway */
2098 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2099 *more_strings = False;
2100 return strings;
2103 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2105 /* look for Range result */
2106 for (attr = ldap_first_attribute(ads->ld, (LDAPMessage *)msg, &ptr);
2107 attr;
2108 attr = ldap_next_attribute(ads->ld, (LDAPMessage *)msg, ptr)) {
2109 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2110 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2111 range_attr = attr;
2112 break;
2114 ldap_memfree(attr);
2116 if (!attr) {
2117 ber_free(ptr, 0);
2118 /* nothing here - this field is just empty */
2119 *more_strings = False;
2120 return NULL;
2123 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2124 &range_start, &range_end) == 2) {
2125 *more_strings = True;
2126 } else {
2127 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2128 &range_start) == 1) {
2129 *more_strings = False;
2130 } else {
2131 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2132 range_attr));
2133 ldap_memfree(range_attr);
2134 *more_strings = False;
2135 return NULL;
2139 if ((*num_strings) != range_start) {
2140 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2141 " - aborting range retreival\n",
2142 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2143 ldap_memfree(range_attr);
2144 *more_strings = False;
2145 return NULL;
2148 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2150 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2151 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2152 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2153 range_attr, (unsigned long int)range_end - range_start + 1,
2154 (unsigned long int)num_new_strings));
2155 ldap_memfree(range_attr);
2156 *more_strings = False;
2157 return NULL;
2160 strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2161 *num_strings + num_new_strings);
2163 if (strings == NULL) {
2164 ldap_memfree(range_attr);
2165 *more_strings = False;
2166 return NULL;
2169 if (new_strings && num_new_strings) {
2170 memcpy(&strings[*num_strings], new_strings,
2171 sizeof(*new_strings) * num_new_strings);
2174 (*num_strings) += num_new_strings;
2176 if (*more_strings) {
2177 *next_attribute = talloc_asprintf(mem_ctx,
2178 "%s;range=%d-*",
2179 field,
2180 (int)*num_strings);
2182 if (!*next_attribute) {
2183 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2184 ldap_memfree(range_attr);
2185 *more_strings = False;
2186 return NULL;
2190 ldap_memfree(range_attr);
2192 return strings;
2196 * pull a single uint32 from a ADS result
2197 * @param ads connection to ads server
2198 * @param msg Results of search
2199 * @param field Attribute to retrieve
2200 * @param v Pointer to int to store result
2201 * @return boolean inidicating success
2203 BOOL ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2204 uint32 *v)
2206 char **values;
2208 values = ldap_get_values(ads->ld, msg, field);
2209 if (!values)
2210 return False;
2211 if (!values[0]) {
2212 ldap_value_free(values);
2213 return False;
2216 *v = atoi(values[0]);
2217 ldap_value_free(values);
2218 return True;
2222 * pull a single objectGUID from an ADS result
2223 * @param ads connection to ADS server
2224 * @param msg results of search
2225 * @param guid 37-byte area to receive text guid
2226 * @return boolean indicating success
2228 BOOL ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2230 char **values;
2231 UUID_FLAT flat_guid;
2233 values = ldap_get_values(ads->ld, msg, "objectGUID");
2234 if (!values)
2235 return False;
2237 if (values[0]) {
2238 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2239 smb_uuid_unpack(flat_guid, guid);
2240 ldap_value_free(values);
2241 return True;
2243 ldap_value_free(values);
2244 return False;
2250 * pull a single DOM_SID from a ADS result
2251 * @param ads connection to ads server
2252 * @param msg Results of search
2253 * @param field Attribute to retrieve
2254 * @param sid Pointer to sid to store result
2255 * @return boolean inidicating success
2257 BOOL ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2258 DOM_SID *sid)
2260 struct berval **values;
2261 BOOL ret = False;
2263 values = ldap_get_values_len(ads->ld, msg, field);
2265 if (!values)
2266 return False;
2268 if (values[0])
2269 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2271 ldap_value_free_len(values);
2272 return ret;
2276 * pull an array of DOM_SIDs from a ADS result
2277 * @param ads connection to ads server
2278 * @param mem_ctx TALLOC_CTX for allocating sid array
2279 * @param msg Results of search
2280 * @param field Attribute to retrieve
2281 * @param sids pointer to sid array to allocate
2282 * @return the count of SIDs pulled
2284 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2285 LDAPMessage *msg, const char *field, DOM_SID **sids)
2287 struct berval **values;
2288 BOOL ret;
2289 int count, i;
2291 values = ldap_get_values_len(ads->ld, msg, field);
2293 if (!values)
2294 return 0;
2296 for (i=0; values[i]; i++)
2297 /* nop */ ;
2299 if (i) {
2300 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2301 if (!(*sids)) {
2302 ldap_value_free_len(values);
2303 return 0;
2305 } else {
2306 (*sids) = NULL;
2309 count = 0;
2310 for (i=0; values[i]; i++) {
2311 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2312 if (ret) {
2313 fstring sid;
2314 DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid, &(*sids)[count])));
2315 count++;
2319 ldap_value_free_len(values);
2320 return count;
2324 * pull a SEC_DESC from a ADS result
2325 * @param ads connection to ads server
2326 * @param mem_ctx TALLOC_CTX for allocating sid array
2327 * @param msg Results of search
2328 * @param field Attribute to retrieve
2329 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2330 * @return boolean inidicating success
2332 BOOL ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2333 LDAPMessage *msg, const char *field, SEC_DESC **sd)
2335 struct berval **values;
2336 BOOL ret = False;
2338 values = ldap_get_values_len(ads->ld, msg, field);
2340 if (!values) return False;
2342 if (values[0]) {
2343 prs_struct ps;
2344 prs_init(&ps, values[0]->bv_len, mem_ctx, UNMARSHALL);
2345 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
2346 prs_set_offset(&ps,0);
2348 ret = sec_io_desc("sd", sd, &ps, 1);
2349 prs_mem_free(&ps);
2352 ldap_value_free_len(values);
2353 return ret;
2357 * in order to support usernames longer than 21 characters we need to
2358 * use both the sAMAccountName and the userPrincipalName attributes
2359 * It seems that not all users have the userPrincipalName attribute set
2361 * @param ads connection to ads server
2362 * @param mem_ctx TALLOC_CTX for allocating sid array
2363 * @param msg Results of search
2364 * @return the username
2366 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2367 LDAPMessage *msg)
2369 #if 0 /* JERRY */
2370 char *ret, *p;
2372 /* lookup_name() only works on the sAMAccountName to
2373 returning the username portion of userPrincipalName
2374 breaks winbindd_getpwnam() */
2376 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2377 if (ret && (p = strchr_m(ret, '@'))) {
2378 *p = 0;
2379 return ret;
2381 #endif
2382 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2387 * find the update serial number - this is the core of the ldap cache
2388 * @param ads connection to ads server
2389 * @param ads connection to ADS server
2390 * @param usn Pointer to retrieved update serial number
2391 * @return status of search
2393 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2395 const char *attrs[] = {"highestCommittedUSN", NULL};
2396 ADS_STATUS status;
2397 LDAPMessage *res;
2399 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2400 if (!ADS_ERR_OK(status))
2401 return status;
2403 if (ads_count_replies(ads, res) != 1) {
2404 ads_msgfree(ads, res);
2405 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2408 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2409 ads_msgfree(ads, res);
2410 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2413 ads_msgfree(ads, res);
2414 return ADS_SUCCESS;
2417 /* parse a ADS timestring - typical string is
2418 '20020917091222.0Z0' which means 09:12.22 17th September
2419 2002, timezone 0 */
2420 static time_t ads_parse_time(const char *str)
2422 struct tm tm;
2424 ZERO_STRUCT(tm);
2426 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2427 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2428 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2429 return 0;
2431 tm.tm_year -= 1900;
2432 tm.tm_mon -= 1;
2434 return timegm(&tm);
2437 /********************************************************************
2438 ********************************************************************/
2440 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2442 const char *attrs[] = {"currentTime", NULL};
2443 ADS_STATUS status;
2444 LDAPMessage *res;
2445 char *timestr;
2446 TALLOC_CTX *ctx;
2447 ADS_STRUCT *ads_s = ads;
2449 if (!(ctx = talloc_init("ads_current_time"))) {
2450 return ADS_ERROR(LDAP_NO_MEMORY);
2453 /* establish a new ldap tcp session if necessary */
2455 if ( !ads->ld ) {
2456 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2457 ads->server.ldap_server )) == NULL )
2459 goto done;
2461 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2462 status = ads_connect( ads_s );
2463 if ( !ADS_ERR_OK(status))
2464 goto done;
2467 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2468 if (!ADS_ERR_OK(status)) {
2469 goto done;
2472 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2473 if (!timestr) {
2474 ads_msgfree(ads_s, res);
2475 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2476 goto done;
2479 /* but save the time and offset in the original ADS_STRUCT */
2481 ads->config.current_time = ads_parse_time(timestr);
2483 if (ads->config.current_time != 0) {
2484 ads->auth.time_offset = ads->config.current_time - time(NULL);
2485 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2488 ads_msgfree(ads, res);
2490 status = ADS_SUCCESS;
2492 done:
2493 /* free any temporary ads connections */
2494 if ( ads_s != ads ) {
2495 ads_destroy( &ads_s );
2497 talloc_destroy(ctx);
2499 return status;
2502 /********************************************************************
2503 ********************************************************************/
2505 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2507 const char *attrs[] = {"domainFunctionality", NULL};
2508 ADS_STATUS status;
2509 LDAPMessage *res;
2510 ADS_STRUCT *ads_s = ads;
2512 *val = DS_DOMAIN_FUNCTION_2000;
2514 /* establish a new ldap tcp session if necessary */
2516 if ( !ads->ld ) {
2517 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2518 ads->server.ldap_server )) == NULL )
2520 goto done;
2522 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2523 status = ads_connect( ads_s );
2524 if ( !ADS_ERR_OK(status))
2525 goto done;
2528 /* If the attribute does not exist assume it is a Windows 2000
2529 functional domain */
2531 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2532 if (!ADS_ERR_OK(status)) {
2533 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2534 status = ADS_SUCCESS;
2536 goto done;
2539 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2540 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2542 DEBUG(3,("ads_domain_func_level: %d\n", *val));
2545 ads_msgfree(ads, res);
2547 done:
2548 /* free any temporary ads connections */
2549 if ( ads_s != ads ) {
2550 ads_destroy( &ads_s );
2553 return status;
2557 * find the domain sid for our domain
2558 * @param ads connection to ads server
2559 * @param sid Pointer to domain sid
2560 * @return status of search
2562 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2564 const char *attrs[] = {"objectSid", NULL};
2565 LDAPMessage *res;
2566 ADS_STATUS rc;
2568 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2569 attrs, &res);
2570 if (!ADS_ERR_OK(rc)) return rc;
2571 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2572 ads_msgfree(ads, res);
2573 return ADS_ERROR_SYSTEM(ENOENT);
2575 ads_msgfree(ads, res);
2577 return ADS_SUCCESS;
2581 * find our site name
2582 * @param ads connection to ads server
2583 * @param mem_ctx Pointer to talloc context
2584 * @param site_name Pointer to the sitename
2585 * @return status of search
2587 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2589 ADS_STATUS status;
2590 LDAPMessage *res;
2591 const char *dn, *service_name;
2592 const char *attrs[] = { "dsServiceName", NULL };
2594 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2595 if (!ADS_ERR_OK(status)) {
2596 return status;
2599 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2600 if (service_name == NULL) {
2601 ads_msgfree(ads, res);
2602 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2605 ads_msgfree(ads, res);
2607 /* go up three levels */
2608 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2609 if (dn == NULL) {
2610 return ADS_ERROR(LDAP_NO_MEMORY);
2613 *site_name = talloc_strdup(mem_ctx, dn);
2614 if (*site_name == NULL) {
2615 return ADS_ERROR(LDAP_NO_MEMORY);
2618 return status;
2620 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2625 * find the site dn where a machine resides
2626 * @param ads connection to ads server
2627 * @param mem_ctx Pointer to talloc context
2628 * @param computer_name name of the machine
2629 * @param site_name Pointer to the sitename
2630 * @return status of search
2632 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
2634 ADS_STATUS status;
2635 LDAPMessage *res;
2636 const char *parent, *config_context, *filter;
2637 const char *attrs[] = { "configurationNamingContext", NULL };
2638 char *dn;
2640 /* shortcut a query */
2641 if (strequal(computer_name, ads->config.ldap_server_name)) {
2642 return ads_site_dn(ads, mem_ctx, site_dn);
2645 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2646 if (!ADS_ERR_OK(status)) {
2647 return status;
2650 config_context = ads_pull_string(ads, mem_ctx, res, "configurationNamingContext");
2651 if (config_context == NULL) {
2652 ads_msgfree(ads, res);
2653 return ADS_ERROR(LDAP_NO_MEMORY);
2656 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
2657 if (filter == NULL) {
2658 ads_msgfree(ads, res);
2659 return ADS_ERROR(LDAP_NO_MEMORY);
2662 ads_msgfree(ads, res);
2664 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE, filter, NULL, &res);
2665 if (!ADS_ERR_OK(status)) {
2666 return status;
2669 if (ads_count_replies(ads, res) != 1) {
2670 ads_msgfree(ads, res);
2671 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2674 dn = ads_get_dn(ads, res);
2675 if (dn == NULL) {
2676 ads_msgfree(ads, res);
2677 return ADS_ERROR(LDAP_NO_MEMORY);
2680 /* go up three levels */
2681 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
2682 if (parent == NULL) {
2683 ads_msgfree(ads, res);
2684 ads_memfree(ads, dn);
2685 return ADS_ERROR(LDAP_NO_MEMORY);
2688 *site_dn = talloc_strdup(mem_ctx, parent);
2689 if (*site_dn == NULL) {
2690 ads_msgfree(ads, res);
2691 ads_memfree(ads, dn);
2692 return ADS_ERROR(LDAP_NO_MEMORY);
2695 ads_memfree(ads, dn);
2696 ads_msgfree(ads, res);
2698 return status;
2702 * get the upn suffixes for a domain
2703 * @param ads connection to ads server
2704 * @param mem_ctx Pointer to talloc context
2705 * @param suffixes Pointer to an array of suffixes
2706 * @param num_suffixes Pointer to the number of suffixes
2707 * @return status of search
2709 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
2711 ADS_STATUS status;
2712 LDAPMessage *res;
2713 const char *config_context, *base;
2714 const char *attrs[] = { "configurationNamingContext", NULL };
2715 const char *attrs2[] = { "uPNSuffixes", NULL };
2717 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2718 if (!ADS_ERR_OK(status)) {
2719 return status;
2722 config_context = ads_pull_string(ads, mem_ctx, res, "configurationNamingContext");
2723 if (config_context == NULL) {
2724 ads_msgfree(ads, res);
2725 return ADS_ERROR(LDAP_NO_MEMORY);
2728 ads_msgfree(ads, res);
2730 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
2731 if (base == NULL) {
2732 return ADS_ERROR(LDAP_NO_MEMORY);
2735 status = ads_search_dn(ads, &res, base, attrs2);
2736 if (!ADS_ERR_OK(status)) {
2737 return status;
2740 if (ads_count_replies(ads, res) != 1) {
2741 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2744 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
2745 if ((*suffixes) == NULL) {
2746 ads_msgfree(ads, res);
2747 return ADS_ERROR(LDAP_NO_MEMORY);
2750 ads_msgfree(ads, res);
2752 return status;
2756 * pull a DOM_SID from an extended dn string
2757 * @param mem_ctx TALLOC_CTX
2758 * @param flags string type of extended_dn
2759 * @param sid pointer to a DOM_SID
2760 * @return boolean inidicating success
2762 BOOL ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
2763 const char *dn,
2764 enum ads_extended_dn_flags flags,
2765 DOM_SID *sid)
2767 char *p, *q;
2769 if (!dn) {
2770 return False;
2774 * ADS_EXTENDED_DN_HEX_STRING:
2775 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
2777 * ADS_EXTENDED_DN_STRING (only with w2k3):
2778 <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
2781 p = strchr(dn, ';');
2782 if (!p) {
2783 return False;
2786 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
2787 return False;
2790 p += strlen(";<SID=");
2792 q = strchr(p, '>');
2793 if (!q) {
2794 return False;
2797 *q = '\0';
2799 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
2801 switch (flags) {
2803 case ADS_EXTENDED_DN_STRING:
2804 if (!string_to_sid(sid, p)) {
2805 return False;
2807 break;
2808 case ADS_EXTENDED_DN_HEX_STRING: {
2809 pstring buf;
2810 size_t buf_len;
2812 buf_len = strhex_to_str(buf, strlen(p), p);
2813 if (buf_len == 0) {
2814 return False;
2817 if (!sid_parse(buf, buf_len, sid)) {
2818 DEBUG(10,("failed to parse sid\n"));
2819 return False;
2821 break;
2823 default:
2824 DEBUG(10,("unknown extended dn format\n"));
2825 return False;
2828 return True;
2832 * pull an array of DOM_SIDs from a ADS result
2833 * @param ads connection to ads server
2834 * @param mem_ctx TALLOC_CTX for allocating sid array
2835 * @param msg Results of search
2836 * @param field Attribute to retrieve
2837 * @param flags string type of extended_dn
2838 * @param sids pointer to sid array to allocate
2839 * @return the count of SIDs pulled
2841 int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads,
2842 TALLOC_CTX *mem_ctx,
2843 LDAPMessage *msg,
2844 const char *field,
2845 enum ads_extended_dn_flags flags,
2846 DOM_SID **sids)
2848 int i;
2849 size_t dn_count;
2850 char **dn_strings;
2852 if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field,
2853 &dn_count)) == NULL) {
2854 return 0;
2857 (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, dn_count + 1);
2858 if (!(*sids)) {
2859 TALLOC_FREE(dn_strings);
2860 return 0;
2863 for (i=0; i<dn_count; i++) {
2865 if (!ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i],
2866 flags, &(*sids)[i])) {
2867 TALLOC_FREE(*sids);
2868 TALLOC_FREE(dn_strings);
2869 return 0;
2873 TALLOC_FREE(dn_strings);
2875 return dn_count;
2878 /********************************************************************
2879 ********************************************************************/
2881 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2883 LDAPMessage *res = NULL;
2884 ADS_STATUS status;
2885 int count = 0;
2886 char *name = NULL;
2888 status = ads_find_machine_acct(ads, &res, global_myname());
2889 if (!ADS_ERR_OK(status)) {
2890 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
2891 global_myname()));
2892 goto out;
2895 if ( (count = ads_count_replies(ads, res)) != 1 ) {
2896 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
2897 goto out;
2900 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
2901 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
2904 out:
2905 ads_msgfree(ads, res);
2907 return name;
2910 /********************************************************************
2911 ********************************************************************/
2913 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2915 LDAPMessage *res = NULL;
2916 ADS_STATUS status;
2917 int count = 0;
2918 char *name = NULL;
2920 status = ads_find_machine_acct(ads, &res, global_myname());
2921 if (!ADS_ERR_OK(status)) {
2922 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
2923 global_myname()));
2924 goto out;
2927 if ( (count = ads_count_replies(ads, res)) != 1 ) {
2928 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
2929 goto out;
2932 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
2933 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
2936 out:
2937 ads_msgfree(ads, res);
2939 return name;
2942 /********************************************************************
2943 ********************************************************************/
2945 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2947 LDAPMessage *res = NULL;
2948 ADS_STATUS status;
2949 int count = 0;
2950 char *name = NULL;
2952 status = ads_find_machine_acct(ads, &res, global_myname());
2953 if (!ADS_ERR_OK(status)) {
2954 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
2955 global_myname()));
2956 goto out;
2959 if ( (count = ads_count_replies(ads, res)) != 1 ) {
2960 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
2961 goto out;
2964 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
2965 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
2968 out:
2969 ads_msgfree(ads, res);
2971 return name;
2974 #if 0
2976 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
2979 * Join a machine to a realm
2980 * Creates the machine account and sets the machine password
2981 * @param ads connection to ads server
2982 * @param machine name of host to add
2983 * @param org_unit Organizational unit to place machine in
2984 * @return status of join
2986 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
2987 uint32 account_type, const char *org_unit)
2989 ADS_STATUS status;
2990 LDAPMessage *res = NULL;
2991 char *machine;
2993 /* machine name must be lowercase */
2994 machine = SMB_STRDUP(machine_name);
2995 strlower_m(machine);
2998 status = ads_find_machine_acct(ads, (void **)&res, machine);
2999 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3000 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3001 status = ads_leave_realm(ads, machine);
3002 if (!ADS_ERR_OK(status)) {
3003 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3004 machine, ads->config.realm));
3005 return status;
3009 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3010 if (!ADS_ERR_OK(status)) {
3011 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3012 SAFE_FREE(machine);
3013 return status;
3016 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3017 if (!ADS_ERR_OK(status)) {
3018 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3019 SAFE_FREE(machine);
3020 return status;
3023 SAFE_FREE(machine);
3024 ads_msgfree(ads, res);
3026 return status;
3028 #endif
3031 * Delete a machine from the realm
3032 * @param ads connection to ads server
3033 * @param hostname Machine to remove
3034 * @return status of delete
3036 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3038 ADS_STATUS status;
3039 void *msg;
3040 LDAPMessage *res;
3041 char *hostnameDN, *host;
3042 int rc;
3043 LDAPControl ldap_control;
3044 LDAPControl * pldap_control[2] = {NULL, NULL};
3046 pldap_control[0] = &ldap_control;
3047 memset(&ldap_control, 0, sizeof(LDAPControl));
3048 ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
3050 /* hostname must be lowercase */
3051 host = SMB_STRDUP(hostname);
3052 strlower_m(host);
3054 status = ads_find_machine_acct(ads, &res, host);
3055 if (!ADS_ERR_OK(status)) {
3056 DEBUG(0, ("Host account for %s does not exist.\n", host));
3057 SAFE_FREE(host);
3058 return status;
3061 msg = ads_first_entry(ads, res);
3062 if (!msg) {
3063 SAFE_FREE(host);
3064 return ADS_ERROR_SYSTEM(ENOENT);
3067 hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
3069 rc = ldap_delete_ext_s(ads->ld, hostnameDN, pldap_control, NULL);
3070 if (rc) {
3071 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3072 }else {
3073 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3076 if (rc != LDAP_SUCCESS) {
3077 const char *attrs[] = { "cn", NULL };
3078 LDAPMessage *msg_sub;
3080 /* we only search with scope ONE, we do not expect any further
3081 * objects to be created deeper */
3083 status = ads_do_search_retry(ads, hostnameDN,
3084 LDAP_SCOPE_ONELEVEL,
3085 "(objectclass=*)", attrs, &res);
3087 if (!ADS_ERR_OK(status)) {
3088 SAFE_FREE(host);
3089 ads_memfree(ads, hostnameDN);
3090 return status;
3093 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3094 msg_sub = ads_next_entry(ads, msg_sub)) {
3096 char *dn = NULL;
3098 if ((dn = ads_get_dn(ads, msg_sub)) == NULL) {
3099 SAFE_FREE(host);
3100 ads_memfree(ads, hostnameDN);
3101 return ADS_ERROR(LDAP_NO_MEMORY);
3104 status = ads_del_dn(ads, dn);
3105 if (!ADS_ERR_OK(status)) {
3106 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3107 SAFE_FREE(host);
3108 ads_memfree(ads, dn);
3109 ads_memfree(ads, hostnameDN);
3110 return status;
3113 ads_memfree(ads, dn);
3116 /* there should be no subordinate objects anymore */
3117 status = ads_do_search_retry(ads, hostnameDN,
3118 LDAP_SCOPE_ONELEVEL,
3119 "(objectclass=*)", attrs, &res);
3121 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3122 SAFE_FREE(host);
3123 ads_memfree(ads, hostnameDN);
3124 return status;
3127 /* delete hostnameDN now */
3128 status = ads_del_dn(ads, hostnameDN);
3129 if (!ADS_ERR_OK(status)) {
3130 SAFE_FREE(host);
3131 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3132 ads_memfree(ads, hostnameDN);
3133 return status;
3137 ads_memfree(ads, hostnameDN);
3139 status = ads_find_machine_acct(ads, &res, host);
3140 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3141 DEBUG(3, ("Failed to remove host account.\n"));
3142 SAFE_FREE(host);
3143 return status;
3146 SAFE_FREE(host);
3147 return status;
3150 #endif