r14252: Fix Coverity #72: free alloc'ed storage before return. Also found one
[Samba/bb.git] / source3 / libads / ldap.c
blobe1cea533a0e39b027f2de6663462ffe395e19db4
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) {
1918 SAFE_FREE(escaped_hostname);
1919 return ADS_ERROR(LDAP_SERVER_DOWN);
1922 ret = ADS_ERROR(LDAP_SUCCESS);
1924 if (!escaped_hostname) {
1925 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1928 if (asprintf(&expr, "(samAccountName=%s$)", escaped_hostname) == -1) {
1929 DEBUG(1, ("ads_set_machine_sd: asprintf failed!\n"));
1930 SAFE_FREE(escaped_hostname);
1931 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1934 SAFE_FREE(escaped_hostname);
1936 ret = ads_search(ads, (void *) &res, expr, attrs);
1938 SAFE_FREE(expr);
1940 if (!ADS_ERR_OK(ret)) return ret;
1942 if ( !(msg = ads_first_entry(ads, res) )) {
1943 ret = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1944 goto ads_set_sd_error;
1947 if (!ads_pull_sid(ads, msg, attrs[1], &sid)) {
1948 ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
1949 goto ads_set_sd_error;
1952 if (!(ctx = talloc_init("sec_io_desc"))) {
1953 ret = ADS_ERROR(LDAP_NO_MEMORY);
1954 goto ads_set_sd_error;
1957 if (!ads_pull_sd(ads, ctx, msg, attrs[0], &psd)) {
1958 ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
1959 goto ads_set_sd_error;
1962 status = sec_desc_add_sid(ctx, &psd, &sid, SEC_RIGHTS_FULL_CTRL, &sd_size);
1964 if (!NT_STATUS_IS_OK(status)) {
1965 ret = ADS_ERROR_NT(status);
1966 goto ads_set_sd_error;
1969 if (!prs_init(&ps_wire, sd_size, ctx, MARSHALL)) {
1970 ret = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1973 if (!sec_io_desc("sd_wire", &psd, &ps_wire, 1)) {
1974 ret = ADS_ERROR(LDAP_NO_MEMORY);
1975 goto ads_set_sd_error;
1978 #if 0
1979 file_save("/tmp/sec_desc.new", ps_wire.data_p, sd_size);
1980 #endif
1981 if (!(mods = ads_init_mods(ctx))) return ADS_ERROR(LDAP_NO_MEMORY);
1983 bval.bv_len = prs_offset(&ps_wire);
1984 bval.bv_val = TALLOC(ctx, bval.bv_len);
1985 if (!bval.bv_val) {
1986 ret = ADS_ERROR(LDAP_NO_MEMORY);
1987 goto ads_set_sd_error;
1990 prs_set_offset(&ps_wire, 0);
1992 if (!prs_copy_data_out(bval.bv_val, &ps_wire, bval.bv_len)) {
1993 ret = ADS_ERROR(LDAP_NO_MEMORY);
1994 goto ads_set_sd_error;
1997 ret = ads_mod_ber(ctx, &mods, attrs[0], &bval);
1998 if (ADS_ERR_OK(ret)) {
1999 ret = ads_gen_mod(ads, dn, mods);
2002 ads_set_sd_error:
2003 ads_msgfree(ads, res);
2004 prs_mem_free(&ps_wire);
2005 talloc_destroy(ctx);
2006 return ret;
2010 * pull the first entry from a ADS result
2011 * @param ads connection to ads server
2012 * @param res Results of search
2013 * @return first entry from result
2015 void *ads_first_entry(ADS_STRUCT *ads, void *res)
2017 return (void *)ldap_first_entry(ads->ld, (LDAPMessage *)res);
2021 * pull the next entry from a ADS result
2022 * @param ads connection to ads server
2023 * @param res Results of search
2024 * @return next entry from result
2026 void *ads_next_entry(ADS_STRUCT *ads, void *res)
2028 return (void *)ldap_next_entry(ads->ld, (LDAPMessage *)res);
2032 * pull a single string from a ADS result
2033 * @param ads connection to ads server
2034 * @param mem_ctx TALLOC_CTX to use for allocating result string
2035 * @param msg Results of search
2036 * @param field Attribute to retrieve
2037 * @return Result string in talloc context
2039 char *ads_pull_string(ADS_STRUCT *ads,
2040 TALLOC_CTX *mem_ctx, void *msg, const char *field)
2042 char **values;
2043 char *ret = NULL;
2044 char *ux_string;
2045 size_t rc;
2047 values = ldap_get_values(ads->ld, msg, field);
2048 if (!values)
2049 return NULL;
2051 if (values[0]) {
2052 rc = pull_utf8_talloc(mem_ctx, &ux_string,
2053 values[0]);
2054 if (rc != (size_t)-1)
2055 ret = ux_string;
2058 ldap_value_free(values);
2059 return ret;
2063 * pull an array of strings from a ADS result
2064 * @param ads connection to ads server
2065 * @param mem_ctx TALLOC_CTX to use for allocating result string
2066 * @param msg Results of search
2067 * @param field Attribute to retrieve
2068 * @return Result strings in talloc context
2070 char **ads_pull_strings(ADS_STRUCT *ads,
2071 TALLOC_CTX *mem_ctx, void *msg, const char *field,
2072 size_t *num_values)
2074 char **values;
2075 char **ret = NULL;
2076 int i;
2078 values = ldap_get_values(ads->ld, msg, field);
2079 if (!values)
2080 return NULL;
2082 *num_values = ldap_count_values(values);
2084 ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
2085 if (!ret) {
2086 ldap_value_free(values);
2087 return NULL;
2090 for (i=0;i<*num_values;i++) {
2091 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
2092 ldap_value_free(values);
2093 return NULL;
2096 ret[i] = NULL;
2098 ldap_value_free(values);
2099 return ret;
2103 * pull an array of strings from a ADS result
2104 * (handle large multivalue attributes with range retrieval)
2105 * @param ads connection to ads server
2106 * @param mem_ctx TALLOC_CTX to use for allocating result string
2107 * @param msg Results of search
2108 * @param field Attribute to retrieve
2109 * @param current_strings strings returned by a previous call to this function
2110 * @param next_attribute The next query should ask for this attribute
2111 * @param num_values How many values did we get this time?
2112 * @param more_values Are there more values to get?
2113 * @return Result strings in talloc context
2115 char **ads_pull_strings_range(ADS_STRUCT *ads,
2116 TALLOC_CTX *mem_ctx,
2117 void *msg, const char *field,
2118 char **current_strings,
2119 const char **next_attribute,
2120 size_t *num_strings,
2121 BOOL *more_strings)
2123 char *attr;
2124 char *expected_range_attrib, *range_attr;
2125 BerElement *ptr = NULL;
2126 char **strings;
2127 char **new_strings;
2128 size_t num_new_strings;
2129 unsigned long int range_start;
2130 unsigned long int range_end;
2132 /* we might have been given the whole lot anyway */
2133 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2134 *more_strings = False;
2135 return strings;
2138 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2140 /* look for Range result */
2141 for (attr = ldap_first_attribute(ads->ld, (LDAPMessage *)msg, &ptr);
2142 attr;
2143 attr = ldap_next_attribute(ads->ld, (LDAPMessage *)msg, ptr)) {
2144 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2145 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2146 range_attr = attr;
2147 break;
2149 ldap_memfree(attr);
2151 if (!attr) {
2152 ber_free(ptr, 0);
2153 /* nothing here - this field is just empty */
2154 *more_strings = False;
2155 return NULL;
2158 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2159 &range_start, &range_end) == 2) {
2160 *more_strings = True;
2161 } else {
2162 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2163 &range_start) == 1) {
2164 *more_strings = False;
2165 } else {
2166 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2167 range_attr));
2168 ldap_memfree(range_attr);
2169 *more_strings = False;
2170 return NULL;
2174 if ((*num_strings) != range_start) {
2175 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2176 " - aborting range retreival\n",
2177 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2178 ldap_memfree(range_attr);
2179 *more_strings = False;
2180 return NULL;
2183 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2185 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2186 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2187 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2188 range_attr, (unsigned long int)range_end - range_start + 1,
2189 (unsigned long int)num_new_strings));
2190 ldap_memfree(range_attr);
2191 *more_strings = False;
2192 return NULL;
2195 strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2196 *num_strings + num_new_strings);
2198 if (strings == NULL) {
2199 ldap_memfree(range_attr);
2200 *more_strings = False;
2201 return NULL;
2204 memcpy(&strings[*num_strings], new_strings,
2205 sizeof(*new_strings) * num_new_strings);
2207 (*num_strings) += num_new_strings;
2209 if (*more_strings) {
2210 *next_attribute = talloc_asprintf(mem_ctx,
2211 "%s;range=%d-*",
2212 field,
2213 (int)*num_strings);
2215 if (!*next_attribute) {
2216 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2217 ldap_memfree(range_attr);
2218 *more_strings = False;
2219 return NULL;
2223 ldap_memfree(range_attr);
2225 return strings;
2229 * pull a single uint32 from a ADS result
2230 * @param ads connection to ads server
2231 * @param msg Results of search
2232 * @param field Attribute to retrieve
2233 * @param v Pointer to int to store result
2234 * @return boolean inidicating success
2236 BOOL ads_pull_uint32(ADS_STRUCT *ads,
2237 void *msg, const char *field, uint32 *v)
2239 char **values;
2241 values = ldap_get_values(ads->ld, msg, field);
2242 if (!values)
2243 return False;
2244 if (!values[0]) {
2245 ldap_value_free(values);
2246 return False;
2249 *v = atoi(values[0]);
2250 ldap_value_free(values);
2251 return True;
2255 * pull a single objectGUID from an ADS result
2256 * @param ads connection to ADS server
2257 * @param msg results of search
2258 * @param guid 37-byte area to receive text guid
2259 * @return boolean indicating success
2261 BOOL ads_pull_guid(ADS_STRUCT *ads,
2262 void *msg, struct uuid *guid)
2264 char **values;
2265 UUID_FLAT flat_guid;
2267 values = ldap_get_values(ads->ld, msg, "objectGUID");
2268 if (!values)
2269 return False;
2271 if (values[0]) {
2272 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2273 smb_uuid_unpack(flat_guid, guid);
2274 ldap_value_free(values);
2275 return True;
2277 ldap_value_free(values);
2278 return False;
2284 * pull a single DOM_SID from a ADS result
2285 * @param ads connection to ads server
2286 * @param msg Results of search
2287 * @param field Attribute to retrieve
2288 * @param sid Pointer to sid to store result
2289 * @return boolean inidicating success
2291 BOOL ads_pull_sid(ADS_STRUCT *ads,
2292 void *msg, const char *field, DOM_SID *sid)
2294 struct berval **values;
2295 BOOL ret = False;
2297 values = ldap_get_values_len(ads->ld, msg, field);
2299 if (!values)
2300 return False;
2302 if (values[0])
2303 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2305 ldap_value_free_len(values);
2306 return ret;
2310 * pull an array of DOM_SIDs from a ADS result
2311 * @param ads connection to ads server
2312 * @param mem_ctx TALLOC_CTX for allocating sid array
2313 * @param msg Results of search
2314 * @param field Attribute to retrieve
2315 * @param sids pointer to sid array to allocate
2316 * @return the count of SIDs pulled
2318 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2319 void *msg, const char *field, DOM_SID **sids)
2321 struct berval **values;
2322 BOOL ret;
2323 int count, i;
2325 values = ldap_get_values_len(ads->ld, msg, field);
2327 if (!values)
2328 return 0;
2330 for (i=0; values[i]; i++)
2331 /* nop */ ;
2333 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2334 if (!(*sids)) {
2335 ldap_value_free_len(values);
2336 return 0;
2339 count = 0;
2340 for (i=0; values[i]; i++) {
2341 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2342 if (ret) {
2343 fstring sid;
2344 DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid, &(*sids)[count])));
2345 count++;
2349 ldap_value_free_len(values);
2350 return count;
2354 * pull a SEC_DESC from a ADS result
2355 * @param ads connection to ads server
2356 * @param mem_ctx TALLOC_CTX for allocating sid array
2357 * @param msg Results of search
2358 * @param field Attribute to retrieve
2359 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2360 * @return boolean inidicating success
2362 BOOL ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2363 void *msg, const char *field, SEC_DESC **sd)
2365 struct berval **values;
2366 prs_struct ps;
2367 BOOL ret = False;
2369 values = ldap_get_values_len(ads->ld, msg, field);
2371 if (!values) return False;
2373 if (values[0]) {
2374 prs_init(&ps, values[0]->bv_len, mem_ctx, UNMARSHALL);
2375 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
2376 prs_set_offset(&ps,0);
2378 ret = sec_io_desc("sd", sd, &ps, 1);
2381 ldap_value_free_len(values);
2382 return ret;
2386 * in order to support usernames longer than 21 characters we need to
2387 * use both the sAMAccountName and the userPrincipalName attributes
2388 * It seems that not all users have the userPrincipalName attribute set
2390 * @param ads connection to ads server
2391 * @param mem_ctx TALLOC_CTX for allocating sid array
2392 * @param msg Results of search
2393 * @return the username
2395 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, void *msg)
2397 #if 0 /* JERRY */
2398 char *ret, *p;
2400 /* lookup_name() only works on the sAMAccountName to
2401 returning the username portion of userPrincipalName
2402 breaks winbindd_getpwnam() */
2404 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2405 if (ret && (p = strchr_m(ret, '@'))) {
2406 *p = 0;
2407 return ret;
2409 #endif
2410 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2415 * find the update serial number - this is the core of the ldap cache
2416 * @param ads connection to ads server
2417 * @param ads connection to ADS server
2418 * @param usn Pointer to retrieved update serial number
2419 * @return status of search
2421 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2423 const char *attrs[] = {"highestCommittedUSN", NULL};
2424 ADS_STATUS status;
2425 void *res;
2427 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2428 if (!ADS_ERR_OK(status))
2429 return status;
2431 if (ads_count_replies(ads, res) != 1) {
2432 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2435 ads_pull_uint32(ads, res, "highestCommittedUSN", usn);
2436 ads_msgfree(ads, res);
2437 return ADS_SUCCESS;
2440 /* parse a ADS timestring - typical string is
2441 '20020917091222.0Z0' which means 09:12.22 17th September
2442 2002, timezone 0 */
2443 static time_t ads_parse_time(const char *str)
2445 struct tm tm;
2447 ZERO_STRUCT(tm);
2449 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2450 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2451 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2452 return 0;
2454 tm.tm_year -= 1900;
2455 tm.tm_mon -= 1;
2457 return timegm(&tm);
2461 const char *ads_get_attrname_by_oid(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char * OID)
2463 ADS_STATUS rc;
2464 int count = 0;
2465 void *res = NULL;
2466 char *expr = NULL;
2467 const char *attrs[] = { "lDAPDisplayName", NULL };
2469 if (ads == NULL || mem_ctx == NULL || OID == NULL) {
2470 goto failed;
2473 expr = talloc_asprintf(mem_ctx, "(attributeId=%s)", OID);
2474 if (expr == NULL) {
2475 goto failed;
2478 rc = ads_do_search_retry(ads, ads->config.schema_path,
2479 LDAP_SCOPE_SUBTREE, expr, attrs, &res);
2480 if (!ADS_ERR_OK(rc)) {
2481 goto failed;
2484 count = ads_count_replies(ads, res);
2485 if (count == 0 || !res) {
2486 goto failed;
2489 return ads_pull_string(ads, mem_ctx, res, "lDAPDisplayName");
2491 failed:
2492 DEBUG(0,("ads_get_attrname_by_oid: failed to retrieve name for oid: %s\n",
2493 OID));
2495 return NULL;
2499 * Find the servers name and realm - this can be done before authentication
2500 * The ldapServiceName field on w2k looks like this:
2501 * vnet3.home.samba.org:win2000-vnet3$@VNET3.HOME.SAMBA.ORG
2502 * @param ads connection to ads server
2503 * @return status of search
2505 ADS_STATUS ads_server_info(ADS_STRUCT *ads)
2507 const char *attrs[] = {"ldapServiceName",
2508 "currentTime",
2509 "schemaNamingContext", NULL};
2510 ADS_STATUS status;
2511 void *res;
2512 char *value;
2513 char *p;
2514 char *timestr;
2515 char *schema_path;
2516 TALLOC_CTX *ctx;
2518 if (!(ctx = talloc_init("ads_server_info"))) {
2519 return ADS_ERROR(LDAP_NO_MEMORY);
2522 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2523 if (!ADS_ERR_OK(status)) {
2524 talloc_destroy(ctx);
2525 return status;
2528 value = ads_pull_string(ads, ctx, res, "ldapServiceName");
2529 if (!value) {
2530 ads_msgfree(ads, res);
2531 talloc_destroy(ctx);
2532 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2535 timestr = ads_pull_string(ads, ctx, res, "currentTime");
2536 if (!timestr) {
2537 ads_msgfree(ads, res);
2538 talloc_destroy(ctx);
2539 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2542 schema_path = ads_pull_string(ads, ctx, res, "schemaNamingContext");
2543 if (!schema_path) {
2544 ads_msgfree(ads, res);
2545 talloc_destroy(ctx);
2546 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2549 SAFE_FREE(ads->config.schema_path);
2550 ads->config.schema_path = SMB_STRDUP(schema_path);
2552 ads_msgfree(ads, res);
2554 p = strchr(value, ':');
2555 if (!p) {
2556 talloc_destroy(ctx);
2557 DEBUG(1, ("ads_server_info: returned ldap server name did not contain a ':' "
2558 "so was deemed invalid\n"));
2559 return ADS_ERROR(LDAP_DECODING_ERROR);
2562 SAFE_FREE(ads->config.ldap_server_name);
2564 ads->config.ldap_server_name = SMB_STRDUP(p+1);
2565 p = strchr(ads->config.ldap_server_name, '$');
2566 if (!p || p[1] != '@') {
2567 talloc_destroy(ctx);
2568 DEBUG(1, ("ads_server_info: returned ldap server name (%s) does not contain '$@'"
2569 " so was deemed invalid\n", ads->config.ldap_server_name));
2570 SAFE_FREE(ads->config.ldap_server_name);
2571 return ADS_ERROR(LDAP_DECODING_ERROR);
2574 *p = 0;
2576 SAFE_FREE(ads->config.realm);
2577 SAFE_FREE(ads->config.bind_path);
2579 ads->config.realm = SMB_STRDUP(p+2);
2580 ads->config.bind_path = ads_build_dn(ads->config.realm);
2582 DEBUG(3,("got ldap server name %s@%s, using bind path: %s\n",
2583 ads->config.ldap_server_name, ads->config.realm,
2584 ads->config.bind_path));
2586 ads->config.current_time = ads_parse_time(timestr);
2588 if (ads->config.current_time != 0) {
2589 ads->auth.time_offset = ads->config.current_time - time(NULL);
2590 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2593 talloc_destroy(ctx);
2595 return ADS_SUCCESS;
2599 * Check for "Services for Unix"-Schema and load some attributes into the ADS_STRUCT
2600 * @param ads connection to ads server
2601 * @return BOOL status of search (False if one or more attributes couldn't be
2602 * found in Active Directory)
2603 **/
2604 BOOL ads_check_sfu_mapping(ADS_STRUCT *ads)
2606 BOOL ret = False;
2607 TALLOC_CTX *ctx = NULL;
2608 const char *gidnumber, *uidnumber, *homedir, *shell, *gecos;
2610 ctx = talloc_init("ads_check_sfu_mapping");
2611 if (ctx == NULL)
2612 goto done;
2614 gidnumber = ads_get_attrname_by_oid(ads, ctx, ADS_ATTR_SFU_GIDNUMBER_OID);
2615 if (gidnumber == NULL)
2616 goto done;
2617 ads->schema.sfu_gidnumber_attr = SMB_STRDUP(gidnumber);
2619 uidnumber = ads_get_attrname_by_oid(ads, ctx, ADS_ATTR_SFU_UIDNUMBER_OID);
2620 if (uidnumber == NULL)
2621 goto done;
2622 ads->schema.sfu_uidnumber_attr = SMB_STRDUP(uidnumber);
2624 homedir = ads_get_attrname_by_oid(ads, ctx, ADS_ATTR_SFU_HOMEDIR_OID);
2625 if (homedir == NULL)
2626 goto done;
2627 ads->schema.sfu_homedir_attr = SMB_STRDUP(homedir);
2629 shell = ads_get_attrname_by_oid(ads, ctx, ADS_ATTR_SFU_SHELL_OID);
2630 if (shell == NULL)
2631 goto done;
2632 ads->schema.sfu_shell_attr = SMB_STRDUP(shell);
2634 gecos = ads_get_attrname_by_oid(ads, ctx, ADS_ATTR_SFU_GECOS_OID);
2635 if (gecos == NULL)
2636 goto done;
2637 ads->schema.sfu_gecos_attr = SMB_STRDUP(gecos);
2639 ret = True;
2640 done:
2641 if (ctx)
2642 talloc_destroy(ctx);
2644 return ret;
2648 * find the domain sid for our domain
2649 * @param ads connection to ads server
2650 * @param sid Pointer to domain sid
2651 * @return status of search
2653 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2655 const char *attrs[] = {"objectSid", NULL};
2656 void *res;
2657 ADS_STATUS rc;
2659 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2660 attrs, &res);
2661 if (!ADS_ERR_OK(rc)) return rc;
2662 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2663 ads_msgfree(ads, res);
2664 return ADS_ERROR_SYSTEM(ENOENT);
2666 ads_msgfree(ads, res);
2668 return ADS_SUCCESS;
2671 /* this is rather complex - we need to find the allternate (netbios) name
2672 for the domain, but there isn't a simple query to do this. Instead
2673 we look for the principle names on the DCs account and find one that has
2674 the right form, then extract the netbios name of the domain from that
2676 NOTE! better method is this:
2678 bin/net -Uadministrator%XXXXX ads search '(&(objectclass=crossref)(dnsroot=VNET3.HOME.SAMBA.ORG))' nETBIOSName
2680 but you need to force the bind path to match the configurationNamingContext from the rootDSE
2683 ADS_STATUS ads_workgroup_name(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **workgroup)
2685 char *expr;
2686 ADS_STATUS rc;
2687 char **principles;
2688 char *prefix;
2689 int prefix_length;
2690 int i;
2691 void *res;
2692 const char *attrs[] = {"servicePrincipalName", NULL};
2693 size_t num_principals;
2695 (*workgroup) = NULL;
2697 asprintf(&expr, "(&(objectclass=computer)(dnshostname=%s.%s))",
2698 ads->config.ldap_server_name, ads->config.realm);
2699 if (expr == NULL) {
2700 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2703 rc = ads_search(ads, &res, expr, attrs);
2704 free(expr);
2706 if (!ADS_ERR_OK(rc)) {
2707 return rc;
2710 principles = ads_pull_strings(ads, mem_ctx, res,
2711 "servicePrincipalName", &num_principals);
2713 ads_msgfree(ads, res);
2715 if (!principles) {
2716 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2719 asprintf(&prefix, "HOST/%s.%s/",
2720 ads->config.ldap_server_name,
2721 ads->config.realm);
2723 prefix_length = strlen(prefix);
2725 for (i=0;principles[i]; i++) {
2726 if (strnequal(principles[i], prefix, prefix_length) &&
2727 !strequal(ads->config.realm, principles[i]+prefix_length) &&
2728 !strchr(principles[i]+prefix_length, '.')) {
2729 /* found an alternate (short) name for the domain. */
2730 DEBUG(3,("Found alternate name '%s' for realm '%s'\n",
2731 principles[i]+prefix_length,
2732 ads->config.realm));
2733 (*workgroup) = talloc_strdup(mem_ctx, principles[i]+prefix_length);
2734 break;
2737 free(prefix);
2739 if (!*workgroup) {
2740 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2743 return ADS_SUCCESS;
2747 * find our site name
2748 * @param ads connection to ads server
2749 * @param mem_ctx Pointer to talloc context
2750 * @param site_name Pointer to the sitename
2751 * @return status of search
2753 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2755 ADS_STATUS status;
2756 void *res;
2757 const char *dn, *service_name;
2758 const char *attrs[] = { "dsServiceName", NULL };
2760 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2761 if (!ADS_ERR_OK(status)) {
2762 return status;
2765 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2766 if (service_name == NULL) {
2767 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2770 /* go up three levels */
2771 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2772 if (dn == NULL) {
2773 return ADS_ERROR(LDAP_NO_MEMORY);
2776 *site_name = talloc_strdup(mem_ctx, dn);
2777 if (*site_name == NULL) {
2778 return ADS_ERROR(LDAP_NO_MEMORY);
2781 ads_msgfree(ads, res);
2783 return status;
2785 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2790 * find the site dn where a machine resides
2791 * @param ads connection to ads server
2792 * @param mem_ctx Pointer to talloc context
2793 * @param computer_name name of the machine
2794 * @param site_name Pointer to the sitename
2795 * @return status of search
2797 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
2799 ADS_STATUS status;
2800 void *res;
2801 const char *parent, *config_context, *filter;
2802 const char *attrs[] = { "configurationNamingContext", NULL };
2803 char *dn;
2805 /* shortcut a query */
2806 if (strequal(computer_name, ads->config.ldap_server_name)) {
2807 return ads_site_dn(ads, mem_ctx, site_dn);
2810 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2811 if (!ADS_ERR_OK(status)) {
2812 return status;
2815 config_context = ads_pull_string(ads, mem_ctx, res, "configurationNamingContext");
2816 if (config_context == NULL) {
2817 return ADS_ERROR(LDAP_NO_MEMORY);
2820 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
2821 if (filter == NULL) {
2822 return ADS_ERROR(LDAP_NO_MEMORY);
2825 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE, filter, NULL, &res);
2826 if (!ADS_ERR_OK(status)) {
2827 return status;
2830 if (ads_count_replies(ads, res) != 1) {
2831 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2834 dn = ads_get_dn(ads, res);
2835 if (dn == NULL) {
2836 return ADS_ERROR(LDAP_NO_MEMORY);
2839 /* go up three levels */
2840 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
2841 if (parent == NULL) {
2842 ads_memfree(ads, dn);
2843 return ADS_ERROR(LDAP_NO_MEMORY);
2846 *site_dn = talloc_strdup(mem_ctx, parent);
2847 if (*site_dn == NULL) {
2848 ads_memfree(ads, dn);
2849 ADS_ERROR(LDAP_NO_MEMORY);
2852 ads_memfree(ads, dn);
2853 ads_msgfree(ads, res);
2855 return status;
2859 * get the upn suffixes for a domain
2860 * @param ads connection to ads server
2861 * @param mem_ctx Pointer to talloc context
2862 * @param suffixes Pointer to an array of suffixes
2863 * @param site_name Pointer to the number of suffixes
2864 * @return status of search
2866 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char **suffixes, size_t *num_suffixes)
2868 ADS_STATUS status;
2869 void *res;
2870 const char *config_context, *base;
2871 const char *attrs[] = { "configurationNamingContext", NULL };
2872 const char *attrs2[] = { "uPNSuffixes", NULL };
2874 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2875 if (!ADS_ERR_OK(status)) {
2876 return status;
2879 config_context = ads_pull_string(ads, mem_ctx, res, "configurationNamingContext");
2880 if (config_context == NULL) {
2881 return ADS_ERROR(LDAP_NO_MEMORY);
2884 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
2885 if (base == NULL) {
2886 return ADS_ERROR(LDAP_NO_MEMORY);
2889 status = ads_search_dn(ads, &res, base, attrs2);
2890 if (!ADS_ERR_OK(status)) {
2891 return status;
2894 if (ads_count_replies(ads, res) != 1) {
2895 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2898 suffixes = ads_pull_strings(ads, mem_ctx, &res, "uPNSuffixes", num_suffixes);
2899 if (suffixes == NULL) {
2900 ads_msgfree(ads, res);
2901 return ADS_ERROR(LDAP_NO_MEMORY);
2904 ads_msgfree(ads, res);
2906 return status;
2909 #endif