r14214: Fix Coverity Bug # 57
[Samba/nascimento.git] / source3 / libads / ldap.c
blobdd49c706f46993b8414121232528255a9021ebb1
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
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 #include "includes.h"
26 #ifdef HAVE_LDAP
28 /**
29 * @file ldap.c
30 * @brief basic ldap client-side routines for ads server communications
32 * The routines contained here should do the necessary ldap calls for
33 * ads setups.
35 * Important note: attribute names passed into ads_ routines must
36 * already be in UTF-8 format. We do not convert them because in almost
37 * all cases, they are just ascii (which is represented with the same
38 * codepoints in UTF-8). This may have to change at some point
39 **/
42 #define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"
44 static SIG_ATOMIC_T gotalarm;
46 /***************************************************************
47 Signal function to tell us we timed out.
48 ****************************************************************/
50 static void gotalarm_sig(void)
52 gotalarm = 1;
55 LDAP *ldap_open_with_timeout(const char *server, int port, unsigned int to)
57 LDAP *ldp = NULL;
59 /* Setup timeout */
60 gotalarm = 0;
61 CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
62 alarm(to);
63 /* End setup timeout. */
65 ldp = ldap_open(server, port);
67 /* Teardown timeout. */
68 CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
69 alarm(0);
71 return ldp;
74 static int ldap_search_with_timeout(LDAP *ld,
75 LDAP_CONST char *base,
76 int scope,
77 LDAP_CONST char *filter,
78 char **attrs,
79 int attrsonly,
80 LDAPControl **sctrls,
81 LDAPControl **cctrls,
82 int sizelimit,
83 LDAPMessage **res )
85 struct timeval timeout;
86 int result;
88 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
89 timeout.tv_sec = lp_ldap_timeout();
90 timeout.tv_usec = 0;
92 /* Setup alarm timeout.... Do we need both of these ? JRA. */
93 gotalarm = 0;
94 CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
95 alarm(lp_ldap_timeout());
96 /* End setup timeout. */
98 result = ldap_search_ext_s(ld, base, scope, filter, attrs,
99 attrsonly, sctrls, cctrls, &timeout,
100 sizelimit, res);
102 /* Teardown timeout. */
103 CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
104 alarm(0);
106 if (gotalarm != 0)
107 return LDAP_TIMELIMIT_EXCEEDED;
109 return result;
113 try a connection to a given ldap server, returning True and setting the servers IP
114 in the ads struct if successful
116 TODO : add a negative connection cache in here leveraged off of the one
117 found in the rpc code. --jerry
119 BOOL ads_try_connect(ADS_STRUCT *ads, const char *server, unsigned port)
121 char *srv;
123 if (!server || !*server) {
124 return False;
127 DEBUG(5,("ads_try_connect: trying ldap server '%s' port %u\n", server, port));
129 /* this copes with inet_ntoa brokenness */
130 srv = SMB_STRDUP(server);
132 ads->ld = ldap_open_with_timeout(srv, port, lp_ldap_timeout());
133 if (!ads->ld) {
134 free(srv);
135 return False;
137 ads->ldap_port = port;
138 ads->ldap_ip = *interpret_addr2(srv);
139 free(srv);
141 /* cache the successful connection */
143 saf_store( ads->server.workgroup, server );
145 return True;
149 try a connection to a given ldap server, based on URL, returning True if successful
151 static BOOL ads_try_connect_uri(ADS_STRUCT *ads)
153 #if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
154 DEBUG(5,("ads_try_connect: trying ldap server at URI '%s'\n",
155 ads->server.ldap_uri));
158 if (ldap_initialize((LDAP**)&(ads->ld), ads->server.ldap_uri) == LDAP_SUCCESS) {
159 return True;
161 DEBUG(0, ("ldap_initialize: %s\n", strerror(errno)));
163 #else
165 DEBUG(1, ("no URL support in LDAP libs!\n"));
166 #endif
168 return False;
171 /**********************************************************************
172 Try to find an AD dc using our internal name resolution routines
173 Try the realm first and then then workgroup name if netbios is not
174 disabled
175 **********************************************************************/
177 static BOOL ads_find_dc(ADS_STRUCT *ads)
179 const char *c_realm;
180 int count, i=0;
181 struct ip_service *ip_list;
182 pstring realm;
183 BOOL got_realm = False;
184 BOOL use_own_domain = False;
186 /* if the realm and workgroup are both empty, assume they are ours */
188 /* realm */
189 c_realm = ads->server.realm;
191 if ( !c_realm || !*c_realm ) {
192 /* special case where no realm and no workgroup means our own */
193 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
194 use_own_domain = True;
195 c_realm = lp_realm();
199 if (c_realm && *c_realm)
200 got_realm = True;
202 again:
203 /* we need to try once with the realm name and fallback to the
204 netbios domain name if we fail (if netbios has not been disabled */
206 if ( !got_realm && !lp_disable_netbios() ) {
207 c_realm = ads->server.workgroup;
208 if (!c_realm || !*c_realm) {
209 if ( use_own_domain )
210 c_realm = lp_workgroup();
213 if ( !c_realm || !*c_realm ) {
214 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
215 return False;
219 pstrcpy( realm, c_realm );
221 DEBUG(6,("ads_find_dc: looking for %s '%s'\n",
222 (got_realm ? "realm" : "domain"), realm));
224 if ( !get_sorted_dc_list(realm, &ip_list, &count, got_realm) ) {
225 /* fall back to netbios if we can */
226 if ( got_realm && !lp_disable_netbios() ) {
227 got_realm = False;
228 goto again;
231 return False;
234 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
235 for ( i=0; i<count; i++ ) {
236 /* since this is an ads conection request, default to LDAP_PORT is not set */
237 int port = (ip_list[i].port!=PORT_NONE) ? ip_list[i].port : LDAP_PORT;
238 fstring server;
240 fstrcpy( server, inet_ntoa(ip_list[i].ip) );
242 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
243 continue;
245 if ( ads_try_connect(ads, server, port) ) {
246 SAFE_FREE(ip_list);
247 return True;
250 /* keep track of failures */
251 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
254 SAFE_FREE(ip_list);
256 return False;
261 * Connect to the LDAP server
262 * @param ads Pointer to an existing ADS_STRUCT
263 * @return status of connection
265 ADS_STATUS ads_connect(ADS_STRUCT *ads)
267 int version = LDAP_VERSION3;
268 ADS_STATUS status;
270 ads->last_attempt = time(NULL);
271 ads->ld = NULL;
273 /* try with a URL based server */
275 if (ads->server.ldap_uri &&
276 ads_try_connect_uri(ads)) {
277 goto got_connection;
280 /* try with a user specified server */
281 if (ads->server.ldap_server &&
282 ads_try_connect(ads, ads->server.ldap_server, LDAP_PORT)) {
283 goto got_connection;
286 if (ads_find_dc(ads)) {
287 goto got_connection;
290 return ADS_ERROR_SYSTEM(errno?errno:ENOENT);
292 got_connection:
293 DEBUG(3,("Connected to LDAP server %s\n", inet_ntoa(ads->ldap_ip)));
295 status = ads_server_info(ads);
296 if (!ADS_ERR_OK(status)) {
297 DEBUG(1,("Failed to get ldap server info\n"));
298 return status;
301 ldap_set_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
303 status = ADS_ERROR(smb_ldap_start_tls(ads->ld, version));
304 if (!ADS_ERR_OK(status)) {
305 return status;
308 if (!ads->auth.user_name) {
309 /* have to use the userPrincipalName value here and
310 not servicePrincipalName; found by Guenther Deschner @ Sernet */
312 asprintf(&ads->auth.user_name, "host/%s", global_myname() );
315 if (!ads->auth.realm) {
316 ads->auth.realm = SMB_STRDUP(ads->config.realm);
319 if (!ads->auth.kdc_server) {
320 ads->auth.kdc_server = SMB_STRDUP(inet_ntoa(ads->ldap_ip));
323 #if KRB5_DNS_HACK
324 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
325 to MIT kerberos to work (tridge) */
327 char *env;
328 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
329 setenv(env, ads->auth.kdc_server, 1);
330 free(env);
332 #endif
334 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
335 return ADS_SUCCESS;
338 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
339 return ADS_ERROR(ldap_simple_bind_s( ads->ld, NULL, NULL));
342 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
343 return ADS_ERROR(ldap_simple_bind_s( ads->ld, ads->auth.user_name, ads->auth.password));
346 return ads_sasl_bind(ads);
350 Duplicate a struct berval into talloc'ed memory
352 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
354 struct berval *value;
356 if (!in_val) return NULL;
358 value = TALLOC_ZERO_P(ctx, struct berval);
359 if (value == NULL)
360 return NULL;
361 if (in_val->bv_len == 0) return value;
363 value->bv_len = in_val->bv_len;
364 value->bv_val = TALLOC_MEMDUP(ctx, in_val->bv_val, in_val->bv_len);
365 return value;
369 Make a values list out of an array of (struct berval *)
371 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
372 const struct berval **in_vals)
374 struct berval **values;
375 int i;
377 if (!in_vals) return NULL;
378 for (i=0; in_vals[i]; i++)
379 ; /* count values */
380 values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
381 if (!values) return NULL;
383 for (i=0; in_vals[i]; i++) {
384 values[i] = dup_berval(ctx, in_vals[i]);
386 return values;
390 UTF8-encode a values list out of an array of (char *)
392 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
394 char **values;
395 int i;
397 if (!in_vals) return NULL;
398 for (i=0; in_vals[i]; i++)
399 ; /* count values */
400 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
401 if (!values) return NULL;
403 for (i=0; in_vals[i]; i++) {
404 push_utf8_talloc(ctx, &values[i], in_vals[i]);
406 return values;
410 Pull a (char *) array out of a UTF8-encoded values list
412 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
414 char **values;
415 int i;
417 if (!in_vals) return NULL;
418 for (i=0; in_vals[i]; i++)
419 ; /* count values */
420 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
421 if (!values) return NULL;
423 for (i=0; in_vals[i]; i++) {
424 pull_utf8_talloc(ctx, &values[i], in_vals[i]);
426 return values;
430 * Do a search with paged results. cookie must be null on the first
431 * call, and then returned on each subsequent call. It will be null
432 * again when the entire search is complete
433 * @param ads connection to ads server
434 * @param bind_path Base dn for the search
435 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
436 * @param expr Search expression - specified in local charset
437 * @param attrs Attributes to retrieve - specified in utf8 or ascii
438 * @param res ** which will contain results - free res* with ads_msgfree()
439 * @param count Number of entries retrieved on this page
440 * @param cookie The paged results cookie to be returned on subsequent calls
441 * @return status of search
443 ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
444 int scope, const char *expr,
445 const char **attrs, void **res,
446 int *count, void **cookie)
448 int rc, i, version;
449 char *utf8_expr, *utf8_path, **search_attrs;
450 LDAPControl PagedResults, NoReferrals, *controls[3], **rcontrols;
451 BerElement *cookie_be = NULL;
452 struct berval *cookie_bv= NULL;
453 TALLOC_CTX *ctx;
455 *res = NULL;
457 if (!(ctx = talloc_init("ads_do_paged_search")))
458 return ADS_ERROR(LDAP_NO_MEMORY);
460 /* 0 means the conversion worked but the result was empty
461 so we only fail if it's -1. In any case, it always
462 at least nulls out the dest */
463 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
464 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
465 rc = LDAP_NO_MEMORY;
466 goto done;
469 if (!attrs || !(*attrs))
470 search_attrs = NULL;
471 else {
472 /* This would be the utf8-encoded version...*/
473 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
474 if (!(str_list_copy(&search_attrs, attrs))) {
475 rc = LDAP_NO_MEMORY;
476 goto done;
481 /* Paged results only available on ldap v3 or later */
482 ldap_get_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
483 if (version < LDAP_VERSION3) {
484 rc = LDAP_NOT_SUPPORTED;
485 goto done;
488 cookie_be = ber_alloc_t(LBER_USE_DER);
489 if (cookie && *cookie) {
490 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
491 ber_bvfree(*cookie); /* don't need it from last time */
492 *cookie = NULL;
493 } else {
494 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
496 ber_flatten(cookie_be, &cookie_bv);
497 PagedResults.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID);
498 PagedResults.ldctl_iscritical = (char) 1;
499 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
500 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
502 NoReferrals.ldctl_oid = CONST_DISCARD(char *, ADS_NO_REFERRALS_OID);
503 NoReferrals.ldctl_iscritical = (char) 0;
504 NoReferrals.ldctl_value.bv_len = 0;
505 NoReferrals.ldctl_value.bv_val = CONST_DISCARD(char *, "");
508 controls[0] = &NoReferrals;
509 controls[1] = &PagedResults;
510 controls[2] = NULL;
512 /* we need to disable referrals as the openldap libs don't
513 handle them and paged results at the same time. Using them
514 together results in the result record containing the server
515 page control being removed from the result list (tridge/jmcd)
517 leaving this in despite the control that says don't generate
518 referrals, in case the server doesn't support it (jmcd)
520 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
522 rc = ldap_search_with_timeout(ads->ld, utf8_path, scope, utf8_expr,
523 search_attrs, 0, controls,
524 NULL, LDAP_NO_LIMIT,
525 (LDAPMessage **)res);
527 ber_free(cookie_be, 1);
528 ber_bvfree(cookie_bv);
530 if (rc) {
531 DEBUG(3,("ads_do_paged_search: ldap_search_with_timeout(%s) -> %s\n", expr,
532 ldap_err2string(rc)));
533 goto done;
536 rc = ldap_parse_result(ads->ld, *res, NULL, NULL, NULL,
537 NULL, &rcontrols, 0);
539 if (!rcontrols) {
540 goto done;
543 for (i=0; rcontrols[i]; i++) {
544 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
545 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
546 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
547 &cookie_bv);
548 /* the berval is the cookie, but must be freed when
549 it is all done */
550 if (cookie_bv->bv_len) /* still more to do */
551 *cookie=ber_bvdup(cookie_bv);
552 else
553 *cookie=NULL;
554 ber_bvfree(cookie_bv);
555 ber_free(cookie_be, 1);
556 break;
559 ldap_controls_free(rcontrols);
561 done:
562 talloc_destroy(ctx);
563 /* if/when we decide to utf8-encode attrs, take out this next line */
564 str_list_free(&search_attrs);
566 return ADS_ERROR(rc);
571 * Get all results for a search. This uses ads_do_paged_search() to return
572 * all entries in a large search.
573 * @param ads connection to ads server
574 * @param bind_path Base dn for the search
575 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
576 * @param expr Search expression
577 * @param attrs Attributes to retrieve
578 * @param res ** which will contain results - free res* with ads_msgfree()
579 * @return status of search
581 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
582 int scope, const char *expr,
583 const char **attrs, void **res)
585 void *cookie = NULL;
586 int count = 0;
587 ADS_STATUS status;
589 *res = NULL;
590 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, res,
591 &count, &cookie);
593 if (!ADS_ERR_OK(status))
594 return status;
596 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
597 while (cookie) {
598 void *res2 = NULL;
599 ADS_STATUS status2;
600 LDAPMessage *msg, *next;
602 status2 = ads_do_paged_search(ads, bind_path, scope, expr,
603 attrs, &res2, &count, &cookie);
605 if (!ADS_ERR_OK(status2)) break;
607 /* this relies on the way that ldap_add_result_entry() works internally. I hope
608 that this works on all ldap libs, but I have only tested with openldap */
609 for (msg = ads_first_entry(ads, res2); msg; msg = next) {
610 next = ads_next_entry(ads, msg);
611 ldap_add_result_entry((LDAPMessage **)res, msg);
613 /* note that we do not free res2, as the memory is now
614 part of the main returned list */
616 #else
617 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
618 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
619 #endif
621 return status;
625 * Run a function on all results for a search. Uses ads_do_paged_search() and
626 * runs the function as each page is returned, using ads_process_results()
627 * @param ads connection to ads server
628 * @param bind_path Base dn for the search
629 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
630 * @param expr Search expression - specified in local charset
631 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
632 * @param fn Function which takes attr name, values list, and data_area
633 * @param data_area Pointer which is passed to function on each call
634 * @return status of search
636 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
637 int scope, const char *expr, const char **attrs,
638 BOOL(*fn)(char *, void **, void *),
639 void *data_area)
641 void *cookie = NULL;
642 int count = 0;
643 ADS_STATUS status;
644 void *res;
646 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
647 &count, &cookie);
649 if (!ADS_ERR_OK(status)) return status;
651 ads_process_results(ads, res, fn, data_area);
652 ads_msgfree(ads, res);
654 while (cookie) {
655 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
656 &res, &count, &cookie);
658 if (!ADS_ERR_OK(status)) break;
660 ads_process_results(ads, res, fn, data_area);
661 ads_msgfree(ads, res);
664 return status;
668 * Do a search with a timeout.
669 * @param ads connection to ads server
670 * @param bind_path Base dn for the search
671 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
672 * @param expr Search expression
673 * @param attrs Attributes to retrieve
674 * @param res ** which will contain results - free res* with ads_msgfree()
675 * @return status of search
677 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
678 const char *expr,
679 const char **attrs, void **res)
681 int rc;
682 char *utf8_expr, *utf8_path, **search_attrs = NULL;
683 TALLOC_CTX *ctx;
685 *res = NULL;
686 if (!(ctx = talloc_init("ads_do_search"))) {
687 DEBUG(1,("ads_do_search: talloc_init() failed!"));
688 return ADS_ERROR(LDAP_NO_MEMORY);
691 /* 0 means the conversion worked but the result was empty
692 so we only fail if it's negative. In any case, it always
693 at least nulls out the dest */
694 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
695 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
696 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
697 rc = LDAP_NO_MEMORY;
698 goto done;
701 if (!attrs || !(*attrs))
702 search_attrs = NULL;
703 else {
704 /* This would be the utf8-encoded version...*/
705 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
706 if (!(str_list_copy(&search_attrs, attrs)))
708 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
709 rc = LDAP_NO_MEMORY;
710 goto done;
714 /* see the note in ads_do_paged_search - we *must* disable referrals */
715 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
717 rc = ldap_search_with_timeout(ads->ld, utf8_path, scope, utf8_expr,
718 search_attrs, 0, NULL, NULL,
719 LDAP_NO_LIMIT,
720 (LDAPMessage **)res);
722 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
723 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
724 rc = 0;
727 done:
728 talloc_destroy(ctx);
729 /* if/when we decide to utf8-encode attrs, take out this next line */
730 str_list_free(&search_attrs);
731 return ADS_ERROR(rc);
734 * Do a general ADS search
735 * @param ads connection to ads server
736 * @param res ** which will contain results - free res* with ads_msgfree()
737 * @param expr Search expression
738 * @param attrs Attributes to retrieve
739 * @return status of search
741 ADS_STATUS ads_search(ADS_STRUCT *ads, void **res,
742 const char *expr,
743 const char **attrs)
745 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
746 expr, attrs, res);
750 * Do a search on a specific DistinguishedName
751 * @param ads connection to ads server
752 * @param res ** which will contain results - free res* with ads_msgfree()
753 * @param dn DistinguishName to search
754 * @param attrs Attributes to retrieve
755 * @return status of search
757 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, void **res,
758 const char *dn,
759 const char **attrs)
761 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)", attrs, res);
765 * Free up memory from a ads_search
766 * @param ads connection to ads server
767 * @param msg Search results to free
769 void ads_msgfree(ADS_STRUCT *ads, void *msg)
771 if (!msg) return;
772 ldap_msgfree(msg);
776 * Free up memory from various ads requests
777 * @param ads connection to ads server
778 * @param mem Area to free
780 void ads_memfree(ADS_STRUCT *ads, void *mem)
782 SAFE_FREE(mem);
786 * Get a dn from search results
787 * @param ads connection to ads server
788 * @param msg Search result
789 * @return dn string
791 char *ads_get_dn(ADS_STRUCT *ads, void *msg)
793 char *utf8_dn, *unix_dn;
795 utf8_dn = ldap_get_dn(ads->ld, msg);
797 if (!utf8_dn) {
798 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
799 return NULL;
802 if (pull_utf8_allocate(&unix_dn, utf8_dn) == (size_t)-1) {
803 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
804 utf8_dn ));
805 return NULL;
807 ldap_memfree(utf8_dn);
808 return unix_dn;
812 * Get a canonical dn from search results
813 * @param ads connection to ads server
814 * @param msg Search result
815 * @return dn string
817 char *ads_get_dn_canonical(ADS_STRUCT *ads, void *msg)
819 #ifdef HAVE_LDAP_DN2AD_CANONICAL
820 return ldap_dn2ad_canonical(ads_get_dn(ads, msg));
821 #else
822 return NULL;
823 #endif
827 * Get the parent from a dn
828 * @param dn the dn to return the parent from
829 * @return parent dn string
831 char *ads_parent_dn(const char *dn)
833 char *p = strchr(dn, ',');
835 if (p == NULL) {
836 return NULL;
839 return p+1;
843 * Find a machine account given a hostname
844 * @param ads connection to ads server
845 * @param res ** which will contain results - free res* with ads_msgfree()
846 * @param host Hostname to search for
847 * @return status of search
849 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, void **res, const char *machine)
851 ADS_STATUS status;
852 char *expr;
853 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
855 *res = NULL;
857 /* the easiest way to find a machine account anywhere in the tree
858 is to look for hostname$ */
859 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
860 DEBUG(1, ("asprintf failed!\n"));
861 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
864 status = ads_search(ads, res, expr, attrs);
865 SAFE_FREE(expr);
866 return status;
870 * Initialize a list of mods to be used in a modify request
871 * @param ctx An initialized TALLOC_CTX
872 * @return allocated ADS_MODLIST
874 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
876 #define ADS_MODLIST_ALLOC_SIZE 10
877 LDAPMod **mods;
879 if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
880 /* -1 is safety to make sure we don't go over the end.
881 need to reset it to NULL before doing ldap modify */
882 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
884 return (ADS_MODLIST)mods;
889 add an attribute to the list, with values list already constructed
891 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
892 int mod_op, const char *name,
893 const void **invals)
895 int curmod;
896 LDAPMod **modlist = (LDAPMod **) *mods;
897 struct berval **ber_values = NULL;
898 char **char_values = NULL;
900 if (!invals) {
901 mod_op = LDAP_MOD_DELETE;
902 } else {
903 if (mod_op & LDAP_MOD_BVALUES)
904 ber_values = ads_dup_values(ctx,
905 (const struct berval **)invals);
906 else
907 char_values = ads_push_strvals(ctx,
908 (const char **) invals);
911 /* find the first empty slot */
912 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
913 curmod++);
914 if (modlist[curmod] == (LDAPMod *) -1) {
915 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
916 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
917 return ADS_ERROR(LDAP_NO_MEMORY);
918 memset(&modlist[curmod], 0,
919 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
920 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
921 *mods = (ADS_MODLIST)modlist;
924 if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
925 return ADS_ERROR(LDAP_NO_MEMORY);
926 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
927 if (mod_op & LDAP_MOD_BVALUES) {
928 modlist[curmod]->mod_bvalues = ber_values;
929 } else if (mod_op & LDAP_MOD_DELETE) {
930 modlist[curmod]->mod_values = NULL;
931 } else {
932 modlist[curmod]->mod_values = char_values;
935 modlist[curmod]->mod_op = mod_op;
936 return ADS_ERROR(LDAP_SUCCESS);
940 * Add a single string value to a mod list
941 * @param ctx An initialized TALLOC_CTX
942 * @param mods An initialized ADS_MODLIST
943 * @param name The attribute name to add
944 * @param val The value to add - NULL means DELETE
945 * @return ADS STATUS indicating success of add
947 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
948 const char *name, const char *val)
950 const char *values[2];
952 values[0] = val;
953 values[1] = NULL;
955 if (!val)
956 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
957 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name,
958 (const void **) values);
962 * Add an array of string values to a mod list
963 * @param ctx An initialized TALLOC_CTX
964 * @param mods An initialized ADS_MODLIST
965 * @param name The attribute name to add
966 * @param vals The array of string values to add - NULL means DELETE
967 * @return ADS STATUS indicating success of add
969 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
970 const char *name, const char **vals)
972 if (!vals)
973 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
974 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
975 name, (const void **) vals);
979 * Add a single ber-encoded value to a mod list
980 * @param ctx An initialized TALLOC_CTX
981 * @param mods An initialized ADS_MODLIST
982 * @param name The attribute name to add
983 * @param val The value to add - NULL means DELETE
984 * @return ADS STATUS indicating success of add
986 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
987 const char *name, const struct berval *val)
989 const struct berval *values[2];
991 values[0] = val;
992 values[1] = NULL;
993 if (!val)
994 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
995 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
996 name, (const void **) values);
1000 * Perform an ldap modify
1001 * @param ads connection to ads server
1002 * @param mod_dn DistinguishedName to modify
1003 * @param mods list of modifications to perform
1004 * @return status of modify
1006 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1008 int ret,i;
1009 char *utf8_dn = NULL;
1011 this control is needed to modify that contains a currently
1012 non-existent attribute (but allowable for the object) to run
1014 LDAPControl PermitModify = {
1015 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
1016 {0, NULL},
1017 (char) 1};
1018 LDAPControl *controls[2];
1020 controls[0] = &PermitModify;
1021 controls[1] = NULL;
1023 if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) {
1024 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1027 /* find the end of the list, marked by NULL or -1 */
1028 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1029 /* make sure the end of the list is NULL */
1030 mods[i] = NULL;
1031 ret = ldap_modify_ext_s(ads->ld, utf8_dn,
1032 (LDAPMod **) mods, controls, NULL);
1033 SAFE_FREE(utf8_dn);
1034 return ADS_ERROR(ret);
1038 * Perform an ldap add
1039 * @param ads connection to ads server
1040 * @param new_dn DistinguishedName to add
1041 * @param mods list of attributes and values for DN
1042 * @return status of add
1044 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1046 int ret, i;
1047 char *utf8_dn = NULL;
1049 if (push_utf8_allocate(&utf8_dn, new_dn) == -1) {
1050 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1051 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1054 /* find the end of the list, marked by NULL or -1 */
1055 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1056 /* make sure the end of the list is NULL */
1057 mods[i] = NULL;
1059 ret = ldap_add_s(ads->ld, utf8_dn, (LDAPMod**)mods);
1060 SAFE_FREE(utf8_dn);
1061 return ADS_ERROR(ret);
1065 * Delete a DistinguishedName
1066 * @param ads connection to ads server
1067 * @param new_dn DistinguishedName to delete
1068 * @return status of delete
1070 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1072 int ret;
1073 char *utf8_dn = NULL;
1074 if (push_utf8_allocate(&utf8_dn, del_dn) == -1) {
1075 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1076 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1079 ret = ldap_delete_s(ads->ld, utf8_dn);
1080 return ADS_ERROR(ret);
1084 * Build an org unit string
1085 * if org unit is Computers or blank then assume a container, otherwise
1086 * assume a \ separated list of organisational units
1087 * @param ads connection to ads server
1088 * @param org_unit Organizational unit
1089 * @return org unit string - caller must free
1091 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1093 char *ret = NULL;
1095 if (!org_unit || !*org_unit) {
1097 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1099 /* samba4 might not yet respond to a wellknownobject-query */
1100 return ret ? ret : SMB_STRDUP("cn=Computers");
1103 if (strequal(org_unit, "Computers")) {
1104 return SMB_STRDUP("cn=Computers");
1107 return ads_build_path(org_unit, "\\/", "ou=", 1);
1111 * Get a org unit string for a well-known GUID
1112 * @param ads connection to ads server
1113 * @param wknguid Well known GUID
1114 * @return org unit string - caller must free
1116 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1118 ADS_STATUS status;
1119 void *res;
1120 char *base, *wkn_dn, *ret, **wkn_dn_exp, **bind_dn_exp;
1121 const char *attrs[] = {"distinguishedName", NULL};
1122 int new_ln, wkn_ln, bind_ln, i;
1124 if (wknguid == NULL) {
1125 return NULL;
1128 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1129 DEBUG(1, ("asprintf failed!\n"));
1130 return NULL;
1133 status = ads_search_dn(ads, &res, base, attrs);
1134 if (!ADS_ERR_OK(status)) {
1135 DEBUG(1,("Failed while searching for: %s\n", base));
1136 return NULL;
1138 free(base);
1140 if (ads_count_replies(ads, res) != 1) {
1141 return NULL;
1144 /* substitute the bind-path from the well-known-guid-search result */
1145 wkn_dn = ads_get_dn(ads, res);
1146 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1147 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1149 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1151 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1154 new_ln = wkn_ln - bind_ln;
1156 ret = wkn_dn_exp[0];
1158 for (i=1; i < new_ln; i++) {
1159 char *s;
1160 asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]);
1161 ret = SMB_STRDUP(s);
1162 free(s);
1165 return ret;
1169 * Adds (appends) an item to an attribute array, rather then
1170 * replacing the whole list
1171 * @param ctx An initialized TALLOC_CTX
1172 * @param mods An initialized ADS_MODLIST
1173 * @param name name of the ldap attribute to append to
1174 * @param vals an array of values to add
1175 * @return status of addition
1178 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1179 const char *name, const char **vals)
1181 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name, (const void **) vals);
1185 * Determines the computer account's current KVNO via an LDAP lookup
1186 * @param ads An initialized ADS_STRUCT
1187 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1188 * @return the kvno for the computer account, or -1 in case of a failure.
1191 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *machine_name)
1193 LDAPMessage *res = NULL;
1194 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1195 char *filter;
1196 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1197 char *dn_string = NULL;
1198 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1200 DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name));
1201 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1202 return kvno;
1204 ret = ads_search(ads, (void**)(void *)&res, filter, attrs);
1205 SAFE_FREE(filter);
1206 if (!ADS_ERR_OK(ret) && ads_count_replies(ads, res)) {
1207 DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name));
1208 ads_msgfree(ads, res);
1209 return kvno;
1212 dn_string = ads_get_dn(ads, res);
1213 if (!dn_string) {
1214 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1215 ads_msgfree(ads, res);
1216 return kvno;
1218 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1219 ads_memfree(ads, dn_string);
1221 /* ---------------------------------------------------------
1222 * 0 is returned as a default KVNO from this point on...
1223 * This is done because Windows 2000 does not support key
1224 * version numbers. Chances are that a failure in the next
1225 * step is simply due to Windows 2000 being used for a
1226 * domain controller. */
1227 kvno = 0;
1229 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1230 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1231 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1232 ads_msgfree(ads, res);
1233 return kvno;
1236 /* Success */
1237 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1238 ads_msgfree(ads, res);
1239 return kvno;
1243 * This clears out all registered spn's for a given hostname
1244 * @param ads An initilaized ADS_STRUCT
1245 * @param machine_name the NetBIOS name of the computer.
1246 * @return 0 upon success, non-zero otherwise.
1249 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1251 TALLOC_CTX *ctx;
1252 LDAPMessage *res = NULL;
1253 ADS_MODLIST mods;
1254 const char *servicePrincipalName[1] = {NULL};
1255 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1256 char *dn_string = NULL;
1258 ret = ads_find_machine_acct(ads, (void **)(void *)&res, machine_name);
1259 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1260 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1261 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1262 ads_msgfree(ads, res);
1263 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1266 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1267 ctx = talloc_init("ads_clear_service_principal_names");
1268 if (!ctx) {
1269 ads_msgfree(ads, res);
1270 return ADS_ERROR(LDAP_NO_MEMORY);
1273 if (!(mods = ads_init_mods(ctx))) {
1274 talloc_destroy(ctx);
1275 ads_msgfree(ads, res);
1276 return ADS_ERROR(LDAP_NO_MEMORY);
1278 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1279 if (!ADS_ERR_OK(ret)) {
1280 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1281 ads_msgfree(ads, res);
1282 talloc_destroy(ctx);
1283 return ret;
1285 dn_string = ads_get_dn(ads, res);
1286 if (!dn_string) {
1287 talloc_destroy(ctx);
1288 ads_msgfree(ads, res);
1289 return ADS_ERROR(LDAP_NO_MEMORY);
1291 ret = ads_gen_mod(ads, dn_string, mods);
1292 ads_memfree(ads,dn_string);
1293 if (!ADS_ERR_OK(ret)) {
1294 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1295 machine_name));
1296 ads_msgfree(ads, res);
1297 talloc_destroy(ctx);
1298 return ret;
1301 ads_msgfree(ads, res);
1302 talloc_destroy(ctx);
1303 return ret;
1307 * This adds a service principal name to an existing computer account
1308 * (found by hostname) in AD.
1309 * @param ads An initialized ADS_STRUCT
1310 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1311 * @param spn A string of the service principal to add, i.e. 'host'
1312 * @return 0 upon sucess, or non-zero if a failure occurs
1315 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name, const char *spn)
1317 ADS_STATUS ret;
1318 TALLOC_CTX *ctx;
1319 LDAPMessage *res = NULL;
1320 char *host_spn, *psp1, *psp2, *psp3;
1321 ADS_MODLIST mods;
1322 fstring my_fqdn;
1323 char *dn_string = NULL;
1324 const char *servicePrincipalName[4] = {NULL, NULL, NULL, NULL};
1326 ret = ads_find_machine_acct(ads, (void **)(void *)&res, machine_name);
1327 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1328 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1329 machine_name));
1330 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1331 spn, machine_name, ads->config.realm));
1332 ads_msgfree(ads, res);
1333 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1336 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1337 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1338 ads_msgfree(ads, res);
1339 return ADS_ERROR(LDAP_NO_MEMORY);
1342 name_to_fqdn(my_fqdn, machine_name);
1343 strlower_m(my_fqdn);
1345 if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", my_fqdn))) {
1346 talloc_destroy(ctx);
1347 ads_msgfree(ads, res);
1348 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1351 /* Add the extra principal */
1352 psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name);
1353 strupper_m(psp1);
1354 strlower_m(&psp1[strlen(spn)]);
1355 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp1, machine_name));
1356 servicePrincipalName[0] = psp1;
1357 psp2 = talloc_asprintf(ctx, "%s/%s.%s", spn, machine_name, ads->config.realm);
1358 strupper_m(psp2);
1359 strlower_m(&psp2[strlen(spn)]);
1360 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp2, machine_name));
1361 servicePrincipalName[1] = psp2;
1363 /* Add another principal in case the realm != the DNS domain, so that
1364 * the KDC doesn't send "server principal unknown" errors to clients
1365 * which use the DNS name in determining service principal names. */
1366 psp3 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn);
1367 strupper_m(psp3);
1368 strlower_m(&psp3[strlen(spn)]);
1369 if (strcmp(psp2, psp3) != 0) {
1370 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp3, machine_name));
1371 servicePrincipalName[2] = psp3;
1374 if (!(mods = ads_init_mods(ctx))) {
1375 talloc_destroy(ctx);
1376 ads_msgfree(ads, res);
1377 return ADS_ERROR(LDAP_NO_MEMORY);
1379 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1380 if (!ADS_ERR_OK(ret)) {
1381 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1382 talloc_destroy(ctx);
1383 ads_msgfree(ads, res);
1384 return ret;
1386 dn_string = ads_get_dn(ads, res);
1387 if (!dn_string) {
1388 talloc_destroy(ctx);
1389 ads_msgfree(ads, res);
1390 return ADS_ERROR(LDAP_NO_MEMORY);
1392 ret = ads_gen_mod(ads, dn_string, mods);
1393 ads_memfree(ads,dn_string);
1394 if (!ADS_ERR_OK(ret)) {
1395 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1396 talloc_destroy(ctx);
1397 ads_msgfree(ads, res);
1398 return ret;
1401 talloc_destroy(ctx);
1402 ads_msgfree(ads, res);
1403 return ret;
1407 * adds a machine account to the ADS server
1408 * @param ads An intialized ADS_STRUCT
1409 * @param machine_name - the NetBIOS machine name of this account.
1410 * @param account_type A number indicating the type of account to create
1411 * @param org_unit The LDAP path in which to place this account
1412 * @return 0 upon success, or non-zero otherwise
1415 static ADS_STATUS ads_add_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1416 uint32 account_type,
1417 const char *org_unit)
1419 ADS_STATUS ret, status;
1420 char *host_spn, *host_upn, *new_dn, *samAccountName, *controlstr;
1421 TALLOC_CTX *ctx;
1422 ADS_MODLIST mods;
1423 const char *objectClass[] = {"top", "person", "organizationalPerson",
1424 "user", "computer", NULL};
1425 const char *servicePrincipalName[7] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
1426 char *psp, *psp2, *psp3, *psp4;
1427 unsigned acct_control;
1428 unsigned exists=0;
1429 fstring my_fqdn;
1430 LDAPMessage *res = NULL;
1431 int i, next_spn;
1433 if (!(ctx = talloc_init("ads_add_machine_acct")))
1434 return ADS_ERROR(LDAP_NO_MEMORY);
1436 ret = ADS_ERROR(LDAP_NO_MEMORY);
1438 name_to_fqdn(my_fqdn, machine_name);
1440 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine_name);
1441 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1442 char *dn_string = ads_get_dn(ads, res);
1443 if (!dn_string) {
1444 DEBUG(1, ("ads_add_machine_acct: ads_get_dn returned NULL (malloc failure?)\n"));
1445 goto done;
1447 new_dn = talloc_strdup(ctx, dn_string);
1448 ads_memfree(ads,dn_string);
1449 DEBUG(0, ("ads_add_machine_acct: Host account for %s already exists - modifying old account\n",
1450 machine_name));
1451 exists=1;
1452 } else {
1453 char *ou_str = ads_ou_string(ads,org_unit);
1454 if (!ou_str) {
1455 DEBUG(1, ("ads_add_machine_acct: ads_ou_string returned NULL (malloc failure?)\n"));
1456 goto done;
1458 new_dn = talloc_asprintf(ctx, "cn=%s,%s,%s", machine_name, ou_str,
1459 ads->config.bind_path);
1461 SAFE_FREE(ou_str);
1464 if (!new_dn) {
1465 goto done;
1468 if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", machine_name)))
1469 goto done;
1470 if (!(host_upn = talloc_asprintf(ctx, "%s@%s", host_spn, ads->config.realm)))
1471 goto done;
1472 servicePrincipalName[0] = talloc_asprintf(ctx, "HOST/%s", machine_name);
1473 psp = talloc_asprintf(ctx, "HOST/%s.%s",
1474 machine_name,
1475 ads->config.realm);
1476 strlower_m(&psp[5]);
1477 servicePrincipalName[1] = psp;
1478 servicePrincipalName[2] = talloc_asprintf(ctx, "CIFS/%s", machine_name);
1479 psp2 = talloc_asprintf(ctx, "CIFS/%s.%s",
1480 machine_name,
1481 ads->config.realm);
1482 strlower_m(&psp2[5]);
1483 servicePrincipalName[3] = psp2;
1485 /* Ensure servicePrincipalName[4] and [5] are unique. */
1486 strlower_m(my_fqdn);
1487 psp3 = talloc_asprintf(ctx, "CIFS/%s", my_fqdn);
1488 strlower_m(&psp3[5]);
1490 next_spn = 4;
1491 for (i = 0; i < next_spn; i++) {
1492 if (strequal(servicePrincipalName[i], psp3))
1493 break;
1495 if (i == next_spn) {
1496 servicePrincipalName[next_spn++] = psp3;
1499 psp4 = talloc_asprintf(ctx, "HOST/%s", my_fqdn);
1500 strlower_m(&psp4[5]);
1501 for (i = 0; i < next_spn; i++) {
1502 if (strequal(servicePrincipalName[i], psp4))
1503 break;
1505 if (i == next_spn) {
1506 servicePrincipalName[next_spn++] = psp4;
1509 if (!(samAccountName = talloc_asprintf(ctx, "%s$", machine_name))) {
1510 goto done;
1513 acct_control = account_type | UF_DONT_EXPIRE_PASSWD;
1514 #ifndef ENCTYPE_ARCFOUR_HMAC
1515 acct_control |= UF_USE_DES_KEY_ONLY;
1516 #endif
1518 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
1519 goto done;
1522 if (!(mods = ads_init_mods(ctx))) {
1523 goto done;
1526 if (!exists) {
1527 ads_mod_str(ctx, &mods, "cn", machine_name);
1528 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1529 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1531 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1532 ads_mod_str(ctx, &mods, "dNSHostName", my_fqdn);
1533 ads_mod_str(ctx, &mods, "userPrincipalName", host_upn);
1534 ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1535 ads_mod_str(ctx, &mods, "operatingSystem", "Samba");
1536 ads_mod_str(ctx, &mods, "operatingSystemVersion", SAMBA_VERSION_STRING);
1538 if (!exists) {
1539 ret = ads_gen_add(ads, new_dn, mods);
1540 } else {
1541 ret = ads_gen_mod(ads, new_dn, mods);
1544 if (!ADS_ERR_OK(ret)) {
1545 goto done;
1548 /* Do not fail if we can't set security descriptor
1549 * it shouldn't be mandatory and probably we just
1550 * don't have enough rights to do it.
1552 if (!exists) {
1553 status = ads_set_machine_sd(ads, machine_name, new_dn);
1555 if (!ADS_ERR_OK(status)) {
1556 DEBUG(0, ("Warning: ads_set_machine_sd: %s\n",
1557 ads_errstr(status)));
1560 done:
1561 ads_msgfree(ads, res);
1562 talloc_destroy(ctx);
1563 return ret;
1567 dump a binary result from ldap
1569 static void dump_binary(const char *field, struct berval **values)
1571 int i, j;
1572 for (i=0; values[i]; i++) {
1573 printf("%s: ", field);
1574 for (j=0; j<values[i]->bv_len; j++) {
1575 printf("%02X", (unsigned char)values[i]->bv_val[j]);
1577 printf("\n");
1581 static void dump_guid(const char *field, struct berval **values)
1583 int i;
1584 UUID_FLAT guid;
1585 for (i=0; values[i]; i++) {
1586 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1587 printf("%s: %s\n", field,
1588 smb_uuid_string_static(smb_uuid_unpack_static(guid)));
1593 dump a sid result from ldap
1595 static void dump_sid(const char *field, struct berval **values)
1597 int i;
1598 for (i=0; values[i]; i++) {
1599 DOM_SID sid;
1600 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1601 printf("%s: %s\n", field, sid_string_static(&sid));
1606 dump ntSecurityDescriptor
1608 static void dump_sd(const char *filed, struct berval **values)
1610 prs_struct ps;
1612 SEC_DESC *psd = 0;
1613 TALLOC_CTX *ctx = 0;
1615 if (!(ctx = talloc_init("sec_io_desc")))
1616 return;
1618 /* prepare data */
1619 prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
1620 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
1621 prs_set_offset(&ps,0);
1623 /* parse secdesc */
1624 if (!sec_io_desc("sd", &psd, &ps, 1)) {
1625 prs_mem_free(&ps);
1626 talloc_destroy(ctx);
1627 return;
1629 if (psd) ads_disp_sd(psd);
1631 prs_mem_free(&ps);
1632 talloc_destroy(ctx);
1636 dump a string result from ldap
1638 static void dump_string(const char *field, char **values)
1640 int i;
1641 for (i=0; values[i]; i++) {
1642 printf("%s: %s\n", field, values[i]);
1647 dump a field from LDAP on stdout
1648 used for debugging
1651 static BOOL ads_dump_field(char *field, void **values, void *data_area)
1653 const struct {
1654 const char *name;
1655 BOOL string;
1656 void (*handler)(const char *, struct berval **);
1657 } handlers[] = {
1658 {"objectGUID", False, dump_guid},
1659 {"netbootGUID", False, dump_guid},
1660 {"nTSecurityDescriptor", False, dump_sd},
1661 {"dnsRecord", False, dump_binary},
1662 {"objectSid", False, dump_sid},
1663 {"tokenGroups", False, dump_sid},
1664 {NULL, True, NULL}
1666 int i;
1668 if (!field) { /* must be end of an entry */
1669 printf("\n");
1670 return False;
1673 for (i=0; handlers[i].name; i++) {
1674 if (StrCaseCmp(handlers[i].name, field) == 0) {
1675 if (!values) /* first time, indicate string or not */
1676 return handlers[i].string;
1677 handlers[i].handler(field, (struct berval **) values);
1678 break;
1681 if (!handlers[i].name) {
1682 if (!values) /* first time, indicate string conversion */
1683 return True;
1684 dump_string(field, (char **)values);
1686 return False;
1690 * Dump a result from LDAP on stdout
1691 * used for debugging
1692 * @param ads connection to ads server
1693 * @param res Results to dump
1696 void ads_dump(ADS_STRUCT *ads, void *res)
1698 ads_process_results(ads, res, ads_dump_field, NULL);
1702 * Walk through results, calling a function for each entry found.
1703 * The function receives a field name, a berval * array of values,
1704 * and a data area passed through from the start. The function is
1705 * called once with null for field and values at the end of each
1706 * entry.
1707 * @param ads connection to ads server
1708 * @param res Results to process
1709 * @param fn Function for processing each result
1710 * @param data_area user-defined area to pass to function
1712 void ads_process_results(ADS_STRUCT *ads, void *res,
1713 BOOL(*fn)(char *, void **, void *),
1714 void *data_area)
1716 void *msg;
1717 TALLOC_CTX *ctx;
1719 if (!(ctx = talloc_init("ads_process_results")))
1720 return;
1722 for (msg = ads_first_entry(ads, res); msg;
1723 msg = ads_next_entry(ads, msg)) {
1724 char *utf8_field;
1725 BerElement *b;
1727 for (utf8_field=ldap_first_attribute(ads->ld,
1728 (LDAPMessage *)msg,&b);
1729 utf8_field;
1730 utf8_field=ldap_next_attribute(ads->ld,
1731 (LDAPMessage *)msg,b)) {
1732 struct berval **ber_vals;
1733 char **str_vals, **utf8_vals;
1734 char *field;
1735 BOOL string;
1737 pull_utf8_talloc(ctx, &field, utf8_field);
1738 string = fn(field, NULL, data_area);
1740 if (string) {
1741 utf8_vals = ldap_get_values(ads->ld,
1742 (LDAPMessage *)msg, field);
1743 str_vals = ads_pull_strvals(ctx,
1744 (const char **) utf8_vals);
1745 fn(field, (void **) str_vals, data_area);
1746 ldap_value_free(utf8_vals);
1747 } else {
1748 ber_vals = ldap_get_values_len(ads->ld,
1749 (LDAPMessage *)msg, field);
1750 fn(field, (void **) ber_vals, data_area);
1752 ldap_value_free_len(ber_vals);
1754 ldap_memfree(utf8_field);
1756 ber_free(b, 0);
1757 talloc_free_children(ctx);
1758 fn(NULL, NULL, data_area); /* completed an entry */
1761 talloc_destroy(ctx);
1765 * count how many replies are in a LDAPMessage
1766 * @param ads connection to ads server
1767 * @param res Results to count
1768 * @return number of replies
1770 int ads_count_replies(ADS_STRUCT *ads, void *res)
1772 return ldap_count_entries(ads->ld, (LDAPMessage *)res);
1776 * Join a machine to a realm
1777 * Creates the machine account and sets the machine password
1778 * @param ads connection to ads server
1779 * @param machine name of host to add
1780 * @param org_unit Organizational unit to place machine in
1781 * @return status of join
1783 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
1784 uint32 account_type, const char *org_unit)
1786 ADS_STATUS status;
1787 LDAPMessage *res = NULL;
1788 char *machine;
1790 /* machine name must be lowercase */
1791 machine = SMB_STRDUP(machine_name);
1792 strlower_m(machine);
1795 status = ads_find_machine_acct(ads, (void **)&res, machine);
1796 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1797 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
1798 status = ads_leave_realm(ads, machine);
1799 if (!ADS_ERR_OK(status)) {
1800 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
1801 machine, ads->config.realm));
1802 return status;
1807 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
1808 if (!ADS_ERR_OK(status)) {
1809 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
1810 SAFE_FREE(machine);
1811 return status;
1814 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
1815 if (!ADS_ERR_OK(status)) {
1816 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
1817 SAFE_FREE(machine);
1818 return status;
1821 SAFE_FREE(machine);
1822 ads_msgfree(ads, res);
1824 return status;
1828 * Delete a machine from the realm
1829 * @param ads connection to ads server
1830 * @param hostname Machine to remove
1831 * @return status of delete
1833 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
1835 ADS_STATUS status;
1836 void *res, *msg;
1837 char *hostnameDN, *host;
1838 int rc;
1839 LDAPControl ldap_control;
1840 LDAPControl * pldap_control[2] = {NULL, NULL};
1842 pldap_control[0] = &ldap_control;
1843 memset(&ldap_control, 0, sizeof(LDAPControl));
1844 ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
1846 /* hostname must be lowercase */
1847 host = SMB_STRDUP(hostname);
1848 strlower_m(host);
1850 status = ads_find_machine_acct(ads, &res, host);
1851 if (!ADS_ERR_OK(status)) {
1852 DEBUG(0, ("Host account for %s does not exist.\n", host));
1853 return status;
1856 msg = ads_first_entry(ads, res);
1857 if (!msg) {
1858 return ADS_ERROR_SYSTEM(ENOENT);
1861 hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
1864 rc = ldap_delete_ext_s(ads->ld, hostnameDN, pldap_control, NULL);
1865 if (rc) {
1866 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
1867 }else {
1868 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
1871 ads_memfree(ads, hostnameDN);
1872 if (rc != LDAP_SUCCESS) {
1873 return ADS_ERROR(rc);
1876 status = ads_find_machine_acct(ads, &res, host);
1877 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1878 DEBUG(0, ("Failed to remove host account.\n"));
1879 return status;
1882 free(host);
1884 return status;
1888 * add machine account to existing security descriptor
1889 * @param ads connection to ads server
1890 * @param hostname machine to add
1891 * @param dn DN of security descriptor
1892 * @return status
1894 ADS_STATUS ads_set_machine_sd(ADS_STRUCT *ads, const char *hostname, char *dn)
1896 const char *attrs[] = {"nTSecurityDescriptor", "objectSid", 0};
1897 char *expr = 0;
1898 size_t sd_size = 0;
1899 struct berval bval = {0, NULL};
1900 prs_struct ps_wire;
1901 char *escaped_hostname = escape_ldap_string_alloc(hostname);
1903 LDAPMessage *res = 0;
1904 LDAPMessage *msg = 0;
1905 ADS_MODLIST mods = 0;
1907 NTSTATUS status;
1908 ADS_STATUS ret;
1909 DOM_SID sid;
1910 SEC_DESC *psd = NULL;
1911 TALLOC_CTX *ctx = NULL;
1913 /* Avoid segmentation fault in prs_mem_free if
1914 * we have to bail out before prs_init */
1915 ps_wire.is_dynamic = False;
1917 if (!ads) return ADS_ERROR(LDAP_SERVER_DOWN);
1919 ret = ADS_ERROR(LDAP_SUCCESS);
1921 if (!escaped_hostname) {
1922 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1925 if (asprintf(&expr, "(samAccountName=%s$)", escaped_hostname) == -1) {
1926 DEBUG(1, ("ads_set_machine_sd: asprintf failed!\n"));
1927 SAFE_FREE(escaped_hostname);
1928 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1931 SAFE_FREE(escaped_hostname);
1933 ret = ads_search(ads, (void *) &res, expr, attrs);
1935 if (!ADS_ERR_OK(ret)) return ret;
1937 if ( !(msg = ads_first_entry(ads, res) )) {
1938 ret = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1939 goto ads_set_sd_error;
1942 if (!ads_pull_sid(ads, msg, attrs[1], &sid)) {
1943 ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
1944 goto ads_set_sd_error;
1947 if (!(ctx = talloc_init("sec_io_desc"))) {
1948 ret = ADS_ERROR(LDAP_NO_MEMORY);
1949 goto ads_set_sd_error;
1952 if (!ads_pull_sd(ads, ctx, msg, attrs[0], &psd)) {
1953 ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
1954 goto ads_set_sd_error;
1957 status = sec_desc_add_sid(ctx, &psd, &sid, SEC_RIGHTS_FULL_CTRL, &sd_size);
1959 if (!NT_STATUS_IS_OK(status)) {
1960 ret = ADS_ERROR_NT(status);
1961 goto ads_set_sd_error;
1964 if (!prs_init(&ps_wire, sd_size, ctx, MARSHALL)) {
1965 ret = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1968 if (!sec_io_desc("sd_wire", &psd, &ps_wire, 1)) {
1969 ret = ADS_ERROR(LDAP_NO_MEMORY);
1970 goto ads_set_sd_error;
1973 #if 0
1974 file_save("/tmp/sec_desc.new", ps_wire.data_p, sd_size);
1975 #endif
1976 if (!(mods = ads_init_mods(ctx))) return ADS_ERROR(LDAP_NO_MEMORY);
1978 bval.bv_len = prs_offset(&ps_wire);
1979 bval.bv_val = TALLOC(ctx, bval.bv_len);
1980 if (!bval.bv_val) {
1981 ret = ADS_ERROR(LDAP_NO_MEMORY);
1982 goto ads_set_sd_error;
1985 prs_set_offset(&ps_wire, 0);
1987 if (!prs_copy_data_out(bval.bv_val, &ps_wire, bval.bv_len)) {
1988 ret = ADS_ERROR(LDAP_NO_MEMORY);
1989 goto ads_set_sd_error;
1992 ret = ads_mod_ber(ctx, &mods, attrs[0], &bval);
1993 if (ADS_ERR_OK(ret)) {
1994 ret = ads_gen_mod(ads, dn, mods);
1997 ads_set_sd_error:
1998 ads_msgfree(ads, res);
1999 prs_mem_free(&ps_wire);
2000 talloc_destroy(ctx);
2001 return ret;
2005 * pull the first entry from a ADS result
2006 * @param ads connection to ads server
2007 * @param res Results of search
2008 * @return first entry from result
2010 void *ads_first_entry(ADS_STRUCT *ads, void *res)
2012 return (void *)ldap_first_entry(ads->ld, (LDAPMessage *)res);
2016 * pull the next entry from a ADS result
2017 * @param ads connection to ads server
2018 * @param res Results of search
2019 * @return next entry from result
2021 void *ads_next_entry(ADS_STRUCT *ads, void *res)
2023 return (void *)ldap_next_entry(ads->ld, (LDAPMessage *)res);
2027 * pull a single string 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 string in talloc context
2034 char *ads_pull_string(ADS_STRUCT *ads,
2035 TALLOC_CTX *mem_ctx, void *msg, const char *field)
2037 char **values;
2038 char *ret = NULL;
2039 char *ux_string;
2040 size_t rc;
2042 values = ldap_get_values(ads->ld, msg, field);
2043 if (!values)
2044 return NULL;
2046 if (values[0]) {
2047 rc = pull_utf8_talloc(mem_ctx, &ux_string,
2048 values[0]);
2049 if (rc != (size_t)-1)
2050 ret = ux_string;
2053 ldap_value_free(values);
2054 return ret;
2058 * pull an array of strings from a ADS result
2059 * @param ads connection to ads server
2060 * @param mem_ctx TALLOC_CTX to use for allocating result string
2061 * @param msg Results of search
2062 * @param field Attribute to retrieve
2063 * @return Result strings in talloc context
2065 char **ads_pull_strings(ADS_STRUCT *ads,
2066 TALLOC_CTX *mem_ctx, void *msg, const char *field,
2067 size_t *num_values)
2069 char **values;
2070 char **ret = NULL;
2071 int i;
2073 values = ldap_get_values(ads->ld, msg, field);
2074 if (!values)
2075 return NULL;
2077 *num_values = ldap_count_values(values);
2079 ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
2080 if (!ret) {
2081 ldap_value_free(values);
2082 return NULL;
2085 for (i=0;i<*num_values;i++) {
2086 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
2087 ldap_value_free(values);
2088 return NULL;
2091 ret[i] = NULL;
2093 ldap_value_free(values);
2094 return ret;
2098 * pull an array of strings from a ADS result
2099 * (handle large multivalue attributes with range retrieval)
2100 * @param ads connection to ads server
2101 * @param mem_ctx TALLOC_CTX to use for allocating result string
2102 * @param msg Results of search
2103 * @param field Attribute to retrieve
2104 * @param current_strings strings returned by a previous call to this function
2105 * @param next_attribute The next query should ask for this attribute
2106 * @param num_values How many values did we get this time?
2107 * @param more_values Are there more values to get?
2108 * @return Result strings in talloc context
2110 char **ads_pull_strings_range(ADS_STRUCT *ads,
2111 TALLOC_CTX *mem_ctx,
2112 void *msg, const char *field,
2113 char **current_strings,
2114 const char **next_attribute,
2115 size_t *num_strings,
2116 BOOL *more_strings)
2118 char *attr;
2119 char *expected_range_attrib, *range_attr;
2120 BerElement *ptr = NULL;
2121 char **strings;
2122 char **new_strings;
2123 size_t num_new_strings;
2124 unsigned long int range_start;
2125 unsigned long int range_end;
2127 /* we might have been given the whole lot anyway */
2128 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2129 *more_strings = False;
2130 return strings;
2133 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2135 /* look for Range result */
2136 for (attr = ldap_first_attribute(ads->ld, (LDAPMessage *)msg, &ptr);
2137 attr;
2138 attr = ldap_next_attribute(ads->ld, (LDAPMessage *)msg, ptr)) {
2139 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2140 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2141 range_attr = attr;
2142 break;
2144 ldap_memfree(attr);
2146 if (!attr) {
2147 ber_free(ptr, 0);
2148 /* nothing here - this field is just empty */
2149 *more_strings = False;
2150 return NULL;
2153 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2154 &range_start, &range_end) == 2) {
2155 *more_strings = True;
2156 } else {
2157 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2158 &range_start) == 1) {
2159 *more_strings = False;
2160 } else {
2161 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2162 range_attr));
2163 ldap_memfree(range_attr);
2164 *more_strings = False;
2165 return NULL;
2169 if ((*num_strings) != range_start) {
2170 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2171 " - aborting range retreival\n",
2172 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2173 ldap_memfree(range_attr);
2174 *more_strings = False;
2175 return NULL;
2178 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2180 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2181 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2182 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2183 range_attr, (unsigned long int)range_end - range_start + 1,
2184 (unsigned long int)num_new_strings));
2185 ldap_memfree(range_attr);
2186 *more_strings = False;
2187 return NULL;
2190 strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2191 *num_strings + num_new_strings);
2193 if (strings == NULL) {
2194 ldap_memfree(range_attr);
2195 *more_strings = False;
2196 return NULL;
2199 memcpy(&strings[*num_strings], new_strings,
2200 sizeof(*new_strings) * num_new_strings);
2202 (*num_strings) += num_new_strings;
2204 if (*more_strings) {
2205 *next_attribute = talloc_asprintf(mem_ctx,
2206 "%s;range=%d-*",
2207 field,
2208 (int)*num_strings);
2210 if (!*next_attribute) {
2211 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2212 ldap_memfree(range_attr);
2213 *more_strings = False;
2214 return NULL;
2218 ldap_memfree(range_attr);
2220 return strings;
2224 * pull a single uint32 from a ADS result
2225 * @param ads connection to ads server
2226 * @param msg Results of search
2227 * @param field Attribute to retrieve
2228 * @param v Pointer to int to store result
2229 * @return boolean inidicating success
2231 BOOL ads_pull_uint32(ADS_STRUCT *ads,
2232 void *msg, const char *field, uint32 *v)
2234 char **values;
2236 values = ldap_get_values(ads->ld, msg, field);
2237 if (!values)
2238 return False;
2239 if (!values[0]) {
2240 ldap_value_free(values);
2241 return False;
2244 *v = atoi(values[0]);
2245 ldap_value_free(values);
2246 return True;
2250 * pull a single objectGUID from an ADS result
2251 * @param ads connection to ADS server
2252 * @param msg results of search
2253 * @param guid 37-byte area to receive text guid
2254 * @return boolean indicating success
2256 BOOL ads_pull_guid(ADS_STRUCT *ads,
2257 void *msg, struct uuid *guid)
2259 char **values;
2260 UUID_FLAT flat_guid;
2262 values = ldap_get_values(ads->ld, msg, "objectGUID");
2263 if (!values)
2264 return False;
2266 if (values[0]) {
2267 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2268 smb_uuid_unpack(flat_guid, guid);
2269 ldap_value_free(values);
2270 return True;
2272 ldap_value_free(values);
2273 return False;
2279 * pull a single DOM_SID from a ADS result
2280 * @param ads connection to ads server
2281 * @param msg Results of search
2282 * @param field Attribute to retrieve
2283 * @param sid Pointer to sid to store result
2284 * @return boolean inidicating success
2286 BOOL ads_pull_sid(ADS_STRUCT *ads,
2287 void *msg, const char *field, DOM_SID *sid)
2289 struct berval **values;
2290 BOOL ret = False;
2292 values = ldap_get_values_len(ads->ld, msg, field);
2294 if (!values)
2295 return False;
2297 if (values[0])
2298 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2300 ldap_value_free_len(values);
2301 return ret;
2305 * pull an array of DOM_SIDs from a ADS result
2306 * @param ads connection to ads server
2307 * @param mem_ctx TALLOC_CTX for allocating sid array
2308 * @param msg Results of search
2309 * @param field Attribute to retrieve
2310 * @param sids pointer to sid array to allocate
2311 * @return the count of SIDs pulled
2313 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2314 void *msg, const char *field, DOM_SID **sids)
2316 struct berval **values;
2317 BOOL ret;
2318 int count, i;
2320 values = ldap_get_values_len(ads->ld, msg, field);
2322 if (!values)
2323 return 0;
2325 for (i=0; values[i]; i++)
2326 /* nop */ ;
2328 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2329 if (!(*sids)) {
2330 ldap_value_free_len(values);
2331 return 0;
2334 count = 0;
2335 for (i=0; values[i]; i++) {
2336 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2337 if (ret) {
2338 fstring sid;
2339 DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid, &(*sids)[count])));
2340 count++;
2344 ldap_value_free_len(values);
2345 return count;
2349 * pull a SEC_DESC from a ADS result
2350 * @param ads connection to ads server
2351 * @param mem_ctx TALLOC_CTX for allocating sid array
2352 * @param msg Results of search
2353 * @param field Attribute to retrieve
2354 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2355 * @return boolean inidicating success
2357 BOOL ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2358 void *msg, const char *field, SEC_DESC **sd)
2360 struct berval **values;
2361 prs_struct ps;
2362 BOOL ret = False;
2364 values = ldap_get_values_len(ads->ld, msg, field);
2366 if (!values) return False;
2368 if (values[0]) {
2369 prs_init(&ps, values[0]->bv_len, mem_ctx, UNMARSHALL);
2370 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
2371 prs_set_offset(&ps,0);
2373 ret = sec_io_desc("sd", sd, &ps, 1);
2376 ldap_value_free_len(values);
2377 return ret;
2381 * in order to support usernames longer than 21 characters we need to
2382 * use both the sAMAccountName and the userPrincipalName attributes
2383 * It seems that not all users have the userPrincipalName attribute set
2385 * @param ads connection to ads server
2386 * @param mem_ctx TALLOC_CTX for allocating sid array
2387 * @param msg Results of search
2388 * @return the username
2390 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, void *msg)
2392 #if 0 /* JERRY */
2393 char *ret, *p;
2395 /* lookup_name() only works on the sAMAccountName to
2396 returning the username portion of userPrincipalName
2397 breaks winbindd_getpwnam() */
2399 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2400 if (ret && (p = strchr_m(ret, '@'))) {
2401 *p = 0;
2402 return ret;
2404 #endif
2405 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2410 * find the update serial number - this is the core of the ldap cache
2411 * @param ads connection to ads server
2412 * @param ads connection to ADS server
2413 * @param usn Pointer to retrieved update serial number
2414 * @return status of search
2416 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2418 const char *attrs[] = {"highestCommittedUSN", NULL};
2419 ADS_STATUS status;
2420 void *res;
2422 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2423 if (!ADS_ERR_OK(status))
2424 return status;
2426 if (ads_count_replies(ads, res) != 1) {
2427 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2430 ads_pull_uint32(ads, res, "highestCommittedUSN", usn);
2431 ads_msgfree(ads, res);
2432 return ADS_SUCCESS;
2435 /* parse a ADS timestring - typical string is
2436 '20020917091222.0Z0' which means 09:12.22 17th September
2437 2002, timezone 0 */
2438 static time_t ads_parse_time(const char *str)
2440 struct tm tm;
2442 ZERO_STRUCT(tm);
2444 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2445 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2446 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2447 return 0;
2449 tm.tm_year -= 1900;
2450 tm.tm_mon -= 1;
2452 return timegm(&tm);
2456 const char *ads_get_attrname_by_oid(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char * OID)
2458 ADS_STATUS rc;
2459 int count = 0;
2460 void *res = NULL;
2461 char *expr = NULL;
2462 const char *attrs[] = { "lDAPDisplayName", NULL };
2464 if (ads == NULL || mem_ctx == NULL || OID == NULL) {
2465 goto failed;
2468 expr = talloc_asprintf(mem_ctx, "(attributeId=%s)", OID);
2469 if (expr == NULL) {
2470 goto failed;
2473 rc = ads_do_search_retry(ads, ads->config.schema_path,
2474 LDAP_SCOPE_SUBTREE, expr, attrs, &res);
2475 if (!ADS_ERR_OK(rc)) {
2476 goto failed;
2479 count = ads_count_replies(ads, res);
2480 if (count == 0 || !res) {
2481 goto failed;
2484 return ads_pull_string(ads, mem_ctx, res, "lDAPDisplayName");
2486 failed:
2487 DEBUG(0,("ads_get_attrname_by_oid: failed to retrieve name for oid: %s\n",
2488 OID));
2490 return NULL;
2494 * Find the servers name and realm - this can be done before authentication
2495 * The ldapServiceName field on w2k looks like this:
2496 * vnet3.home.samba.org:win2000-vnet3$@VNET3.HOME.SAMBA.ORG
2497 * @param ads connection to ads server
2498 * @return status of search
2500 ADS_STATUS ads_server_info(ADS_STRUCT *ads)
2502 const char *attrs[] = {"ldapServiceName",
2503 "currentTime",
2504 "schemaNamingContext", NULL};
2505 ADS_STATUS status;
2506 void *res;
2507 char *value;
2508 char *p;
2509 char *timestr;
2510 char *schema_path;
2511 TALLOC_CTX *ctx;
2513 if (!(ctx = talloc_init("ads_server_info"))) {
2514 return ADS_ERROR(LDAP_NO_MEMORY);
2517 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2518 if (!ADS_ERR_OK(status)) {
2519 talloc_destroy(ctx);
2520 return status;
2523 value = ads_pull_string(ads, ctx, res, "ldapServiceName");
2524 if (!value) {
2525 ads_msgfree(ads, res);
2526 talloc_destroy(ctx);
2527 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2530 timestr = ads_pull_string(ads, ctx, res, "currentTime");
2531 if (!timestr) {
2532 ads_msgfree(ads, res);
2533 talloc_destroy(ctx);
2534 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2537 schema_path = ads_pull_string(ads, ctx, res, "schemaNamingContext");
2538 if (!schema_path) {
2539 ads_msgfree(ads, res);
2540 talloc_destroy(ctx);
2541 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2544 SAFE_FREE(ads->config.schema_path);
2545 ads->config.schema_path = SMB_STRDUP(schema_path);
2547 ads_msgfree(ads, res);
2549 p = strchr(value, ':');
2550 if (!p) {
2551 talloc_destroy(ctx);
2552 DEBUG(1, ("ads_server_info: returned ldap server name did not contain a ':' "
2553 "so was deemed invalid\n"));
2554 return ADS_ERROR(LDAP_DECODING_ERROR);
2557 SAFE_FREE(ads->config.ldap_server_name);
2559 ads->config.ldap_server_name = SMB_STRDUP(p+1);
2560 p = strchr(ads->config.ldap_server_name, '$');
2561 if (!p || p[1] != '@') {
2562 talloc_destroy(ctx);
2563 DEBUG(1, ("ads_server_info: returned ldap server name (%s) does not contain '$@'"
2564 " so was deemed invalid\n", ads->config.ldap_server_name));
2565 SAFE_FREE(ads->config.ldap_server_name);
2566 return ADS_ERROR(LDAP_DECODING_ERROR);
2569 *p = 0;
2571 SAFE_FREE(ads->config.realm);
2572 SAFE_FREE(ads->config.bind_path);
2574 ads->config.realm = SMB_STRDUP(p+2);
2575 ads->config.bind_path = ads_build_dn(ads->config.realm);
2577 DEBUG(3,("got ldap server name %s@%s, using bind path: %s\n",
2578 ads->config.ldap_server_name, ads->config.realm,
2579 ads->config.bind_path));
2581 ads->config.current_time = ads_parse_time(timestr);
2583 if (ads->config.current_time != 0) {
2584 ads->auth.time_offset = ads->config.current_time - time(NULL);
2585 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2588 talloc_destroy(ctx);
2590 return ADS_SUCCESS;
2594 * Check for "Services for Unix"-Schema and load some attributes into the ADS_STRUCT
2595 * @param ads connection to ads server
2596 * @return BOOL status of search (False if one or more attributes couldn't be
2597 * found in Active Directory)
2598 **/
2599 BOOL ads_check_sfu_mapping(ADS_STRUCT *ads)
2601 BOOL ret = False;
2602 TALLOC_CTX *ctx = NULL;
2603 const char *gidnumber, *uidnumber, *homedir, *shell, *gecos;
2605 ctx = talloc_init("ads_check_sfu_mapping");
2606 if (ctx == NULL)
2607 goto done;
2609 gidnumber = ads_get_attrname_by_oid(ads, ctx, ADS_ATTR_SFU_GIDNUMBER_OID);
2610 if (gidnumber == NULL)
2611 goto done;
2612 ads->schema.sfu_gidnumber_attr = SMB_STRDUP(gidnumber);
2614 uidnumber = ads_get_attrname_by_oid(ads, ctx, ADS_ATTR_SFU_UIDNUMBER_OID);
2615 if (uidnumber == NULL)
2616 goto done;
2617 ads->schema.sfu_uidnumber_attr = SMB_STRDUP(uidnumber);
2619 homedir = ads_get_attrname_by_oid(ads, ctx, ADS_ATTR_SFU_HOMEDIR_OID);
2620 if (homedir == NULL)
2621 goto done;
2622 ads->schema.sfu_homedir_attr = SMB_STRDUP(homedir);
2624 shell = ads_get_attrname_by_oid(ads, ctx, ADS_ATTR_SFU_SHELL_OID);
2625 if (shell == NULL)
2626 goto done;
2627 ads->schema.sfu_shell_attr = SMB_STRDUP(shell);
2629 gecos = ads_get_attrname_by_oid(ads, ctx, ADS_ATTR_SFU_GECOS_OID);
2630 if (gecos == NULL)
2631 goto done;
2632 ads->schema.sfu_gecos_attr = SMB_STRDUP(gecos);
2634 ret = True;
2635 done:
2636 if (ctx)
2637 talloc_destroy(ctx);
2639 return ret;
2643 * find the domain sid for our domain
2644 * @param ads connection to ads server
2645 * @param sid Pointer to domain sid
2646 * @return status of search
2648 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2650 const char *attrs[] = {"objectSid", NULL};
2651 void *res;
2652 ADS_STATUS rc;
2654 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2655 attrs, &res);
2656 if (!ADS_ERR_OK(rc)) return rc;
2657 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2658 ads_msgfree(ads, res);
2659 return ADS_ERROR_SYSTEM(ENOENT);
2661 ads_msgfree(ads, res);
2663 return ADS_SUCCESS;
2666 /* this is rather complex - we need to find the allternate (netbios) name
2667 for the domain, but there isn't a simple query to do this. Instead
2668 we look for the principle names on the DCs account and find one that has
2669 the right form, then extract the netbios name of the domain from that
2671 NOTE! better method is this:
2673 bin/net -Uadministrator%XXXXX ads search '(&(objectclass=crossref)(dnsroot=VNET3.HOME.SAMBA.ORG))' nETBIOSName
2675 but you need to force the bind path to match the configurationNamingContext from the rootDSE
2678 ADS_STATUS ads_workgroup_name(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **workgroup)
2680 char *expr;
2681 ADS_STATUS rc;
2682 char **principles;
2683 char *prefix;
2684 int prefix_length;
2685 int i;
2686 void *res;
2687 const char *attrs[] = {"servicePrincipalName", NULL};
2688 size_t num_principals;
2690 (*workgroup) = NULL;
2692 asprintf(&expr, "(&(objectclass=computer)(dnshostname=%s.%s))",
2693 ads->config.ldap_server_name, ads->config.realm);
2694 if (expr == NULL) {
2695 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2698 rc = ads_search(ads, &res, expr, attrs);
2699 free(expr);
2701 if (!ADS_ERR_OK(rc)) {
2702 return rc;
2705 principles = ads_pull_strings(ads, mem_ctx, res,
2706 "servicePrincipalName", &num_principals);
2708 ads_msgfree(ads, res);
2710 if (!principles) {
2711 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2714 asprintf(&prefix, "HOST/%s.%s/",
2715 ads->config.ldap_server_name,
2716 ads->config.realm);
2718 prefix_length = strlen(prefix);
2720 for (i=0;principles[i]; i++) {
2721 if (strnequal(principles[i], prefix, prefix_length) &&
2722 !strequal(ads->config.realm, principles[i]+prefix_length) &&
2723 !strchr(principles[i]+prefix_length, '.')) {
2724 /* found an alternate (short) name for the domain. */
2725 DEBUG(3,("Found alternate name '%s' for realm '%s'\n",
2726 principles[i]+prefix_length,
2727 ads->config.realm));
2728 (*workgroup) = talloc_strdup(mem_ctx, principles[i]+prefix_length);
2729 break;
2732 free(prefix);
2734 if (!*workgroup) {
2735 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2738 return ADS_SUCCESS;
2742 * find our site name
2743 * @param ads connection to ads server
2744 * @param mem_ctx Pointer to talloc context
2745 * @param site_name Pointer to the sitename
2746 * @return status of search
2748 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2750 ADS_STATUS status;
2751 void *res;
2752 const char *dn, *service_name;
2753 const char *attrs[] = { "dsServiceName", NULL };
2755 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2756 if (!ADS_ERR_OK(status)) {
2757 return status;
2760 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2761 if (service_name == NULL) {
2762 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2765 /* go up three levels */
2766 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2767 if (dn == NULL) {
2768 return ADS_ERROR(LDAP_NO_MEMORY);
2771 *site_name = talloc_strdup(mem_ctx, dn);
2772 if (*site_name == NULL) {
2773 return ADS_ERROR(LDAP_NO_MEMORY);
2776 ads_msgfree(ads, res);
2778 return status;
2780 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2785 * find the site dn where a machine resides
2786 * @param ads connection to ads server
2787 * @param mem_ctx Pointer to talloc context
2788 * @param computer_name name of the machine
2789 * @param site_name Pointer to the sitename
2790 * @return status of search
2792 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
2794 ADS_STATUS status;
2795 void *res;
2796 const char *parent, *config_context, *filter;
2797 const char *attrs[] = { "configurationNamingContext", NULL };
2798 char *dn;
2800 /* shortcut a query */
2801 if (strequal(computer_name, ads->config.ldap_server_name)) {
2802 return ads_site_dn(ads, mem_ctx, site_dn);
2805 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2806 if (!ADS_ERR_OK(status)) {
2807 return status;
2810 config_context = ads_pull_string(ads, mem_ctx, res, "configurationNamingContext");
2811 if (config_context == NULL) {
2812 return ADS_ERROR(LDAP_NO_MEMORY);
2815 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
2816 if (filter == NULL) {
2817 return ADS_ERROR(LDAP_NO_MEMORY);
2820 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE, filter, NULL, &res);
2821 if (!ADS_ERR_OK(status)) {
2822 return status;
2825 if (ads_count_replies(ads, res) != 1) {
2826 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2829 dn = ads_get_dn(ads, res);
2830 if (dn == NULL) {
2831 return ADS_ERROR(LDAP_NO_MEMORY);
2834 /* go up three levels */
2835 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
2836 if (parent == NULL) {
2837 ads_memfree(ads, dn);
2838 return ADS_ERROR(LDAP_NO_MEMORY);
2841 *site_dn = talloc_strdup(mem_ctx, parent);
2842 if (*site_dn == NULL) {
2843 ads_memfree(ads, dn);
2844 ADS_ERROR(LDAP_NO_MEMORY);
2847 ads_memfree(ads, dn);
2848 ads_msgfree(ads, res);
2850 return status;
2854 * get the upn suffixes for a domain
2855 * @param ads connection to ads server
2856 * @param mem_ctx Pointer to talloc context
2857 * @param suffixes Pointer to an array of suffixes
2858 * @param site_name Pointer to the number of suffixes
2859 * @return status of search
2861 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char **suffixes, size_t *num_suffixes)
2863 ADS_STATUS status;
2864 void *res;
2865 const char *config_context, *base;
2866 const char *attrs[] = { "configurationNamingContext", NULL };
2867 const char *attrs2[] = { "uPNSuffixes", NULL };
2869 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2870 if (!ADS_ERR_OK(status)) {
2871 return status;
2874 config_context = ads_pull_string(ads, mem_ctx, res, "configurationNamingContext");
2875 if (config_context == NULL) {
2876 return ADS_ERROR(LDAP_NO_MEMORY);
2879 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
2880 if (base == NULL) {
2881 return ADS_ERROR(LDAP_NO_MEMORY);
2884 status = ads_search_dn(ads, &res, base, attrs2);
2885 if (!ADS_ERR_OK(status)) {
2886 return status;
2889 if (ads_count_replies(ads, res) != 1) {
2890 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2893 suffixes = ads_pull_strings(ads, mem_ctx, &res, "uPNSuffixes", num_suffixes);
2894 if (suffixes == NULL) {
2895 ads_msgfree(ads, res);
2896 return ADS_ERROR(LDAP_NO_MEMORY);
2899 ads_msgfree(ads, res);
2901 return status;
2904 #endif