r22728: Patch from Danilo Almeida <dalmeida@centeris.com>:
[Samba.git] / source / libads / ldap.c
blobaf4347c1474405b22f8baa939ba4527b584707d2
1 /*
2 Unix SMB/CIFS implementation.
3 ads (active directory) utility library
4 Copyright (C) Andrew Tridgell 2001
5 Copyright (C) Remus Koos 2001
6 Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002
7 Copyright (C) Guenther Deschner 2005
8 Copyright (C) Gerald Carter 2006
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 #include "includes.h"
27 #ifdef HAVE_LDAP
29 /**
30 * @file ldap.c
31 * @brief basic ldap client-side routines for ads server communications
33 * The routines contained here should do the necessary ldap calls for
34 * ads setups.
36 * Important note: attribute names passed into ads_ routines must
37 * already be in UTF-8 format. We do not convert them because in almost
38 * all cases, they are just ascii (which is represented with the same
39 * codepoints in UTF-8). This may have to change at some point
40 **/
43 #define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"
45 static SIG_ATOMIC_T gotalarm;
47 /***************************************************************
48 Signal function to tell us we timed out.
49 ****************************************************************/
51 static void gotalarm_sig(void)
53 gotalarm = 1;
56 LDAP *ldap_open_with_timeout(const char *server, int port, unsigned int to)
58 LDAP *ldp = NULL;
60 /* Setup timeout */
61 gotalarm = 0;
62 CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
63 alarm(to);
64 /* End setup timeout. */
66 ldp = ldap_open(server, port);
68 if (ldp == NULL) {
69 DEBUG(2,("Could not open LDAP connection to %s:%d: %s\n",
70 server, port, strerror(errno)));
73 /* Teardown timeout. */
74 CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
75 alarm(0);
77 return ldp;
80 static int ldap_search_with_timeout(LDAP *ld,
81 LDAP_CONST char *base,
82 int scope,
83 LDAP_CONST char *filter,
84 char **attrs,
85 int attrsonly,
86 LDAPControl **sctrls,
87 LDAPControl **cctrls,
88 int sizelimit,
89 LDAPMessage **res )
91 struct timeval timeout;
92 int result;
94 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
95 timeout.tv_sec = lp_ldap_timeout();
96 timeout.tv_usec = 0;
98 /* Setup alarm timeout.... Do we need both of these ? JRA. */
99 gotalarm = 0;
100 CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
101 alarm(lp_ldap_timeout());
102 /* End setup timeout. */
104 result = ldap_search_ext_s(ld, base, scope, filter, attrs,
105 attrsonly, sctrls, cctrls, &timeout,
106 sizelimit, res);
108 /* Teardown timeout. */
109 CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
110 alarm(0);
112 if (gotalarm != 0)
113 return LDAP_TIMELIMIT_EXCEEDED;
115 return result;
118 /**********************************************
119 Do client and server sitename match ?
120 **********************************************/
122 BOOL ads_sitename_match(ADS_STRUCT *ads)
124 if (ads->config.server_site_name == NULL &&
125 ads->config.client_site_name == NULL ) {
126 DEBUG(10,("ads_sitename_match: both null\n"));
127 return True;
129 if (ads->config.server_site_name &&
130 ads->config.client_site_name &&
131 strequal(ads->config.server_site_name,
132 ads->config.client_site_name)) {
133 DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
134 return True;
136 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
137 ads->config.server_site_name ? ads->config.server_site_name : "NULL",
138 ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
139 return False;
142 /**********************************************
143 Is this the closest DC ?
144 **********************************************/
146 BOOL ads_closest_dc(ADS_STRUCT *ads)
148 if (ads->config.flags & ADS_CLOSEST) {
149 DEBUG(10,("ads_closest_dc: ADS_CLOSEST flag set\n"));
150 return True;
153 /* not sure if this can ever happen */
154 if (ads_sitename_match(ads)) {
155 DEBUG(10,("ads_closest_dc: ADS_CLOSEST flag not set but sites match\n"));
156 return True;
159 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
160 ads->config.ldap_server_name));
162 return False;
167 try a connection to a given ldap server, returning True and setting the servers IP
168 in the ads struct if successful
170 BOOL ads_try_connect(ADS_STRUCT *ads, const char *server )
172 char *srv;
173 struct cldap_netlogon_reply cldap_reply;
175 if (!server || !*server) {
176 return False;
179 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
180 server, ads->server.realm));
182 /* this copes with inet_ntoa brokenness */
184 srv = SMB_STRDUP(server);
186 ZERO_STRUCT( cldap_reply );
188 if ( !ads_cldap_netlogon( srv, ads->server.realm, &cldap_reply ) ) {
189 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", srv));
190 SAFE_FREE( srv );
191 return False;
194 /* Check the CLDAP reply flags */
196 if ( !(cldap_reply.flags & ADS_LDAP) ) {
197 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
198 srv));
199 SAFE_FREE( srv );
200 return False;
203 /* Fill in the ads->config values */
205 SAFE_FREE(ads->config.realm);
206 SAFE_FREE(ads->config.bind_path);
207 SAFE_FREE(ads->config.ldap_server_name);
208 SAFE_FREE(ads->config.server_site_name);
209 SAFE_FREE(ads->config.client_site_name);
210 SAFE_FREE(ads->server.workgroup);
212 ads->config.flags = cldap_reply.flags;
213 ads->config.ldap_server_name = SMB_STRDUP(cldap_reply.hostname);
214 strupper_m(cldap_reply.domain);
215 ads->config.realm = SMB_STRDUP(cldap_reply.domain);
216 ads->config.bind_path = ads_build_dn(ads->config.realm);
217 if (*cldap_reply.server_site_name) {
218 ads->config.server_site_name =
219 SMB_STRDUP(cldap_reply.server_site_name);
221 if (*cldap_reply.client_site_name) {
222 ads->config.client_site_name =
223 SMB_STRDUP(cldap_reply.client_site_name);
226 ads->server.workgroup = SMB_STRDUP(cldap_reply.netbios_domain);
228 ads->ldap_port = LDAP_PORT;
229 ads->ldap_ip = *interpret_addr2(srv);
230 SAFE_FREE(srv);
232 /* Store our site name. */
233 sitename_store( cldap_reply.domain, cldap_reply.client_site_name );
235 return True;
238 /**********************************************************************
239 Try to find an AD dc using our internal name resolution routines
240 Try the realm first and then then workgroup name if netbios is not
241 disabled
242 **********************************************************************/
244 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
246 const char *c_realm;
247 int count, i=0;
248 struct ip_service *ip_list;
249 pstring realm;
250 BOOL got_realm = False;
251 BOOL use_own_domain = False;
252 char *sitename;
253 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
255 /* if the realm and workgroup are both empty, assume they are ours */
257 /* realm */
258 c_realm = ads->server.realm;
260 if ( !c_realm || !*c_realm ) {
261 /* special case where no realm and no workgroup means our own */
262 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
263 use_own_domain = True;
264 c_realm = lp_realm();
268 if (c_realm && *c_realm)
269 got_realm = True;
271 /* we need to try once with the realm name and fallback to the
272 netbios domain name if we fail (if netbios has not been disabled */
274 if ( !got_realm && !lp_disable_netbios() ) {
275 c_realm = ads->server.workgroup;
276 if (!c_realm || !*c_realm) {
277 if ( use_own_domain )
278 c_realm = lp_workgroup();
281 if ( !c_realm || !*c_realm ) {
282 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
283 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
287 pstrcpy( realm, c_realm );
289 sitename = sitename_fetch(realm);
291 again:
293 DEBUG(6,("ads_find_dc: looking for %s '%s'\n",
294 (got_realm ? "realm" : "domain"), realm));
296 status = get_sorted_dc_list(realm, sitename, &ip_list, &count, got_realm);
297 if (!NT_STATUS_IS_OK(status)) {
298 /* fall back to netbios if we can */
299 if ( got_realm && !lp_disable_netbios() ) {
300 got_realm = False;
301 goto again;
304 SAFE_FREE(sitename);
305 return status;
308 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
309 for ( i=0; i<count; i++ ) {
310 fstring server;
312 fstrcpy( server, inet_ntoa(ip_list[i].ip) );
314 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
315 continue;
317 if (!got_realm) {
318 /* realm in this case is a workgroup name. We need
319 to ignore any IP addresses in the negative connection
320 cache that match ip addresses returned in the ad realm
321 case. It sucks that I have to reproduce the logic above... */
322 c_realm = ads->server.realm;
323 if ( !c_realm || !*c_realm ) {
324 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
325 c_realm = lp_realm();
328 if (c_realm && *c_realm &&
329 !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm, server))) {
330 /* Ensure we add the workgroup name for this
331 IP address as negative too. */
332 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
333 continue;
337 if ( ads_try_connect(ads, server) ) {
338 SAFE_FREE(ip_list);
339 SAFE_FREE(sitename);
340 return NT_STATUS_OK;
343 /* keep track of failures */
344 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
347 SAFE_FREE(ip_list);
349 /* In case we failed to contact one of our closest DC on our site we
350 * need to try to find another DC, retry with a site-less SRV DNS query
351 * - Guenther */
353 if (sitename) {
354 DEBUG(1,("ads_find_dc: failed to find a valid DC on our site (%s), "
355 "trying to find another DC\n", sitename));
356 SAFE_FREE(sitename);
357 namecache_delete(realm, 0x1C);
358 goto again;
361 return NT_STATUS_NO_LOGON_SERVERS;
366 * Connect to the LDAP server
367 * @param ads Pointer to an existing ADS_STRUCT
368 * @return status of connection
370 ADS_STATUS ads_connect(ADS_STRUCT *ads)
372 int version = LDAP_VERSION3;
373 ADS_STATUS status;
374 NTSTATUS ntstatus;
376 ads->last_attempt = time(NULL);
377 ads->ld = NULL;
379 /* try with a user specified server */
381 if (ads->server.ldap_server &&
382 ads_try_connect(ads, ads->server.ldap_server)) {
383 goto got_connection;
386 ntstatus = ads_find_dc(ads);
387 if (NT_STATUS_IS_OK(ntstatus)) {
388 goto got_connection;
391 return ADS_ERROR_NT(ntstatus);
393 got_connection:
394 DEBUG(3,("Connected to LDAP server %s\n", inet_ntoa(ads->ldap_ip)));
396 if (!ads->auth.user_name) {
397 /* Must use the userPrincipalName value here or sAMAccountName
398 and not servicePrincipalName; found by Guenther Deschner */
400 asprintf(&ads->auth.user_name, "%s$", global_myname() );
403 if (!ads->auth.realm) {
404 ads->auth.realm = SMB_STRDUP(ads->config.realm);
407 if (!ads->auth.kdc_server) {
408 ads->auth.kdc_server = SMB_STRDUP(inet_ntoa(ads->ldap_ip));
411 #if KRB5_DNS_HACK
412 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
413 to MIT kerberos to work (tridge) */
415 char *env;
416 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
417 setenv(env, ads->auth.kdc_server, 1);
418 free(env);
420 #endif
422 /* If the caller() requested no LDAP bind, then we are done */
424 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
425 return ADS_SUCCESS;
428 /* Otherwise setup the TCP LDAP session */
430 if ( (ads->ld = ldap_open_with_timeout(ads->config.ldap_server_name,
431 LDAP_PORT, lp_ldap_timeout())) == NULL )
433 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
436 /* cache the successful connection for workgroup and realm */
437 if (ads_closest_dc(ads)) {
438 saf_store( ads->server.workgroup, inet_ntoa(ads->ldap_ip));
439 saf_store( ads->server.realm, inet_ntoa(ads->ldap_ip));
442 ldap_set_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
444 status = ADS_ERROR(smb_ldap_start_tls(ads->ld, version));
445 if (!ADS_ERR_OK(status)) {
446 return status;
449 /* fill in the current time and offsets */
451 status = ads_current_time( ads );
452 if ( !ADS_ERR_OK(status) ) {
453 return status;
456 /* Now do the bind */
458 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
459 return ADS_ERROR(ldap_simple_bind_s( ads->ld, NULL, NULL));
462 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
463 return ADS_ERROR(ldap_simple_bind_s( ads->ld, ads->auth.user_name, ads->auth.password));
466 return ads_sasl_bind(ads);
470 Duplicate a struct berval into talloc'ed memory
472 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
474 struct berval *value;
476 if (!in_val) return NULL;
478 value = TALLOC_ZERO_P(ctx, struct berval);
479 if (value == NULL)
480 return NULL;
481 if (in_val->bv_len == 0) return value;
483 value->bv_len = in_val->bv_len;
484 value->bv_val = (char *)TALLOC_MEMDUP(ctx, in_val->bv_val,
485 in_val->bv_len);
486 return value;
490 Make a values list out of an array of (struct berval *)
492 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
493 const struct berval **in_vals)
495 struct berval **values;
496 int i;
498 if (!in_vals) return NULL;
499 for (i=0; in_vals[i]; i++)
500 ; /* count values */
501 values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
502 if (!values) return NULL;
504 for (i=0; in_vals[i]; i++) {
505 values[i] = dup_berval(ctx, in_vals[i]);
507 return values;
511 UTF8-encode a values list out of an array of (char *)
513 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
515 char **values;
516 int i;
518 if (!in_vals) return NULL;
519 for (i=0; in_vals[i]; i++)
520 ; /* count values */
521 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
522 if (!values) return NULL;
524 for (i=0; in_vals[i]; i++) {
525 push_utf8_talloc(ctx, &values[i], in_vals[i]);
527 return values;
531 Pull a (char *) array out of a UTF8-encoded values list
533 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
535 char **values;
536 int i;
538 if (!in_vals) return NULL;
539 for (i=0; in_vals[i]; i++)
540 ; /* count values */
541 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
542 if (!values) return NULL;
544 for (i=0; in_vals[i]; i++) {
545 pull_utf8_talloc(ctx, &values[i], in_vals[i]);
547 return values;
551 * Do a search with paged results. cookie must be null on the first
552 * call, and then returned on each subsequent call. It will be null
553 * again when the entire search is complete
554 * @param ads connection to ads server
555 * @param bind_path Base dn for the search
556 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
557 * @param expr Search expression - specified in local charset
558 * @param attrs Attributes to retrieve - specified in utf8 or ascii
559 * @param res ** which will contain results - free res* with ads_msgfree()
560 * @param count Number of entries retrieved on this page
561 * @param cookie The paged results cookie to be returned on subsequent calls
562 * @return status of search
564 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
565 const char *bind_path,
566 int scope, const char *expr,
567 const char **attrs, void *args,
568 LDAPMessage **res,
569 int *count, struct berval **cookie)
571 int rc, i, version;
572 char *utf8_expr, *utf8_path, **search_attrs;
573 LDAPControl PagedResults, NoReferrals, ExtendedDn, *controls[4], **rcontrols;
574 BerElement *cookie_be = NULL;
575 struct berval *cookie_bv= NULL;
576 BerElement *extdn_be = NULL;
577 struct berval *extdn_bv= NULL;
579 TALLOC_CTX *ctx;
580 ads_control *external_control = (ads_control *) args;
582 *res = NULL;
584 if (!(ctx = talloc_init("ads_do_paged_search_args")))
585 return ADS_ERROR(LDAP_NO_MEMORY);
587 /* 0 means the conversion worked but the result was empty
588 so we only fail if it's -1. In any case, it always
589 at least nulls out the dest */
590 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
591 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
592 rc = LDAP_NO_MEMORY;
593 goto done;
596 if (!attrs || !(*attrs))
597 search_attrs = NULL;
598 else {
599 /* This would be the utf8-encoded version...*/
600 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
601 if (!(str_list_copy(&search_attrs, attrs))) {
602 rc = LDAP_NO_MEMORY;
603 goto done;
608 /* Paged results only available on ldap v3 or later */
609 ldap_get_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
610 if (version < LDAP_VERSION3) {
611 rc = LDAP_NOT_SUPPORTED;
612 goto done;
615 cookie_be = ber_alloc_t(LBER_USE_DER);
616 if (*cookie) {
617 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
618 ber_bvfree(*cookie); /* don't need it from last time */
619 *cookie = NULL;
620 } else {
621 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
623 ber_flatten(cookie_be, &cookie_bv);
624 PagedResults.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID);
625 PagedResults.ldctl_iscritical = (char) 1;
626 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
627 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
629 NoReferrals.ldctl_oid = CONST_DISCARD(char *, ADS_NO_REFERRALS_OID);
630 NoReferrals.ldctl_iscritical = (char) 0;
631 NoReferrals.ldctl_value.bv_len = 0;
632 NoReferrals.ldctl_value.bv_val = CONST_DISCARD(char *, "");
634 if (external_control && strequal(external_control->control, ADS_EXTENDED_DN_OID)) {
636 ExtendedDn.ldctl_oid = CONST_DISCARD(char *, external_control->control);
637 ExtendedDn.ldctl_iscritical = (char) external_control->critical;
639 /* win2k does not accept a ldctl_value beeing passed in */
641 if (external_control->val != 0) {
643 if ((extdn_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
644 rc = LDAP_NO_MEMORY;
645 goto done;
648 if ((ber_printf(extdn_be, "{i}", (ber_int_t) external_control->val)) == -1) {
649 rc = LDAP_NO_MEMORY;
650 goto done;
652 if ((ber_flatten(extdn_be, &extdn_bv)) == -1) {
653 rc = LDAP_NO_MEMORY;
654 goto done;
657 ExtendedDn.ldctl_value.bv_len = extdn_bv->bv_len;
658 ExtendedDn.ldctl_value.bv_val = extdn_bv->bv_val;
660 } else {
661 ExtendedDn.ldctl_value.bv_len = 0;
662 ExtendedDn.ldctl_value.bv_val = NULL;
665 controls[0] = &NoReferrals;
666 controls[1] = &PagedResults;
667 controls[2] = &ExtendedDn;
668 controls[3] = NULL;
670 } else {
671 controls[0] = &NoReferrals;
672 controls[1] = &PagedResults;
673 controls[2] = NULL;
676 /* we need to disable referrals as the openldap libs don't
677 handle them and paged results at the same time. Using them
678 together results in the result record containing the server
679 page control being removed from the result list (tridge/jmcd)
681 leaving this in despite the control that says don't generate
682 referrals, in case the server doesn't support it (jmcd)
684 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
686 rc = ldap_search_with_timeout(ads->ld, utf8_path, scope, utf8_expr,
687 search_attrs, 0, controls,
688 NULL, LDAP_NO_LIMIT,
689 (LDAPMessage **)res);
691 ber_free(cookie_be, 1);
692 ber_bvfree(cookie_bv);
694 if (rc) {
695 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
696 ldap_err2string(rc)));
697 goto done;
700 rc = ldap_parse_result(ads->ld, *res, NULL, NULL, NULL,
701 NULL, &rcontrols, 0);
703 if (!rcontrols) {
704 goto done;
707 for (i=0; rcontrols[i]; i++) {
708 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
709 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
710 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
711 &cookie_bv);
712 /* the berval is the cookie, but must be freed when
713 it is all done */
714 if (cookie_bv->bv_len) /* still more to do */
715 *cookie=ber_bvdup(cookie_bv);
716 else
717 *cookie=NULL;
718 ber_bvfree(cookie_bv);
719 ber_free(cookie_be, 1);
720 break;
723 ldap_controls_free(rcontrols);
725 done:
726 talloc_destroy(ctx);
728 if (extdn_be) {
729 ber_free(extdn_be, 1);
732 if (extdn_bv) {
733 ber_bvfree(extdn_bv);
736 /* if/when we decide to utf8-encode attrs, take out this next line */
737 str_list_free(&search_attrs);
739 return ADS_ERROR(rc);
742 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
743 int scope, const char *expr,
744 const char **attrs, LDAPMessage **res,
745 int *count, struct berval **cookie)
747 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
752 * Get all results for a search. This uses ads_do_paged_search() to return
753 * all entries in a large search.
754 * @param ads connection to ads server
755 * @param bind_path Base dn for the search
756 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
757 * @param expr Search expression
758 * @param attrs Attributes to retrieve
759 * @param res ** which will contain results - free res* with ads_msgfree()
760 * @return status of search
762 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
763 int scope, const char *expr,
764 const char **attrs, void *args,
765 LDAPMessage **res)
767 struct berval *cookie = NULL;
768 int count = 0;
769 ADS_STATUS status;
771 *res = NULL;
772 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
773 &count, &cookie);
775 if (!ADS_ERR_OK(status))
776 return status;
778 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
779 while (cookie) {
780 LDAPMessage *res2 = NULL;
781 ADS_STATUS status2;
782 LDAPMessage *msg, *next;
784 status2 = ads_do_paged_search_args(ads, bind_path, scope, expr,
785 attrs, args, &res2, &count, &cookie);
787 if (!ADS_ERR_OK(status2)) break;
789 /* this relies on the way that ldap_add_result_entry() works internally. I hope
790 that this works on all ldap libs, but I have only tested with openldap */
791 for (msg = ads_first_entry(ads, res2); msg; msg = next) {
792 next = ads_next_entry(ads, msg);
793 ldap_add_result_entry((LDAPMessage **)res, msg);
795 /* note that we do not free res2, as the memory is now
796 part of the main returned list */
798 #else
799 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
800 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
801 #endif
803 return status;
806 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
807 int scope, const char *expr,
808 const char **attrs, LDAPMessage **res)
810 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
814 * Run a function on all results for a search. Uses ads_do_paged_search() and
815 * runs the function as each page is returned, using ads_process_results()
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 - specified in local charset
820 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
821 * @param fn Function which takes attr name, values list, and data_area
822 * @param data_area Pointer which is passed to function on each call
823 * @return status of search
825 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
826 int scope, const char *expr, const char **attrs,
827 BOOL(*fn)(char *, void **, void *),
828 void *data_area)
830 struct berval *cookie = NULL;
831 int count = 0;
832 ADS_STATUS status;
833 LDAPMessage *res;
835 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
836 &count, &cookie);
838 if (!ADS_ERR_OK(status)) return status;
840 ads_process_results(ads, res, fn, data_area);
841 ads_msgfree(ads, res);
843 while (cookie) {
844 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
845 &res, &count, &cookie);
847 if (!ADS_ERR_OK(status)) break;
849 ads_process_results(ads, res, fn, data_area);
850 ads_msgfree(ads, res);
853 return status;
857 * Do a search with a timeout.
858 * @param ads connection to ads server
859 * @param bind_path Base dn for the search
860 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
861 * @param expr Search expression
862 * @param attrs Attributes to retrieve
863 * @param res ** which will contain results - free res* with ads_msgfree()
864 * @return status of search
866 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
867 const char *expr,
868 const char **attrs, LDAPMessage **res)
870 int rc;
871 char *utf8_expr, *utf8_path, **search_attrs = NULL;
872 TALLOC_CTX *ctx;
874 *res = NULL;
875 if (!(ctx = talloc_init("ads_do_search"))) {
876 DEBUG(1,("ads_do_search: talloc_init() failed!"));
877 return ADS_ERROR(LDAP_NO_MEMORY);
880 /* 0 means the conversion worked but the result was empty
881 so we only fail if it's negative. In any case, it always
882 at least nulls out the dest */
883 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
884 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
885 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
886 rc = LDAP_NO_MEMORY;
887 goto done;
890 if (!attrs || !(*attrs))
891 search_attrs = NULL;
892 else {
893 /* This would be the utf8-encoded version...*/
894 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
895 if (!(str_list_copy(&search_attrs, attrs)))
897 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
898 rc = LDAP_NO_MEMORY;
899 goto done;
903 /* see the note in ads_do_paged_search - we *must* disable referrals */
904 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
906 rc = ldap_search_with_timeout(ads->ld, utf8_path, scope, utf8_expr,
907 search_attrs, 0, NULL, NULL,
908 LDAP_NO_LIMIT,
909 (LDAPMessage **)res);
911 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
912 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
913 rc = 0;
916 done:
917 talloc_destroy(ctx);
918 /* if/when we decide to utf8-encode attrs, take out this next line */
919 str_list_free(&search_attrs);
920 return ADS_ERROR(rc);
923 * Do a general ADS search
924 * @param ads connection to ads server
925 * @param res ** which will contain results - free res* with ads_msgfree()
926 * @param expr Search expression
927 * @param attrs Attributes to retrieve
928 * @return status of search
930 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
931 const char *expr, const char **attrs)
933 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
934 expr, attrs, res);
938 * Do a search on a specific DistinguishedName
939 * @param ads connection to ads server
940 * @param res ** which will contain results - free res* with ads_msgfree()
941 * @param dn DistinguishName to search
942 * @param attrs Attributes to retrieve
943 * @return status of search
945 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
946 const char *dn, const char **attrs)
948 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
949 attrs, res);
953 * Free up memory from a ads_search
954 * @param ads connection to ads server
955 * @param msg Search results to free
957 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
959 if (!msg) return;
960 ldap_msgfree(msg);
964 * Free up memory from various ads requests
965 * @param ads connection to ads server
966 * @param mem Area to free
968 void ads_memfree(ADS_STRUCT *ads, void *mem)
970 SAFE_FREE(mem);
974 * Get a dn from search results
975 * @param ads connection to ads server
976 * @param msg Search result
977 * @return dn string
979 char *ads_get_dn(ADS_STRUCT *ads, LDAPMessage *msg)
981 char *utf8_dn, *unix_dn;
983 utf8_dn = ldap_get_dn(ads->ld, msg);
985 if (!utf8_dn) {
986 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
987 return NULL;
990 if (pull_utf8_allocate(&unix_dn, utf8_dn) == (size_t)-1) {
991 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
992 utf8_dn ));
993 return NULL;
995 ldap_memfree(utf8_dn);
996 return unix_dn;
1000 * Get the parent from a dn
1001 * @param dn the dn to return the parent from
1002 * @return parent dn string
1004 char *ads_parent_dn(const char *dn)
1006 char *p;
1008 if (dn == NULL) {
1009 return NULL;
1012 p = strchr(dn, ',');
1014 if (p == NULL) {
1015 return NULL;
1018 return p+1;
1022 * Find a machine account given a hostname
1023 * @param ads connection to ads server
1024 * @param res ** which will contain results - free res* with ads_msgfree()
1025 * @param host Hostname to search for
1026 * @return status of search
1028 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1029 const char *machine)
1031 ADS_STATUS status;
1032 char *expr;
1033 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1035 *res = NULL;
1037 /* the easiest way to find a machine account anywhere in the tree
1038 is to look for hostname$ */
1039 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1040 DEBUG(1, ("asprintf failed!\n"));
1041 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1044 status = ads_search(ads, res, expr, attrs);
1045 SAFE_FREE(expr);
1046 return status;
1050 * Initialize a list of mods to be used in a modify request
1051 * @param ctx An initialized TALLOC_CTX
1052 * @return allocated ADS_MODLIST
1054 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1056 #define ADS_MODLIST_ALLOC_SIZE 10
1057 LDAPMod **mods;
1059 if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1060 /* -1 is safety to make sure we don't go over the end.
1061 need to reset it to NULL before doing ldap modify */
1062 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1064 return (ADS_MODLIST)mods;
1069 add an attribute to the list, with values list already constructed
1071 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1072 int mod_op, const char *name,
1073 const void *_invals)
1075 const void **invals = (const void **)_invals;
1076 int curmod;
1077 LDAPMod **modlist = (LDAPMod **) *mods;
1078 struct berval **ber_values = NULL;
1079 char **char_values = NULL;
1081 if (!invals) {
1082 mod_op = LDAP_MOD_DELETE;
1083 } else {
1084 if (mod_op & LDAP_MOD_BVALUES)
1085 ber_values = ads_dup_values(ctx,
1086 (const struct berval **)invals);
1087 else
1088 char_values = ads_push_strvals(ctx,
1089 (const char **) invals);
1092 /* find the first empty slot */
1093 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1094 curmod++);
1095 if (modlist[curmod] == (LDAPMod *) -1) {
1096 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
1097 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1098 return ADS_ERROR(LDAP_NO_MEMORY);
1099 memset(&modlist[curmod], 0,
1100 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1101 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1102 *mods = (ADS_MODLIST)modlist;
1105 if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
1106 return ADS_ERROR(LDAP_NO_MEMORY);
1107 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1108 if (mod_op & LDAP_MOD_BVALUES) {
1109 modlist[curmod]->mod_bvalues = ber_values;
1110 } else if (mod_op & LDAP_MOD_DELETE) {
1111 modlist[curmod]->mod_values = NULL;
1112 } else {
1113 modlist[curmod]->mod_values = char_values;
1116 modlist[curmod]->mod_op = mod_op;
1117 return ADS_ERROR(LDAP_SUCCESS);
1121 * Add a single string value to a mod list
1122 * @param ctx An initialized TALLOC_CTX
1123 * @param mods An initialized ADS_MODLIST
1124 * @param name The attribute name to add
1125 * @param val The value to add - NULL means DELETE
1126 * @return ADS STATUS indicating success of add
1128 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1129 const char *name, const char *val)
1131 const char *values[2];
1133 values[0] = val;
1134 values[1] = NULL;
1136 if (!val)
1137 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1138 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1142 * Add an array of string values to a mod list
1143 * @param ctx An initialized TALLOC_CTX
1144 * @param mods An initialized ADS_MODLIST
1145 * @param name The attribute name to add
1146 * @param vals The array of string values to add - NULL means DELETE
1147 * @return ADS STATUS indicating success of add
1149 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1150 const char *name, const char **vals)
1152 if (!vals)
1153 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1154 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1155 name, (const void **) vals);
1158 #if 0
1160 * Add a single ber-encoded value to a mod list
1161 * @param ctx An initialized TALLOC_CTX
1162 * @param mods An initialized ADS_MODLIST
1163 * @param name The attribute name to add
1164 * @param val The value to add - NULL means DELETE
1165 * @return ADS STATUS indicating success of add
1167 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1168 const char *name, const struct berval *val)
1170 const struct berval *values[2];
1172 values[0] = val;
1173 values[1] = NULL;
1174 if (!val)
1175 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1176 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1177 name, (const void **) values);
1179 #endif
1182 * Perform an ldap modify
1183 * @param ads connection to ads server
1184 * @param mod_dn DistinguishedName to modify
1185 * @param mods list of modifications to perform
1186 * @return status of modify
1188 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1190 int ret,i;
1191 char *utf8_dn = NULL;
1193 this control is needed to modify that contains a currently
1194 non-existent attribute (but allowable for the object) to run
1196 LDAPControl PermitModify = {
1197 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
1198 {0, NULL},
1199 (char) 1};
1200 LDAPControl *controls[2];
1202 controls[0] = &PermitModify;
1203 controls[1] = NULL;
1205 if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) {
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;
1213 ret = ldap_modify_ext_s(ads->ld, utf8_dn,
1214 (LDAPMod **) mods, controls, NULL);
1215 SAFE_FREE(utf8_dn);
1216 return ADS_ERROR(ret);
1220 * Perform an ldap add
1221 * @param ads connection to ads server
1222 * @param new_dn DistinguishedName to add
1223 * @param mods list of attributes and values for DN
1224 * @return status of add
1226 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1228 int ret, i;
1229 char *utf8_dn = NULL;
1231 if (push_utf8_allocate(&utf8_dn, new_dn) == -1) {
1232 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1233 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1236 /* find the end of the list, marked by NULL or -1 */
1237 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1238 /* make sure the end of the list is NULL */
1239 mods[i] = NULL;
1241 ret = ldap_add_s(ads->ld, utf8_dn, (LDAPMod**)mods);
1242 SAFE_FREE(utf8_dn);
1243 return ADS_ERROR(ret);
1247 * Delete a DistinguishedName
1248 * @param ads connection to ads server
1249 * @param new_dn DistinguishedName to delete
1250 * @return status of delete
1252 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1254 int ret;
1255 char *utf8_dn = NULL;
1256 if (push_utf8_allocate(&utf8_dn, del_dn) == -1) {
1257 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1258 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1261 ret = ldap_delete_s(ads->ld, utf8_dn);
1262 SAFE_FREE(utf8_dn);
1263 return ADS_ERROR(ret);
1267 * Build an org unit string
1268 * if org unit is Computers or blank then assume a container, otherwise
1269 * assume a / separated list of organisational units.
1270 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1271 * @param ads connection to ads server
1272 * @param org_unit Organizational unit
1273 * @return org unit string - caller must free
1275 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1277 char *ret = NULL;
1279 if (!org_unit || !*org_unit) {
1281 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1283 /* samba4 might not yet respond to a wellknownobject-query */
1284 return ret ? ret : SMB_STRDUP("cn=Computers");
1287 if (strequal(org_unit, "Computers")) {
1288 return SMB_STRDUP("cn=Computers");
1291 /* jmcd: removed "\\" from the separation chars, because it is
1292 needed as an escape for chars like '#' which are valid in an
1293 OU name */
1294 return ads_build_path(org_unit, "/", "ou=", 1);
1298 * Get a org unit string for a well-known GUID
1299 * @param ads connection to ads server
1300 * @param wknguid Well known GUID
1301 * @return org unit string - caller must free
1303 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1305 ADS_STATUS status;
1306 LDAPMessage *res = NULL;
1307 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1308 **bind_dn_exp = NULL;
1309 const char *attrs[] = {"distinguishedName", NULL};
1310 int new_ln, wkn_ln, bind_ln, i;
1312 if (wknguid == NULL) {
1313 return NULL;
1316 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1317 DEBUG(1, ("asprintf failed!\n"));
1318 return NULL;
1321 status = ads_search_dn(ads, &res, base, attrs);
1322 if (!ADS_ERR_OK(status)) {
1323 DEBUG(1,("Failed while searching for: %s\n", base));
1324 goto out;
1327 if (ads_count_replies(ads, res) != 1) {
1328 goto out;
1331 /* substitute the bind-path from the well-known-guid-search result */
1332 wkn_dn = ads_get_dn(ads, res);
1333 if (!wkn_dn) {
1334 goto out;
1337 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1338 if (!wkn_dn_exp) {
1339 goto out;
1342 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1343 if (!bind_dn_exp) {
1344 goto out;
1347 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1349 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1352 new_ln = wkn_ln - bind_ln;
1354 ret = SMB_STRDUP(wkn_dn_exp[0]);
1355 if (!ret) {
1356 goto out;
1359 for (i=1; i < new_ln; i++) {
1360 char *s = NULL;
1362 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1363 SAFE_FREE(ret);
1364 goto out;
1367 SAFE_FREE(ret);
1368 ret = SMB_STRDUP(s);
1369 free(s);
1370 if (!ret) {
1371 goto out;
1375 out:
1376 SAFE_FREE(base);
1377 ads_msgfree(ads, res);
1378 ads_memfree(ads, wkn_dn);
1379 if (wkn_dn_exp) {
1380 ldap_value_free(wkn_dn_exp);
1382 if (bind_dn_exp) {
1383 ldap_value_free(bind_dn_exp);
1386 return ret;
1390 * Adds (appends) an item to an attribute array, rather then
1391 * replacing the whole list
1392 * @param ctx An initialized TALLOC_CTX
1393 * @param mods An initialized ADS_MODLIST
1394 * @param name name of the ldap attribute to append to
1395 * @param vals an array of values to add
1396 * @return status of addition
1399 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1400 const char *name, const char **vals)
1402 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1403 (const void *) vals);
1407 * Determines the computer account's current KVNO via an LDAP lookup
1408 * @param ads An initialized ADS_STRUCT
1409 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1410 * @return the kvno for the computer account, or -1 in case of a failure.
1413 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *machine_name)
1415 LDAPMessage *res = NULL;
1416 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1417 char *filter;
1418 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1419 char *dn_string = NULL;
1420 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1422 DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name));
1423 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1424 return kvno;
1426 ret = ads_search(ads, &res, filter, attrs);
1427 SAFE_FREE(filter);
1428 if (!ADS_ERR_OK(ret) && ads_count_replies(ads, res)) {
1429 DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name));
1430 ads_msgfree(ads, res);
1431 return kvno;
1434 dn_string = ads_get_dn(ads, res);
1435 if (!dn_string) {
1436 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1437 ads_msgfree(ads, res);
1438 return kvno;
1440 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1441 ads_memfree(ads, dn_string);
1443 /* ---------------------------------------------------------
1444 * 0 is returned as a default KVNO from this point on...
1445 * This is done because Windows 2000 does not support key
1446 * version numbers. Chances are that a failure in the next
1447 * step is simply due to Windows 2000 being used for a
1448 * domain controller. */
1449 kvno = 0;
1451 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1452 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1453 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1454 ads_msgfree(ads, res);
1455 return kvno;
1458 /* Success */
1459 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1460 ads_msgfree(ads, res);
1461 return kvno;
1465 * This clears out all registered spn's for a given hostname
1466 * @param ads An initilaized ADS_STRUCT
1467 * @param machine_name the NetBIOS name of the computer.
1468 * @return 0 upon success, non-zero otherwise.
1471 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1473 TALLOC_CTX *ctx;
1474 LDAPMessage *res = NULL;
1475 ADS_MODLIST mods;
1476 const char *servicePrincipalName[1] = {NULL};
1477 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1478 char *dn_string = NULL;
1480 ret = ads_find_machine_acct(ads, &res, machine_name);
1481 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1482 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1483 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1484 ads_msgfree(ads, res);
1485 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1488 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1489 ctx = talloc_init("ads_clear_service_principal_names");
1490 if (!ctx) {
1491 ads_msgfree(ads, res);
1492 return ADS_ERROR(LDAP_NO_MEMORY);
1495 if (!(mods = ads_init_mods(ctx))) {
1496 talloc_destroy(ctx);
1497 ads_msgfree(ads, res);
1498 return ADS_ERROR(LDAP_NO_MEMORY);
1500 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1501 if (!ADS_ERR_OK(ret)) {
1502 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1503 ads_msgfree(ads, res);
1504 talloc_destroy(ctx);
1505 return ret;
1507 dn_string = ads_get_dn(ads, res);
1508 if (!dn_string) {
1509 talloc_destroy(ctx);
1510 ads_msgfree(ads, res);
1511 return ADS_ERROR(LDAP_NO_MEMORY);
1513 ret = ads_gen_mod(ads, dn_string, mods);
1514 ads_memfree(ads,dn_string);
1515 if (!ADS_ERR_OK(ret)) {
1516 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1517 machine_name));
1518 ads_msgfree(ads, res);
1519 talloc_destroy(ctx);
1520 return ret;
1523 ads_msgfree(ads, res);
1524 talloc_destroy(ctx);
1525 return ret;
1529 * This adds a service principal name to an existing computer account
1530 * (found by hostname) in AD.
1531 * @param ads An initialized ADS_STRUCT
1532 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1533 * @param my_fqdn The fully qualified DNS name of the machine
1534 * @param spn A string of the service principal to add, i.e. 'host'
1535 * @return 0 upon sucess, or non-zero if a failure occurs
1538 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1539 const char *my_fqdn, const char *spn)
1541 ADS_STATUS ret;
1542 TALLOC_CTX *ctx;
1543 LDAPMessage *res = NULL;
1544 char *psp1, *psp2;
1545 ADS_MODLIST mods;
1546 char *dn_string = NULL;
1547 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1549 ret = ads_find_machine_acct(ads, &res, machine_name);
1550 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1551 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1552 machine_name));
1553 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1554 spn, machine_name, ads->config.realm));
1555 ads_msgfree(ads, res);
1556 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1559 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1560 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1561 ads_msgfree(ads, res);
1562 return ADS_ERROR(LDAP_NO_MEMORY);
1565 /* add short name spn */
1567 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1568 talloc_destroy(ctx);
1569 ads_msgfree(ads, res);
1570 return ADS_ERROR(LDAP_NO_MEMORY);
1572 strupper_m(psp1);
1573 strlower_m(&psp1[strlen(spn)]);
1574 servicePrincipalName[0] = psp1;
1576 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1577 psp1, machine_name));
1580 /* add fully qualified spn */
1582 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1583 ret = ADS_ERROR(LDAP_NO_MEMORY);
1584 goto out;
1586 strupper_m(psp2);
1587 strlower_m(&psp2[strlen(spn)]);
1588 servicePrincipalName[1] = psp2;
1590 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1591 psp2, machine_name));
1593 if ( (mods = ads_init_mods(ctx)) == NULL ) {
1594 ret = ADS_ERROR(LDAP_NO_MEMORY);
1595 goto out;
1598 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1599 if (!ADS_ERR_OK(ret)) {
1600 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1601 goto out;
1604 if ( (dn_string = ads_get_dn(ads, res)) == NULL ) {
1605 ret = ADS_ERROR(LDAP_NO_MEMORY);
1606 goto out;
1609 ret = ads_gen_mod(ads, dn_string, mods);
1610 ads_memfree(ads,dn_string);
1611 if (!ADS_ERR_OK(ret)) {
1612 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1613 goto out;
1616 out:
1617 TALLOC_FREE( ctx );
1618 ads_msgfree(ads, res);
1619 return ret;
1623 * adds a machine account to the ADS server
1624 * @param ads An intialized ADS_STRUCT
1625 * @param machine_name - the NetBIOS machine name of this account.
1626 * @param account_type A number indicating the type of account to create
1627 * @param org_unit The LDAP path in which to place this account
1628 * @return 0 upon success, or non-zero otherwise
1631 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1632 const char *org_unit)
1634 ADS_STATUS ret;
1635 char *samAccountName, *controlstr;
1636 TALLOC_CTX *ctx;
1637 ADS_MODLIST mods;
1638 char *machine_escaped = NULL;
1639 char *new_dn;
1640 const char *objectClass[] = {"top", "person", "organizationalPerson",
1641 "user", "computer", NULL};
1642 LDAPMessage *res = NULL;
1643 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
1644 UF_DONT_EXPIRE_PASSWD |\
1645 UF_ACCOUNTDISABLE );
1647 if (!(ctx = talloc_init("ads_add_machine_acct")))
1648 return ADS_ERROR(LDAP_NO_MEMORY);
1650 ret = ADS_ERROR(LDAP_NO_MEMORY);
1652 machine_escaped = escape_rdn_val_string_alloc(machine_name);
1653 if (!machine_escaped) {
1654 goto done;
1657 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
1658 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
1660 if ( !new_dn || !samAccountName ) {
1661 goto done;
1664 #ifndef ENCTYPE_ARCFOUR_HMAC
1665 acct_control |= UF_USE_DES_KEY_ONLY;
1666 #endif
1668 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
1669 goto done;
1672 if (!(mods = ads_init_mods(ctx))) {
1673 goto done;
1676 ads_mod_str(ctx, &mods, "cn", machine_name);
1677 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1678 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1679 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1681 ret = ads_gen_add(ads, new_dn, mods);
1683 done:
1684 SAFE_FREE(machine_escaped);
1685 ads_msgfree(ads, res);
1686 talloc_destroy(ctx);
1688 return ret;
1692 * move a machine account to another OU on the ADS server
1693 * @param ads - An intialized ADS_STRUCT
1694 * @param machine_name - the NetBIOS machine name of this account.
1695 * @param org_unit - The LDAP path in which to place this account
1696 * @param moved - whether we moved the machine account (optional)
1697 * @return 0 upon success, or non-zero otherwise
1700 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1701 const char *org_unit, BOOL *moved)
1703 ADS_STATUS rc;
1704 int ldap_status;
1705 LDAPMessage *res = NULL;
1706 char *filter = NULL;
1707 char *computer_dn = NULL;
1708 char *parent_dn;
1709 char *computer_rdn = NULL;
1710 BOOL need_move = False;
1712 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1713 rc = ADS_ERROR(LDAP_NO_MEMORY);
1714 goto done;
1717 /* Find pre-existing machine */
1718 rc = ads_search(ads, &res, filter, NULL);
1719 if (!ADS_ERR_OK(rc)) {
1720 goto done;
1723 computer_dn = ads_get_dn(ads, res);
1724 if (!computer_dn) {
1725 rc = ADS_ERROR(LDAP_NO_MEMORY);
1726 goto done;
1729 parent_dn = ads_parent_dn(computer_dn);
1730 if (strequal(parent_dn, org_unit)) {
1731 goto done;
1734 need_move = True;
1736 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
1737 rc = ADS_ERROR(LDAP_NO_MEMORY);
1738 goto done;
1741 ldap_status = ldap_rename2_s(ads->ld, computer_dn, computer_rdn, org_unit, 1);
1742 rc = ADS_ERROR(ldap_status);
1744 done:
1745 ads_msgfree(ads, res);
1746 SAFE_FREE(filter);
1747 SAFE_FREE(computer_dn);
1748 SAFE_FREE(computer_rdn);
1750 if (!ADS_ERR_OK(rc)) {
1751 need_move = False;
1754 if (moved) {
1755 *moved = need_move;
1758 return rc;
1762 dump a binary result from ldap
1764 static void dump_binary(const char *field, struct berval **values)
1766 int i, j;
1767 for (i=0; values[i]; i++) {
1768 printf("%s: ", field);
1769 for (j=0; j<values[i]->bv_len; j++) {
1770 printf("%02X", (unsigned char)values[i]->bv_val[j]);
1772 printf("\n");
1776 static void dump_guid(const char *field, struct berval **values)
1778 int i;
1779 UUID_FLAT guid;
1780 for (i=0; values[i]; i++) {
1781 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1782 printf("%s: %s\n", field,
1783 smb_uuid_string_static(smb_uuid_unpack_static(guid)));
1788 dump a sid result from ldap
1790 static void dump_sid(const char *field, struct berval **values)
1792 int i;
1793 for (i=0; values[i]; i++) {
1794 DOM_SID sid;
1795 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1796 printf("%s: %s\n", field, sid_string_static(&sid));
1801 dump ntSecurityDescriptor
1803 static void dump_sd(const char *filed, struct berval **values)
1805 prs_struct ps;
1807 SEC_DESC *psd = 0;
1808 TALLOC_CTX *ctx = 0;
1810 if (!(ctx = talloc_init("sec_io_desc")))
1811 return;
1813 /* prepare data */
1814 prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
1815 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
1816 prs_set_offset(&ps,0);
1818 /* parse secdesc */
1819 if (!sec_io_desc("sd", &psd, &ps, 1)) {
1820 prs_mem_free(&ps);
1821 talloc_destroy(ctx);
1822 return;
1824 if (psd) ads_disp_sd(psd);
1826 prs_mem_free(&ps);
1827 talloc_destroy(ctx);
1831 dump a string result from ldap
1833 static void dump_string(const char *field, char **values)
1835 int i;
1836 for (i=0; values[i]; i++) {
1837 printf("%s: %s\n", field, values[i]);
1842 dump a field from LDAP on stdout
1843 used for debugging
1846 static BOOL ads_dump_field(char *field, void **values, void *data_area)
1848 const struct {
1849 const char *name;
1850 BOOL string;
1851 void (*handler)(const char *, struct berval **);
1852 } handlers[] = {
1853 {"objectGUID", False, dump_guid},
1854 {"netbootGUID", False, dump_guid},
1855 {"nTSecurityDescriptor", False, dump_sd},
1856 {"dnsRecord", False, dump_binary},
1857 {"objectSid", False, dump_sid},
1858 {"tokenGroups", False, dump_sid},
1859 {"tokenGroupsNoGCAcceptable", False, dump_sid},
1860 {"tokengroupsGlobalandUniversal", False, dump_sid},
1861 {"mS-DS-CreatorSID", False, dump_sid},
1862 {NULL, True, NULL}
1864 int i;
1866 if (!field) { /* must be end of an entry */
1867 printf("\n");
1868 return False;
1871 for (i=0; handlers[i].name; i++) {
1872 if (StrCaseCmp(handlers[i].name, field) == 0) {
1873 if (!values) /* first time, indicate string or not */
1874 return handlers[i].string;
1875 handlers[i].handler(field, (struct berval **) values);
1876 break;
1879 if (!handlers[i].name) {
1880 if (!values) /* first time, indicate string conversion */
1881 return True;
1882 dump_string(field, (char **)values);
1884 return False;
1888 * Dump a result from LDAP on stdout
1889 * used for debugging
1890 * @param ads connection to ads server
1891 * @param res Results to dump
1894 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
1896 ads_process_results(ads, res, ads_dump_field, NULL);
1900 * Walk through results, calling a function for each entry found.
1901 * The function receives a field name, a berval * array of values,
1902 * and a data area passed through from the start. The function is
1903 * called once with null for field and values at the end of each
1904 * entry.
1905 * @param ads connection to ads server
1906 * @param res Results to process
1907 * @param fn Function for processing each result
1908 * @param data_area user-defined area to pass to function
1910 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
1911 BOOL(*fn)(char *, void **, void *),
1912 void *data_area)
1914 LDAPMessage *msg;
1915 TALLOC_CTX *ctx;
1917 if (!(ctx = talloc_init("ads_process_results")))
1918 return;
1920 for (msg = ads_first_entry(ads, res); msg;
1921 msg = ads_next_entry(ads, msg)) {
1922 char *utf8_field;
1923 BerElement *b;
1925 for (utf8_field=ldap_first_attribute(ads->ld,
1926 (LDAPMessage *)msg,&b);
1927 utf8_field;
1928 utf8_field=ldap_next_attribute(ads->ld,
1929 (LDAPMessage *)msg,b)) {
1930 struct berval **ber_vals;
1931 char **str_vals, **utf8_vals;
1932 char *field;
1933 BOOL string;
1935 pull_utf8_talloc(ctx, &field, utf8_field);
1936 string = fn(field, NULL, data_area);
1938 if (string) {
1939 utf8_vals = ldap_get_values(ads->ld,
1940 (LDAPMessage *)msg, field);
1941 str_vals = ads_pull_strvals(ctx,
1942 (const char **) utf8_vals);
1943 fn(field, (void **) str_vals, data_area);
1944 ldap_value_free(utf8_vals);
1945 } else {
1946 ber_vals = ldap_get_values_len(ads->ld,
1947 (LDAPMessage *)msg, field);
1948 fn(field, (void **) ber_vals, data_area);
1950 ldap_value_free_len(ber_vals);
1952 ldap_memfree(utf8_field);
1954 ber_free(b, 0);
1955 talloc_free_children(ctx);
1956 fn(NULL, NULL, data_area); /* completed an entry */
1959 talloc_destroy(ctx);
1963 * count how many replies are in a LDAPMessage
1964 * @param ads connection to ads server
1965 * @param res Results to count
1966 * @return number of replies
1968 int ads_count_replies(ADS_STRUCT *ads, void *res)
1970 return ldap_count_entries(ads->ld, (LDAPMessage *)res);
1974 * pull the first entry from a ADS result
1975 * @param ads connection to ads server
1976 * @param res Results of search
1977 * @return first entry from result
1979 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
1981 return ldap_first_entry(ads->ld, res);
1985 * pull the next entry from a ADS result
1986 * @param ads connection to ads server
1987 * @param res Results of search
1988 * @return next entry from result
1990 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
1992 return ldap_next_entry(ads->ld, res);
1996 * pull a single string from a ADS result
1997 * @param ads connection to ads server
1998 * @param mem_ctx TALLOC_CTX to use for allocating result string
1999 * @param msg Results of search
2000 * @param field Attribute to retrieve
2001 * @return Result string in talloc context
2003 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2004 const char *field)
2006 char **values;
2007 char *ret = NULL;
2008 char *ux_string;
2009 size_t rc;
2011 values = ldap_get_values(ads->ld, msg, field);
2012 if (!values)
2013 return NULL;
2015 if (values[0]) {
2016 rc = pull_utf8_talloc(mem_ctx, &ux_string,
2017 values[0]);
2018 if (rc != (size_t)-1)
2019 ret = ux_string;
2022 ldap_value_free(values);
2023 return ret;
2027 * pull an array of strings from a ADS result
2028 * @param ads connection to ads server
2029 * @param mem_ctx TALLOC_CTX to use for allocating result string
2030 * @param msg Results of search
2031 * @param field Attribute to retrieve
2032 * @return Result strings in talloc context
2034 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2035 LDAPMessage *msg, const char *field,
2036 size_t *num_values)
2038 char **values;
2039 char **ret = NULL;
2040 int i;
2042 values = ldap_get_values(ads->ld, msg, field);
2043 if (!values)
2044 return NULL;
2046 *num_values = ldap_count_values(values);
2048 ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
2049 if (!ret) {
2050 ldap_value_free(values);
2051 return NULL;
2054 for (i=0;i<*num_values;i++) {
2055 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
2056 ldap_value_free(values);
2057 return NULL;
2060 ret[i] = NULL;
2062 ldap_value_free(values);
2063 return ret;
2067 * pull an array of strings from a ADS result
2068 * (handle large multivalue attributes with range retrieval)
2069 * @param ads connection to ads server
2070 * @param mem_ctx TALLOC_CTX to use for allocating result string
2071 * @param msg Results of search
2072 * @param field Attribute to retrieve
2073 * @param current_strings strings returned by a previous call to this function
2074 * @param next_attribute The next query should ask for this attribute
2075 * @param num_values How many values did we get this time?
2076 * @param more_values Are there more values to get?
2077 * @return Result strings in talloc context
2079 char **ads_pull_strings_range(ADS_STRUCT *ads,
2080 TALLOC_CTX *mem_ctx,
2081 LDAPMessage *msg, const char *field,
2082 char **current_strings,
2083 const char **next_attribute,
2084 size_t *num_strings,
2085 BOOL *more_strings)
2087 char *attr;
2088 char *expected_range_attrib, *range_attr;
2089 BerElement *ptr = NULL;
2090 char **strings;
2091 char **new_strings;
2092 size_t num_new_strings;
2093 unsigned long int range_start;
2094 unsigned long int range_end;
2096 /* we might have been given the whole lot anyway */
2097 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2098 *more_strings = False;
2099 return strings;
2102 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2104 /* look for Range result */
2105 for (attr = ldap_first_attribute(ads->ld, (LDAPMessage *)msg, &ptr);
2106 attr;
2107 attr = ldap_next_attribute(ads->ld, (LDAPMessage *)msg, ptr)) {
2108 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2109 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2110 range_attr = attr;
2111 break;
2113 ldap_memfree(attr);
2115 if (!attr) {
2116 ber_free(ptr, 0);
2117 /* nothing here - this field is just empty */
2118 *more_strings = False;
2119 return NULL;
2122 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2123 &range_start, &range_end) == 2) {
2124 *more_strings = True;
2125 } else {
2126 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2127 &range_start) == 1) {
2128 *more_strings = False;
2129 } else {
2130 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2131 range_attr));
2132 ldap_memfree(range_attr);
2133 *more_strings = False;
2134 return NULL;
2138 if ((*num_strings) != range_start) {
2139 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2140 " - aborting range retreival\n",
2141 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2142 ldap_memfree(range_attr);
2143 *more_strings = False;
2144 return NULL;
2147 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2149 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2150 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2151 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2152 range_attr, (unsigned long int)range_end - range_start + 1,
2153 (unsigned long int)num_new_strings));
2154 ldap_memfree(range_attr);
2155 *more_strings = False;
2156 return NULL;
2159 strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2160 *num_strings + num_new_strings);
2162 if (strings == NULL) {
2163 ldap_memfree(range_attr);
2164 *more_strings = False;
2165 return NULL;
2168 if (new_strings && num_new_strings) {
2169 memcpy(&strings[*num_strings], new_strings,
2170 sizeof(*new_strings) * num_new_strings);
2173 (*num_strings) += num_new_strings;
2175 if (*more_strings) {
2176 *next_attribute = talloc_asprintf(mem_ctx,
2177 "%s;range=%d-*",
2178 field,
2179 (int)*num_strings);
2181 if (!*next_attribute) {
2182 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2183 ldap_memfree(range_attr);
2184 *more_strings = False;
2185 return NULL;
2189 ldap_memfree(range_attr);
2191 return strings;
2195 * pull a single uint32 from a ADS result
2196 * @param ads connection to ads server
2197 * @param msg Results of search
2198 * @param field Attribute to retrieve
2199 * @param v Pointer to int to store result
2200 * @return boolean inidicating success
2202 BOOL ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2203 uint32 *v)
2205 char **values;
2207 values = ldap_get_values(ads->ld, msg, field);
2208 if (!values)
2209 return False;
2210 if (!values[0]) {
2211 ldap_value_free(values);
2212 return False;
2215 *v = atoi(values[0]);
2216 ldap_value_free(values);
2217 return True;
2221 * pull a single objectGUID from an ADS result
2222 * @param ads connection to ADS server
2223 * @param msg results of search
2224 * @param guid 37-byte area to receive text guid
2225 * @return boolean indicating success
2227 BOOL ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2229 char **values;
2230 UUID_FLAT flat_guid;
2232 values = ldap_get_values(ads->ld, msg, "objectGUID");
2233 if (!values)
2234 return False;
2236 if (values[0]) {
2237 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2238 smb_uuid_unpack(flat_guid, guid);
2239 ldap_value_free(values);
2240 return True;
2242 ldap_value_free(values);
2243 return False;
2249 * pull a single DOM_SID from a ADS result
2250 * @param ads connection to ads server
2251 * @param msg Results of search
2252 * @param field Attribute to retrieve
2253 * @param sid Pointer to sid to store result
2254 * @return boolean inidicating success
2256 BOOL ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2257 DOM_SID *sid)
2259 struct berval **values;
2260 BOOL ret = False;
2262 values = ldap_get_values_len(ads->ld, msg, field);
2264 if (!values)
2265 return False;
2267 if (values[0])
2268 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2270 ldap_value_free_len(values);
2271 return ret;
2275 * pull an array of DOM_SIDs from a ADS result
2276 * @param ads connection to ads server
2277 * @param mem_ctx TALLOC_CTX for allocating sid array
2278 * @param msg Results of search
2279 * @param field Attribute to retrieve
2280 * @param sids pointer to sid array to allocate
2281 * @return the count of SIDs pulled
2283 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2284 LDAPMessage *msg, const char *field, DOM_SID **sids)
2286 struct berval **values;
2287 BOOL ret;
2288 int count, i;
2290 values = ldap_get_values_len(ads->ld, msg, field);
2292 if (!values)
2293 return 0;
2295 for (i=0; values[i]; i++)
2296 /* nop */ ;
2298 if (i) {
2299 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2300 if (!(*sids)) {
2301 ldap_value_free_len(values);
2302 return 0;
2304 } else {
2305 (*sids) = NULL;
2308 count = 0;
2309 for (i=0; values[i]; i++) {
2310 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2311 if (ret) {
2312 fstring sid;
2313 DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid, &(*sids)[count])));
2314 count++;
2318 ldap_value_free_len(values);
2319 return count;
2323 * pull a SEC_DESC from a ADS result
2324 * @param ads connection to ads server
2325 * @param mem_ctx TALLOC_CTX for allocating sid array
2326 * @param msg Results of search
2327 * @param field Attribute to retrieve
2328 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2329 * @return boolean inidicating success
2331 BOOL ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2332 LDAPMessage *msg, const char *field, SEC_DESC **sd)
2334 struct berval **values;
2335 BOOL ret = False;
2337 values = ldap_get_values_len(ads->ld, msg, field);
2339 if (!values) return False;
2341 if (values[0]) {
2342 prs_struct ps;
2343 prs_init(&ps, values[0]->bv_len, mem_ctx, UNMARSHALL);
2344 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
2345 prs_set_offset(&ps,0);
2347 ret = sec_io_desc("sd", sd, &ps, 1);
2348 prs_mem_free(&ps);
2351 ldap_value_free_len(values);
2352 return ret;
2356 * in order to support usernames longer than 21 characters we need to
2357 * use both the sAMAccountName and the userPrincipalName attributes
2358 * It seems that not all users have the userPrincipalName attribute set
2360 * @param ads connection to ads server
2361 * @param mem_ctx TALLOC_CTX for allocating sid array
2362 * @param msg Results of search
2363 * @return the username
2365 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2366 LDAPMessage *msg)
2368 #if 0 /* JERRY */
2369 char *ret, *p;
2371 /* lookup_name() only works on the sAMAccountName to
2372 returning the username portion of userPrincipalName
2373 breaks winbindd_getpwnam() */
2375 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2376 if (ret && (p = strchr_m(ret, '@'))) {
2377 *p = 0;
2378 return ret;
2380 #endif
2381 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2386 * find the update serial number - this is the core of the ldap cache
2387 * @param ads connection to ads server
2388 * @param ads connection to ADS server
2389 * @param usn Pointer to retrieved update serial number
2390 * @return status of search
2392 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2394 const char *attrs[] = {"highestCommittedUSN", NULL};
2395 ADS_STATUS status;
2396 LDAPMessage *res;
2398 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2399 if (!ADS_ERR_OK(status))
2400 return status;
2402 if (ads_count_replies(ads, res) != 1) {
2403 ads_msgfree(ads, res);
2404 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2407 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2408 ads_msgfree(ads, res);
2409 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2412 ads_msgfree(ads, res);
2413 return ADS_SUCCESS;
2416 /* parse a ADS timestring - typical string is
2417 '20020917091222.0Z0' which means 09:12.22 17th September
2418 2002, timezone 0 */
2419 static time_t ads_parse_time(const char *str)
2421 struct tm tm;
2423 ZERO_STRUCT(tm);
2425 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2426 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2427 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2428 return 0;
2430 tm.tm_year -= 1900;
2431 tm.tm_mon -= 1;
2433 return timegm(&tm);
2436 /********************************************************************
2437 ********************************************************************/
2439 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2441 const char *attrs[] = {"currentTime", NULL};
2442 ADS_STATUS status;
2443 LDAPMessage *res;
2444 char *timestr;
2445 TALLOC_CTX *ctx;
2446 ADS_STRUCT *ads_s = ads;
2448 if (!(ctx = talloc_init("ads_current_time"))) {
2449 return ADS_ERROR(LDAP_NO_MEMORY);
2452 /* establish a new ldap tcp session if necessary */
2454 if ( !ads->ld ) {
2455 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2456 ads->server.ldap_server )) == NULL )
2458 goto done;
2460 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2461 status = ads_connect( ads_s );
2462 if ( !ADS_ERR_OK(status))
2463 goto done;
2466 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2467 if (!ADS_ERR_OK(status)) {
2468 goto done;
2471 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2472 if (!timestr) {
2473 ads_msgfree(ads_s, res);
2474 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2475 goto done;
2478 /* but save the time and offset in the original ADS_STRUCT */
2480 ads->config.current_time = ads_parse_time(timestr);
2482 if (ads->config.current_time != 0) {
2483 ads->auth.time_offset = ads->config.current_time - time(NULL);
2484 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2487 ads_msgfree(ads, res);
2489 status = ADS_SUCCESS;
2491 done:
2492 /* free any temporary ads connections */
2493 if ( ads_s != ads ) {
2494 ads_destroy( &ads_s );
2496 talloc_destroy(ctx);
2498 return status;
2501 /********************************************************************
2502 ********************************************************************/
2504 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2506 const char *attrs[] = {"domainFunctionality", NULL};
2507 ADS_STATUS status;
2508 LDAPMessage *res;
2509 ADS_STRUCT *ads_s = ads;
2511 *val = DS_DOMAIN_FUNCTION_2000;
2513 /* establish a new ldap tcp session if necessary */
2515 if ( !ads->ld ) {
2516 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2517 ads->server.ldap_server )) == NULL )
2519 goto done;
2521 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2522 status = ads_connect( ads_s );
2523 if ( !ADS_ERR_OK(status))
2524 goto done;
2527 /* If the attribute does not exist assume it is a Windows 2000
2528 functional domain */
2530 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2531 if (!ADS_ERR_OK(status)) {
2532 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2533 status = ADS_SUCCESS;
2535 goto done;
2538 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2539 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2541 DEBUG(3,("ads_domain_func_level: %d\n", *val));
2544 ads_msgfree(ads, res);
2546 done:
2547 /* free any temporary ads connections */
2548 if ( ads_s != ads ) {
2549 ads_destroy( &ads_s );
2552 return status;
2556 * find the domain sid for our domain
2557 * @param ads connection to ads server
2558 * @param sid Pointer to domain sid
2559 * @return status of search
2561 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2563 const char *attrs[] = {"objectSid", NULL};
2564 LDAPMessage *res;
2565 ADS_STATUS rc;
2567 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2568 attrs, &res);
2569 if (!ADS_ERR_OK(rc)) return rc;
2570 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2571 ads_msgfree(ads, res);
2572 return ADS_ERROR_SYSTEM(ENOENT);
2574 ads_msgfree(ads, res);
2576 return ADS_SUCCESS;
2580 * find our site name
2581 * @param ads connection to ads server
2582 * @param mem_ctx Pointer to talloc context
2583 * @param site_name Pointer to the sitename
2584 * @return status of search
2586 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2588 ADS_STATUS status;
2589 LDAPMessage *res;
2590 const char *dn, *service_name;
2591 const char *attrs[] = { "dsServiceName", NULL };
2593 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2594 if (!ADS_ERR_OK(status)) {
2595 return status;
2598 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2599 if (service_name == NULL) {
2600 ads_msgfree(ads, res);
2601 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2604 ads_msgfree(ads, res);
2606 /* go up three levels */
2607 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2608 if (dn == NULL) {
2609 return ADS_ERROR(LDAP_NO_MEMORY);
2612 *site_name = talloc_strdup(mem_ctx, dn);
2613 if (*site_name == NULL) {
2614 return ADS_ERROR(LDAP_NO_MEMORY);
2617 return status;
2619 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2624 * find the site dn where a machine resides
2625 * @param ads connection to ads server
2626 * @param mem_ctx Pointer to talloc context
2627 * @param computer_name name of the machine
2628 * @param site_name Pointer to the sitename
2629 * @return status of search
2631 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
2633 ADS_STATUS status;
2634 LDAPMessage *res;
2635 const char *parent, *config_context, *filter;
2636 const char *attrs[] = { "configurationNamingContext", NULL };
2637 char *dn;
2639 /* shortcut a query */
2640 if (strequal(computer_name, ads->config.ldap_server_name)) {
2641 return ads_site_dn(ads, mem_ctx, site_dn);
2644 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2645 if (!ADS_ERR_OK(status)) {
2646 return status;
2649 config_context = ads_pull_string(ads, mem_ctx, res, "configurationNamingContext");
2650 if (config_context == NULL) {
2651 ads_msgfree(ads, res);
2652 return ADS_ERROR(LDAP_NO_MEMORY);
2655 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
2656 if (filter == NULL) {
2657 ads_msgfree(ads, res);
2658 return ADS_ERROR(LDAP_NO_MEMORY);
2661 ads_msgfree(ads, res);
2663 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE, filter, NULL, &res);
2664 if (!ADS_ERR_OK(status)) {
2665 return status;
2668 if (ads_count_replies(ads, res) != 1) {
2669 ads_msgfree(ads, res);
2670 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2673 dn = ads_get_dn(ads, res);
2674 if (dn == NULL) {
2675 ads_msgfree(ads, res);
2676 return ADS_ERROR(LDAP_NO_MEMORY);
2679 /* go up three levels */
2680 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
2681 if (parent == NULL) {
2682 ads_msgfree(ads, res);
2683 ads_memfree(ads, dn);
2684 return ADS_ERROR(LDAP_NO_MEMORY);
2687 *site_dn = talloc_strdup(mem_ctx, parent);
2688 if (*site_dn == NULL) {
2689 ads_msgfree(ads, res);
2690 ads_memfree(ads, dn);
2691 ADS_ERROR(LDAP_NO_MEMORY);
2694 ads_memfree(ads, dn);
2695 ads_msgfree(ads, res);
2697 return status;
2701 * get the upn suffixes for a domain
2702 * @param ads connection to ads server
2703 * @param mem_ctx Pointer to talloc context
2704 * @param suffixes Pointer to an array of suffixes
2705 * @param num_suffixes Pointer to the number of suffixes
2706 * @return status of search
2708 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
2710 ADS_STATUS status;
2711 LDAPMessage *res;
2712 const char *config_context, *base;
2713 const char *attrs[] = { "configurationNamingContext", NULL };
2714 const char *attrs2[] = { "uPNSuffixes", NULL };
2716 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2717 if (!ADS_ERR_OK(status)) {
2718 return status;
2721 config_context = ads_pull_string(ads, mem_ctx, res, "configurationNamingContext");
2722 if (config_context == NULL) {
2723 ads_msgfree(ads, res);
2724 return ADS_ERROR(LDAP_NO_MEMORY);
2727 ads_msgfree(ads, res);
2729 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
2730 if (base == NULL) {
2731 return ADS_ERROR(LDAP_NO_MEMORY);
2734 status = ads_search_dn(ads, &res, base, attrs2);
2735 if (!ADS_ERR_OK(status)) {
2736 return status;
2739 if (ads_count_replies(ads, res) != 1) {
2740 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2743 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
2744 if ((*suffixes) == NULL) {
2745 ads_msgfree(ads, res);
2746 return ADS_ERROR(LDAP_NO_MEMORY);
2749 ads_msgfree(ads, res);
2751 return status;
2755 * pull a dn from an extended dn string
2756 * @param mem_ctx TALLOC_CTX
2757 * @param extended_dn string
2758 * @param dn pointer to the dn
2759 * @return boolean inidicating success
2761 BOOL ads_get_dn_from_extended_dn(TALLOC_CTX *mem_ctx,
2762 const char *extended_dn,
2763 char **dn)
2765 char *p;
2766 pstring tok;
2768 if (!extended_dn) {
2769 return False;
2772 while (next_token(&extended_dn, tok, ";", sizeof(tok))) {
2773 p = tok;
2776 if ((*dn = talloc_strdup(mem_ctx, p)) == NULL) {
2777 return False;
2780 return True;
2784 * pull a DOM_SID from an extended dn string
2785 * @param mem_ctx TALLOC_CTX
2786 * @param extended_dn string
2787 * @param flags string type of extended_dn
2788 * @param sid pointer to a DOM_SID
2789 * @return boolean inidicating success
2791 BOOL ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
2792 const char *extended_dn,
2793 enum ads_extended_dn_flags flags,
2794 DOM_SID *sid)
2796 char *p, *q, *dn;
2798 if (!extended_dn) {
2799 return False;
2802 /* otherwise extended_dn gets stripped off */
2803 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
2804 return False;
2807 * ADS_EXTENDED_DN_HEX_STRING:
2808 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
2810 * ADS_EXTENDED_DN_STRING (only with w2k3):
2811 <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
2814 p = strchr(dn, ';');
2815 if (!p) {
2816 return False;
2819 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
2820 return False;
2823 p += strlen(";<SID=");
2825 q = strchr(p, '>');
2826 if (!q) {
2827 return False;
2830 *q = '\0';
2832 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
2834 switch (flags) {
2836 case ADS_EXTENDED_DN_STRING:
2837 if (!string_to_sid(sid, p)) {
2838 return False;
2840 break;
2841 case ADS_EXTENDED_DN_HEX_STRING: {
2842 pstring buf;
2843 size_t buf_len;
2845 buf_len = strhex_to_str(buf, strlen(p), p);
2846 if (buf_len == 0) {
2847 return False;
2850 if (!sid_parse(buf, buf_len, sid)) {
2851 DEBUG(10,("failed to parse sid\n"));
2852 return False;
2854 break;
2856 default:
2857 DEBUG(10,("unknown extended dn format\n"));
2858 return False;
2861 return True;
2865 * pull an array of DOM_SIDs from a ADS result
2866 * @param ads connection to ads server
2867 * @param mem_ctx TALLOC_CTX for allocating sid array
2868 * @param msg Results of search
2869 * @param field Attribute to retrieve
2870 * @param flags string type of extended_dn
2871 * @param sids pointer to sid array to allocate
2872 * @return the count of SIDs pulled
2874 int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads,
2875 TALLOC_CTX *mem_ctx,
2876 LDAPMessage *msg,
2877 const char *field,
2878 enum ads_extended_dn_flags flags,
2879 DOM_SID **sids)
2881 int i;
2882 size_t dn_count;
2883 char **dn_strings;
2885 if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field,
2886 &dn_count)) == NULL) {
2887 return 0;
2890 (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, dn_count + 1);
2891 if (!(*sids)) {
2892 TALLOC_FREE(dn_strings);
2893 return 0;
2896 for (i=0; i<dn_count; i++) {
2898 if (!ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i],
2899 flags, &(*sids)[i])) {
2900 TALLOC_FREE(*sids);
2901 TALLOC_FREE(dn_strings);
2902 return 0;
2906 TALLOC_FREE(dn_strings);
2908 return dn_count;
2911 /********************************************************************
2912 ********************************************************************/
2914 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2916 LDAPMessage *res = NULL;
2917 ADS_STATUS status;
2918 int count = 0;
2919 char *name = NULL;
2921 status = ads_find_machine_acct(ads, &res, global_myname());
2922 if (!ADS_ERR_OK(status)) {
2923 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
2924 global_myname()));
2925 goto out;
2928 if ( (count = ads_count_replies(ads, res)) != 1 ) {
2929 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
2930 goto out;
2933 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
2934 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
2937 out:
2938 ads_msgfree(ads, res);
2940 return name;
2943 /********************************************************************
2944 ********************************************************************/
2946 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2948 LDAPMessage *res = NULL;
2949 ADS_STATUS status;
2950 int count = 0;
2951 char *name = NULL;
2953 status = ads_find_machine_acct(ads, &res, global_myname());
2954 if (!ADS_ERR_OK(status)) {
2955 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
2956 global_myname()));
2957 goto out;
2960 if ( (count = ads_count_replies(ads, res)) != 1 ) {
2961 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
2962 goto out;
2965 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
2966 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
2969 out:
2970 ads_msgfree(ads, res);
2972 return name;
2975 /********************************************************************
2976 ********************************************************************/
2978 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2980 LDAPMessage *res = NULL;
2981 ADS_STATUS status;
2982 int count = 0;
2983 char *name = NULL;
2985 status = ads_find_machine_acct(ads, &res, global_myname());
2986 if (!ADS_ERR_OK(status)) {
2987 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
2988 global_myname()));
2989 goto out;
2992 if ( (count = ads_count_replies(ads, res)) != 1 ) {
2993 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
2994 goto out;
2997 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
2998 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3001 out:
3002 ads_msgfree(ads, res);
3004 return name;
3007 #if 0
3009 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3012 * Join a machine to a realm
3013 * Creates the machine account and sets the machine password
3014 * @param ads connection to ads server
3015 * @param machine name of host to add
3016 * @param org_unit Organizational unit to place machine in
3017 * @return status of join
3019 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3020 uint32 account_type, const char *org_unit)
3022 ADS_STATUS status;
3023 LDAPMessage *res = NULL;
3024 char *machine;
3026 /* machine name must be lowercase */
3027 machine = SMB_STRDUP(machine_name);
3028 strlower_m(machine);
3031 status = ads_find_machine_acct(ads, (void **)&res, machine);
3032 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3033 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3034 status = ads_leave_realm(ads, machine);
3035 if (!ADS_ERR_OK(status)) {
3036 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3037 machine, ads->config.realm));
3038 return status;
3042 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3043 if (!ADS_ERR_OK(status)) {
3044 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3045 SAFE_FREE(machine);
3046 return status;
3049 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3050 if (!ADS_ERR_OK(status)) {
3051 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3052 SAFE_FREE(machine);
3053 return status;
3056 SAFE_FREE(machine);
3057 ads_msgfree(ads, res);
3059 return status;
3061 #endif
3064 * Delete a machine from the realm
3065 * @param ads connection to ads server
3066 * @param hostname Machine to remove
3067 * @return status of delete
3069 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3071 ADS_STATUS status;
3072 void *msg;
3073 LDAPMessage *res;
3074 char *hostnameDN, *host;
3075 int rc;
3076 LDAPControl ldap_control;
3077 LDAPControl * pldap_control[2] = {NULL, NULL};
3079 pldap_control[0] = &ldap_control;
3080 memset(&ldap_control, 0, sizeof(LDAPControl));
3081 ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
3083 /* hostname must be lowercase */
3084 host = SMB_STRDUP(hostname);
3085 strlower_m(host);
3087 status = ads_find_machine_acct(ads, &res, host);
3088 if (!ADS_ERR_OK(status)) {
3089 DEBUG(0, ("Host account for %s does not exist.\n", host));
3090 SAFE_FREE(host);
3091 return status;
3094 msg = ads_first_entry(ads, res);
3095 if (!msg) {
3096 SAFE_FREE(host);
3097 return ADS_ERROR_SYSTEM(ENOENT);
3100 hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
3102 rc = ldap_delete_ext_s(ads->ld, hostnameDN, pldap_control, NULL);
3103 if (rc) {
3104 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3105 }else {
3106 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3109 if (rc != LDAP_SUCCESS) {
3110 const char *attrs[] = { "cn", NULL };
3111 LDAPMessage *msg_sub;
3113 /* we only search with scope ONE, we do not expect any further
3114 * objects to be created deeper */
3116 status = ads_do_search_retry(ads, hostnameDN,
3117 LDAP_SCOPE_ONELEVEL,
3118 "(objectclass=*)", attrs, &res);
3120 if (!ADS_ERR_OK(status)) {
3121 SAFE_FREE(host);
3122 ads_memfree(ads, hostnameDN);
3123 return status;
3126 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3127 msg_sub = ads_next_entry(ads, msg_sub)) {
3129 char *dn = NULL;
3131 if ((dn = ads_get_dn(ads, msg_sub)) == NULL) {
3132 SAFE_FREE(host);
3133 ads_memfree(ads, hostnameDN);
3134 return ADS_ERROR(LDAP_NO_MEMORY);
3137 status = ads_del_dn(ads, dn);
3138 if (!ADS_ERR_OK(status)) {
3139 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3140 SAFE_FREE(host);
3141 ads_memfree(ads, dn);
3142 ads_memfree(ads, hostnameDN);
3143 return status;
3146 ads_memfree(ads, dn);
3149 /* there should be no subordinate objects anymore */
3150 status = ads_do_search_retry(ads, hostnameDN,
3151 LDAP_SCOPE_ONELEVEL,
3152 "(objectclass=*)", attrs, &res);
3154 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3155 SAFE_FREE(host);
3156 ads_memfree(ads, hostnameDN);
3157 return status;
3160 /* delete hostnameDN now */
3161 status = ads_del_dn(ads, hostnameDN);
3162 if (!ADS_ERR_OK(status)) {
3163 SAFE_FREE(host);
3164 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3165 ads_memfree(ads, hostnameDN);
3166 return status;
3170 ads_memfree(ads, hostnameDN);
3172 status = ads_find_machine_acct(ads, &res, host);
3173 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3174 DEBUG(3, ("Failed to remove host account.\n"));
3175 SAFE_FREE(host);
3176 return status;
3179 SAFE_FREE(host);
3180 return status;
3183 #endif