r14931: Fix #1374: can't join an OU with name that contains '#'
[Samba/bb.git] / source3 / libads / ldap.c
blobc2ebf14d2f93ff55badfdd2d07bd3a1ca99ab531
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 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1088 * @param ads connection to ads server
1089 * @param org_unit Organizational unit
1090 * @return org unit string - caller must free
1092 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1094 char *ret = NULL;
1096 if (!org_unit || !*org_unit) {
1098 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1100 /* samba4 might not yet respond to a wellknownobject-query */
1101 return ret ? ret : SMB_STRDUP("cn=Computers");
1104 if (strequal(org_unit, "Computers")) {
1105 return SMB_STRDUP("cn=Computers");
1108 /* jmcd: removed "\\" from the separation chars, because it is
1109 needed as an escape for chars like '#' which are valid in an
1110 OU name */
1111 return ads_build_path(org_unit, "/", "ou=", 1);
1115 * Get a org unit string for a well-known GUID
1116 * @param ads connection to ads server
1117 * @param wknguid Well known GUID
1118 * @return org unit string - caller must free
1120 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1122 ADS_STATUS status;
1123 void *res;
1124 char *base, *wkn_dn, *ret, **wkn_dn_exp, **bind_dn_exp;
1125 const char *attrs[] = {"distinguishedName", NULL};
1126 int new_ln, wkn_ln, bind_ln, i;
1128 if (wknguid == NULL) {
1129 return NULL;
1132 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1133 DEBUG(1, ("asprintf failed!\n"));
1134 return NULL;
1137 status = ads_search_dn(ads, &res, base, attrs);
1138 if (!ADS_ERR_OK(status)) {
1139 DEBUG(1,("Failed while searching for: %s\n", base));
1140 return NULL;
1142 free(base);
1144 if (ads_count_replies(ads, res) != 1) {
1145 return NULL;
1148 /* substitute the bind-path from the well-known-guid-search result */
1149 wkn_dn = ads_get_dn(ads, res);
1150 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1151 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1153 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1155 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1158 new_ln = wkn_ln - bind_ln;
1160 ret = wkn_dn_exp[0];
1162 for (i=1; i < new_ln; i++) {
1163 char *s;
1164 asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]);
1165 ret = SMB_STRDUP(s);
1166 free(s);
1169 return ret;
1173 * Adds (appends) an item to an attribute array, rather then
1174 * replacing the whole list
1175 * @param ctx An initialized TALLOC_CTX
1176 * @param mods An initialized ADS_MODLIST
1177 * @param name name of the ldap attribute to append to
1178 * @param vals an array of values to add
1179 * @return status of addition
1182 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1183 const char *name, const char **vals)
1185 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name, (const void **) vals);
1189 * Determines the computer account's current KVNO via an LDAP lookup
1190 * @param ads An initialized ADS_STRUCT
1191 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1192 * @return the kvno for the computer account, or -1 in case of a failure.
1195 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *machine_name)
1197 LDAPMessage *res = NULL;
1198 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1199 char *filter;
1200 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1201 char *dn_string = NULL;
1202 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1204 DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name));
1205 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1206 return kvno;
1208 ret = ads_search(ads, (void**)(void *)&res, filter, attrs);
1209 SAFE_FREE(filter);
1210 if (!ADS_ERR_OK(ret) && ads_count_replies(ads, res)) {
1211 DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name));
1212 ads_msgfree(ads, res);
1213 return kvno;
1216 dn_string = ads_get_dn(ads, res);
1217 if (!dn_string) {
1218 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1219 ads_msgfree(ads, res);
1220 return kvno;
1222 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1223 ads_memfree(ads, dn_string);
1225 /* ---------------------------------------------------------
1226 * 0 is returned as a default KVNO from this point on...
1227 * This is done because Windows 2000 does not support key
1228 * version numbers. Chances are that a failure in the next
1229 * step is simply due to Windows 2000 being used for a
1230 * domain controller. */
1231 kvno = 0;
1233 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1234 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1235 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1236 ads_msgfree(ads, res);
1237 return kvno;
1240 /* Success */
1241 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1242 ads_msgfree(ads, res);
1243 return kvno;
1247 * This clears out all registered spn's for a given hostname
1248 * @param ads An initilaized ADS_STRUCT
1249 * @param machine_name the NetBIOS name of the computer.
1250 * @return 0 upon success, non-zero otherwise.
1253 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1255 TALLOC_CTX *ctx;
1256 LDAPMessage *res = NULL;
1257 ADS_MODLIST mods;
1258 const char *servicePrincipalName[1] = {NULL};
1259 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1260 char *dn_string = NULL;
1262 ret = ads_find_machine_acct(ads, (void **)(void *)&res, machine_name);
1263 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1264 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1265 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1266 ads_msgfree(ads, res);
1267 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1270 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1271 ctx = talloc_init("ads_clear_service_principal_names");
1272 if (!ctx) {
1273 ads_msgfree(ads, res);
1274 return ADS_ERROR(LDAP_NO_MEMORY);
1277 if (!(mods = ads_init_mods(ctx))) {
1278 talloc_destroy(ctx);
1279 ads_msgfree(ads, res);
1280 return ADS_ERROR(LDAP_NO_MEMORY);
1282 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1283 if (!ADS_ERR_OK(ret)) {
1284 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1285 ads_msgfree(ads, res);
1286 talloc_destroy(ctx);
1287 return ret;
1289 dn_string = ads_get_dn(ads, res);
1290 if (!dn_string) {
1291 talloc_destroy(ctx);
1292 ads_msgfree(ads, res);
1293 return ADS_ERROR(LDAP_NO_MEMORY);
1295 ret = ads_gen_mod(ads, dn_string, mods);
1296 ads_memfree(ads,dn_string);
1297 if (!ADS_ERR_OK(ret)) {
1298 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1299 machine_name));
1300 ads_msgfree(ads, res);
1301 talloc_destroy(ctx);
1302 return ret;
1305 ads_msgfree(ads, res);
1306 talloc_destroy(ctx);
1307 return ret;
1311 * This adds a service principal name to an existing computer account
1312 * (found by hostname) in AD.
1313 * @param ads An initialized ADS_STRUCT
1314 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1315 * @param spn A string of the service principal to add, i.e. 'host'
1316 * @return 0 upon sucess, or non-zero if a failure occurs
1319 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name, const char *spn)
1321 ADS_STATUS ret;
1322 TALLOC_CTX *ctx;
1323 LDAPMessage *res = NULL;
1324 char *host_spn, *psp1, *psp2, *psp3;
1325 ADS_MODLIST mods;
1326 fstring my_fqdn;
1327 char *dn_string = NULL;
1328 const char *servicePrincipalName[4] = {NULL, NULL, NULL, NULL};
1330 ret = ads_find_machine_acct(ads, (void **)(void *)&res, machine_name);
1331 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1332 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1333 machine_name));
1334 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1335 spn, machine_name, ads->config.realm));
1336 ads_msgfree(ads, res);
1337 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1340 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1341 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1342 ads_msgfree(ads, res);
1343 return ADS_ERROR(LDAP_NO_MEMORY);
1346 name_to_fqdn(my_fqdn, machine_name);
1347 strlower_m(my_fqdn);
1349 if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", my_fqdn))) {
1350 talloc_destroy(ctx);
1351 ads_msgfree(ads, res);
1352 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1355 /* Add the extra principal */
1356 psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name);
1357 strupper_m(psp1);
1358 strlower_m(&psp1[strlen(spn)]);
1359 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp1, machine_name));
1360 servicePrincipalName[0] = psp1;
1361 psp2 = talloc_asprintf(ctx, "%s/%s.%s", spn, machine_name, ads->config.realm);
1362 strupper_m(psp2);
1363 strlower_m(&psp2[strlen(spn)]);
1364 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp2, machine_name));
1365 servicePrincipalName[1] = psp2;
1367 /* Add another principal in case the realm != the DNS domain, so that
1368 * the KDC doesn't send "server principal unknown" errors to clients
1369 * which use the DNS name in determining service principal names. */
1370 psp3 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn);
1371 strupper_m(psp3);
1372 strlower_m(&psp3[strlen(spn)]);
1373 if (strcmp(psp2, psp3) != 0) {
1374 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp3, machine_name));
1375 servicePrincipalName[2] = psp3;
1378 if (!(mods = ads_init_mods(ctx))) {
1379 talloc_destroy(ctx);
1380 ads_msgfree(ads, res);
1381 return ADS_ERROR(LDAP_NO_MEMORY);
1383 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1384 if (!ADS_ERR_OK(ret)) {
1385 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1386 talloc_destroy(ctx);
1387 ads_msgfree(ads, res);
1388 return ret;
1390 dn_string = ads_get_dn(ads, res);
1391 if (!dn_string) {
1392 talloc_destroy(ctx);
1393 ads_msgfree(ads, res);
1394 return ADS_ERROR(LDAP_NO_MEMORY);
1396 ret = ads_gen_mod(ads, dn_string, mods);
1397 ads_memfree(ads,dn_string);
1398 if (!ADS_ERR_OK(ret)) {
1399 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1400 talloc_destroy(ctx);
1401 ads_msgfree(ads, res);
1402 return ret;
1405 talloc_destroy(ctx);
1406 ads_msgfree(ads, res);
1407 return ret;
1411 * adds a machine account to the ADS server
1412 * @param ads An intialized ADS_STRUCT
1413 * @param machine_name - the NetBIOS machine name of this account.
1414 * @param account_type A number indicating the type of account to create
1415 * @param org_unit The LDAP path in which to place this account
1416 * @return 0 upon success, or non-zero otherwise
1419 static ADS_STATUS ads_add_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1420 uint32 account_type,
1421 const char *org_unit)
1423 ADS_STATUS ret, status;
1424 char *host_spn, *host_upn, *new_dn, *samAccountName, *controlstr;
1425 TALLOC_CTX *ctx;
1426 ADS_MODLIST mods;
1427 const char *objectClass[] = {"top", "person", "organizationalPerson",
1428 "user", "computer", NULL};
1429 const char *servicePrincipalName[7] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
1430 char *psp, *psp2, *psp3, *psp4;
1431 unsigned acct_control;
1432 unsigned exists=0;
1433 fstring my_fqdn;
1434 LDAPMessage *res = NULL;
1435 int i, next_spn;
1437 if (!(ctx = talloc_init("ads_add_machine_acct")))
1438 return ADS_ERROR(LDAP_NO_MEMORY);
1440 ret = ADS_ERROR(LDAP_NO_MEMORY);
1442 name_to_fqdn(my_fqdn, machine_name);
1444 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine_name);
1445 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1446 char *dn_string = ads_get_dn(ads, res);
1447 if (!dn_string) {
1448 DEBUG(1, ("ads_add_machine_acct: ads_get_dn returned NULL (malloc failure?)\n"));
1449 goto done;
1451 new_dn = talloc_strdup(ctx, dn_string);
1452 ads_memfree(ads,dn_string);
1453 DEBUG(0, ("ads_add_machine_acct: Host account for %s already exists - modifying old account\n",
1454 machine_name));
1455 exists=1;
1456 } else {
1457 char *ou_str = ads_ou_string(ads,org_unit);
1458 if (!ou_str) {
1459 DEBUG(1, ("ads_add_machine_acct: ads_ou_string returned NULL (malloc failure?)\n"));
1460 goto done;
1462 new_dn = talloc_asprintf(ctx, "cn=%s,%s,%s", machine_name, ou_str,
1463 ads->config.bind_path);
1465 SAFE_FREE(ou_str);
1468 if (!new_dn) {
1469 goto done;
1472 if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", machine_name)))
1473 goto done;
1474 if (!(host_upn = talloc_asprintf(ctx, "%s@%s", host_spn, ads->config.realm)))
1475 goto done;
1476 servicePrincipalName[0] = talloc_asprintf(ctx, "HOST/%s", machine_name);
1477 psp = talloc_asprintf(ctx, "HOST/%s.%s",
1478 machine_name,
1479 ads->config.realm);
1480 strlower_m(&psp[5]);
1481 servicePrincipalName[1] = psp;
1482 servicePrincipalName[2] = talloc_asprintf(ctx, "CIFS/%s", machine_name);
1483 psp2 = talloc_asprintf(ctx, "CIFS/%s.%s",
1484 machine_name,
1485 ads->config.realm);
1486 strlower_m(&psp2[5]);
1487 servicePrincipalName[3] = psp2;
1489 /* Ensure servicePrincipalName[4] and [5] are unique. */
1490 strlower_m(my_fqdn);
1491 psp3 = talloc_asprintf(ctx, "CIFS/%s", my_fqdn);
1492 strlower_m(&psp3[5]);
1494 next_spn = 4;
1495 for (i = 0; i < next_spn; i++) {
1496 if (strequal(servicePrincipalName[i], psp3))
1497 break;
1499 if (i == next_spn) {
1500 servicePrincipalName[next_spn++] = psp3;
1503 psp4 = talloc_asprintf(ctx, "HOST/%s", my_fqdn);
1504 strlower_m(&psp4[5]);
1505 for (i = 0; i < next_spn; i++) {
1506 if (strequal(servicePrincipalName[i], psp4))
1507 break;
1509 if (i == next_spn) {
1510 servicePrincipalName[next_spn++] = psp4;
1513 if (!(samAccountName = talloc_asprintf(ctx, "%s$", machine_name))) {
1514 goto done;
1517 acct_control = account_type | UF_DONT_EXPIRE_PASSWD;
1518 #ifndef ENCTYPE_ARCFOUR_HMAC
1519 acct_control |= UF_USE_DES_KEY_ONLY;
1520 #endif
1522 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
1523 goto done;
1526 if (!(mods = ads_init_mods(ctx))) {
1527 goto done;
1530 if (!exists) {
1531 ads_mod_str(ctx, &mods, "cn", machine_name);
1532 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1533 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1535 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1536 ads_mod_str(ctx, &mods, "dNSHostName", my_fqdn);
1537 ads_mod_str(ctx, &mods, "userPrincipalName", host_upn);
1538 ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1539 ads_mod_str(ctx, &mods, "operatingSystem", "Samba");
1540 ads_mod_str(ctx, &mods, "operatingSystemVersion", SAMBA_VERSION_STRING);
1542 if (!exists) {
1543 ret = ads_gen_add(ads, new_dn, mods);
1544 } else {
1545 ret = ads_gen_mod(ads, new_dn, mods);
1548 if (!ADS_ERR_OK(ret)) {
1549 goto done;
1552 /* Do not fail if we can't set security descriptor
1553 * it shouldn't be mandatory and probably we just
1554 * don't have enough rights to do it.
1556 if (!exists) {
1557 status = ads_set_machine_sd(ads, machine_name, new_dn);
1559 if (!ADS_ERR_OK(status)) {
1560 DEBUG(0, ("Warning: ads_set_machine_sd: %s\n",
1561 ads_errstr(status)));
1564 done:
1565 ads_msgfree(ads, res);
1566 talloc_destroy(ctx);
1567 return ret;
1571 dump a binary result from ldap
1573 static void dump_binary(const char *field, struct berval **values)
1575 int i, j;
1576 for (i=0; values[i]; i++) {
1577 printf("%s: ", field);
1578 for (j=0; j<values[i]->bv_len; j++) {
1579 printf("%02X", (unsigned char)values[i]->bv_val[j]);
1581 printf("\n");
1585 static void dump_guid(const char *field, struct berval **values)
1587 int i;
1588 UUID_FLAT guid;
1589 for (i=0; values[i]; i++) {
1590 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1591 printf("%s: %s\n", field,
1592 smb_uuid_string_static(smb_uuid_unpack_static(guid)));
1597 dump a sid result from ldap
1599 static void dump_sid(const char *field, struct berval **values)
1601 int i;
1602 for (i=0; values[i]; i++) {
1603 DOM_SID sid;
1604 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1605 printf("%s: %s\n", field, sid_string_static(&sid));
1610 dump ntSecurityDescriptor
1612 static void dump_sd(const char *filed, struct berval **values)
1614 prs_struct ps;
1616 SEC_DESC *psd = 0;
1617 TALLOC_CTX *ctx = 0;
1619 if (!(ctx = talloc_init("sec_io_desc")))
1620 return;
1622 /* prepare data */
1623 prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
1624 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
1625 prs_set_offset(&ps,0);
1627 /* parse secdesc */
1628 if (!sec_io_desc("sd", &psd, &ps, 1)) {
1629 prs_mem_free(&ps);
1630 talloc_destroy(ctx);
1631 return;
1633 if (psd) ads_disp_sd(psd);
1635 prs_mem_free(&ps);
1636 talloc_destroy(ctx);
1640 dump a string result from ldap
1642 static void dump_string(const char *field, char **values)
1644 int i;
1645 for (i=0; values[i]; i++) {
1646 printf("%s: %s\n", field, values[i]);
1651 dump a field from LDAP on stdout
1652 used for debugging
1655 static BOOL ads_dump_field(char *field, void **values, void *data_area)
1657 const struct {
1658 const char *name;
1659 BOOL string;
1660 void (*handler)(const char *, struct berval **);
1661 } handlers[] = {
1662 {"objectGUID", False, dump_guid},
1663 {"netbootGUID", False, dump_guid},
1664 {"nTSecurityDescriptor", False, dump_sd},
1665 {"dnsRecord", False, dump_binary},
1666 {"objectSid", False, dump_sid},
1667 {"tokenGroups", False, dump_sid},
1668 {NULL, True, NULL}
1670 int i;
1672 if (!field) { /* must be end of an entry */
1673 printf("\n");
1674 return False;
1677 for (i=0; handlers[i].name; i++) {
1678 if (StrCaseCmp(handlers[i].name, field) == 0) {
1679 if (!values) /* first time, indicate string or not */
1680 return handlers[i].string;
1681 handlers[i].handler(field, (struct berval **) values);
1682 break;
1685 if (!handlers[i].name) {
1686 if (!values) /* first time, indicate string conversion */
1687 return True;
1688 dump_string(field, (char **)values);
1690 return False;
1694 * Dump a result from LDAP on stdout
1695 * used for debugging
1696 * @param ads connection to ads server
1697 * @param res Results to dump
1700 void ads_dump(ADS_STRUCT *ads, void *res)
1702 ads_process_results(ads, res, ads_dump_field, NULL);
1706 * Walk through results, calling a function for each entry found.
1707 * The function receives a field name, a berval * array of values,
1708 * and a data area passed through from the start. The function is
1709 * called once with null for field and values at the end of each
1710 * entry.
1711 * @param ads connection to ads server
1712 * @param res Results to process
1713 * @param fn Function for processing each result
1714 * @param data_area user-defined area to pass to function
1716 void ads_process_results(ADS_STRUCT *ads, void *res,
1717 BOOL(*fn)(char *, void **, void *),
1718 void *data_area)
1720 void *msg;
1721 TALLOC_CTX *ctx;
1723 if (!(ctx = talloc_init("ads_process_results")))
1724 return;
1726 for (msg = ads_first_entry(ads, res); msg;
1727 msg = ads_next_entry(ads, msg)) {
1728 char *utf8_field;
1729 BerElement *b;
1731 for (utf8_field=ldap_first_attribute(ads->ld,
1732 (LDAPMessage *)msg,&b);
1733 utf8_field;
1734 utf8_field=ldap_next_attribute(ads->ld,
1735 (LDAPMessage *)msg,b)) {
1736 struct berval **ber_vals;
1737 char **str_vals, **utf8_vals;
1738 char *field;
1739 BOOL string;
1741 pull_utf8_talloc(ctx, &field, utf8_field);
1742 string = fn(field, NULL, data_area);
1744 if (string) {
1745 utf8_vals = ldap_get_values(ads->ld,
1746 (LDAPMessage *)msg, field);
1747 str_vals = ads_pull_strvals(ctx,
1748 (const char **) utf8_vals);
1749 fn(field, (void **) str_vals, data_area);
1750 ldap_value_free(utf8_vals);
1751 } else {
1752 ber_vals = ldap_get_values_len(ads->ld,
1753 (LDAPMessage *)msg, field);
1754 fn(field, (void **) ber_vals, data_area);
1756 ldap_value_free_len(ber_vals);
1758 ldap_memfree(utf8_field);
1760 ber_free(b, 0);
1761 talloc_free_children(ctx);
1762 fn(NULL, NULL, data_area); /* completed an entry */
1765 talloc_destroy(ctx);
1769 * count how many replies are in a LDAPMessage
1770 * @param ads connection to ads server
1771 * @param res Results to count
1772 * @return number of replies
1774 int ads_count_replies(ADS_STRUCT *ads, void *res)
1776 return ldap_count_entries(ads->ld, (LDAPMessage *)res);
1780 * Join a machine to a realm
1781 * Creates the machine account and sets the machine password
1782 * @param ads connection to ads server
1783 * @param machine name of host to add
1784 * @param org_unit Organizational unit to place machine in
1785 * @return status of join
1787 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
1788 uint32 account_type, const char *org_unit)
1790 ADS_STATUS status;
1791 LDAPMessage *res = NULL;
1792 char *machine;
1794 /* machine name must be lowercase */
1795 machine = SMB_STRDUP(machine_name);
1796 strlower_m(machine);
1799 status = ads_find_machine_acct(ads, (void **)&res, machine);
1800 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1801 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
1802 status = ads_leave_realm(ads, machine);
1803 if (!ADS_ERR_OK(status)) {
1804 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
1805 machine, ads->config.realm));
1806 return status;
1811 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
1812 if (!ADS_ERR_OK(status)) {
1813 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
1814 SAFE_FREE(machine);
1815 return status;
1818 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
1819 if (!ADS_ERR_OK(status)) {
1820 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
1821 SAFE_FREE(machine);
1822 return status;
1825 SAFE_FREE(machine);
1826 ads_msgfree(ads, res);
1828 return status;
1832 * Delete a machine from the realm
1833 * @param ads connection to ads server
1834 * @param hostname Machine to remove
1835 * @return status of delete
1837 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
1839 ADS_STATUS status;
1840 void *res, *msg;
1841 char *hostnameDN, *host;
1842 int rc;
1843 LDAPControl ldap_control;
1844 LDAPControl * pldap_control[2] = {NULL, NULL};
1846 pldap_control[0] = &ldap_control;
1847 memset(&ldap_control, 0, sizeof(LDAPControl));
1848 ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
1850 /* hostname must be lowercase */
1851 host = SMB_STRDUP(hostname);
1852 strlower_m(host);
1854 status = ads_find_machine_acct(ads, &res, host);
1855 if (!ADS_ERR_OK(status)) {
1856 DEBUG(0, ("Host account for %s does not exist.\n", host));
1857 return status;
1860 msg = ads_first_entry(ads, res);
1861 if (!msg) {
1862 return ADS_ERROR_SYSTEM(ENOENT);
1865 hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
1868 rc = ldap_delete_ext_s(ads->ld, hostnameDN, pldap_control, NULL);
1869 if (rc) {
1870 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
1871 }else {
1872 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
1875 ads_memfree(ads, hostnameDN);
1876 if (rc != LDAP_SUCCESS) {
1877 return ADS_ERROR(rc);
1880 status = ads_find_machine_acct(ads, &res, host);
1881 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1882 DEBUG(0, ("Failed to remove host account.\n"));
1883 return status;
1886 free(host);
1888 return status;
1892 * add machine account to existing security descriptor
1893 * @param ads connection to ads server
1894 * @param hostname machine to add
1895 * @param dn DN of security descriptor
1896 * @return status
1898 ADS_STATUS ads_set_machine_sd(ADS_STRUCT *ads, const char *hostname, char *dn)
1900 const char *attrs[] = {"nTSecurityDescriptor", "objectSid", 0};
1901 char *expr = 0;
1902 size_t sd_size = 0;
1903 struct berval bval = {0, NULL};
1904 prs_struct ps_wire;
1905 char *escaped_hostname = escape_ldap_string_alloc(hostname);
1907 LDAPMessage *res = 0;
1908 LDAPMessage *msg = 0;
1909 ADS_MODLIST mods = 0;
1911 NTSTATUS status;
1912 ADS_STATUS ret;
1913 DOM_SID sid;
1914 SEC_DESC *psd = NULL;
1915 TALLOC_CTX *ctx = NULL;
1917 /* Avoid segmentation fault in prs_mem_free if
1918 * we have to bail out before prs_init */
1919 ps_wire.is_dynamic = False;
1921 if (!ads) {
1922 SAFE_FREE(escaped_hostname);
1923 return ADS_ERROR(LDAP_SERVER_DOWN);
1926 ret = ADS_ERROR(LDAP_SUCCESS);
1928 if (!escaped_hostname) {
1929 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1932 if (asprintf(&expr, "(samAccountName=%s$)", escaped_hostname) == -1) {
1933 DEBUG(1, ("ads_set_machine_sd: asprintf failed!\n"));
1934 SAFE_FREE(escaped_hostname);
1935 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1938 SAFE_FREE(escaped_hostname);
1940 ret = ads_search(ads, (void *) &res, expr, attrs);
1942 SAFE_FREE(expr);
1944 if (!ADS_ERR_OK(ret)) return ret;
1946 if ( !(msg = ads_first_entry(ads, res) )) {
1947 ret = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1948 goto ads_set_sd_error;
1951 if (!ads_pull_sid(ads, msg, attrs[1], &sid)) {
1952 ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
1953 goto ads_set_sd_error;
1956 if (!(ctx = talloc_init("sec_io_desc"))) {
1957 ret = ADS_ERROR(LDAP_NO_MEMORY);
1958 goto ads_set_sd_error;
1961 if (!ads_pull_sd(ads, ctx, msg, attrs[0], &psd)) {
1962 ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
1963 goto ads_set_sd_error;
1966 status = sec_desc_add_sid(ctx, &psd, &sid, SEC_RIGHTS_FULL_CTRL, &sd_size);
1968 if (!NT_STATUS_IS_OK(status)) {
1969 ret = ADS_ERROR_NT(status);
1970 goto ads_set_sd_error;
1973 if (!prs_init(&ps_wire, sd_size, ctx, MARSHALL)) {
1974 ret = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1977 if (!sec_io_desc("sd_wire", &psd, &ps_wire, 1)) {
1978 ret = ADS_ERROR(LDAP_NO_MEMORY);
1979 goto ads_set_sd_error;
1982 #if 0
1983 file_save("/tmp/sec_desc.new", ps_wire.data_p, sd_size);
1984 #endif
1985 if (!(mods = ads_init_mods(ctx))) return ADS_ERROR(LDAP_NO_MEMORY);
1987 bval.bv_len = prs_offset(&ps_wire);
1988 bval.bv_val = TALLOC(ctx, bval.bv_len);
1989 if (!bval.bv_val) {
1990 ret = ADS_ERROR(LDAP_NO_MEMORY);
1991 goto ads_set_sd_error;
1994 prs_set_offset(&ps_wire, 0);
1996 if (!prs_copy_data_out(bval.bv_val, &ps_wire, bval.bv_len)) {
1997 ret = ADS_ERROR(LDAP_NO_MEMORY);
1998 goto ads_set_sd_error;
2001 ret = ads_mod_ber(ctx, &mods, attrs[0], &bval);
2002 if (ADS_ERR_OK(ret)) {
2003 ret = ads_gen_mod(ads, dn, mods);
2006 ads_set_sd_error:
2007 ads_msgfree(ads, res);
2008 prs_mem_free(&ps_wire);
2009 talloc_destroy(ctx);
2010 return ret;
2014 * pull the first entry from a ADS result
2015 * @param ads connection to ads server
2016 * @param res Results of search
2017 * @return first entry from result
2019 void *ads_first_entry(ADS_STRUCT *ads, void *res)
2021 return (void *)ldap_first_entry(ads->ld, (LDAPMessage *)res);
2025 * pull the next entry from a ADS result
2026 * @param ads connection to ads server
2027 * @param res Results of search
2028 * @return next entry from result
2030 void *ads_next_entry(ADS_STRUCT *ads, void *res)
2032 return (void *)ldap_next_entry(ads->ld, (LDAPMessage *)res);
2036 * pull a single string from a ADS result
2037 * @param ads connection to ads server
2038 * @param mem_ctx TALLOC_CTX to use for allocating result string
2039 * @param msg Results of search
2040 * @param field Attribute to retrieve
2041 * @return Result string in talloc context
2043 char *ads_pull_string(ADS_STRUCT *ads,
2044 TALLOC_CTX *mem_ctx, void *msg, const char *field)
2046 char **values;
2047 char *ret = NULL;
2048 char *ux_string;
2049 size_t rc;
2051 values = ldap_get_values(ads->ld, msg, field);
2052 if (!values)
2053 return NULL;
2055 if (values[0]) {
2056 rc = pull_utf8_talloc(mem_ctx, &ux_string,
2057 values[0]);
2058 if (rc != (size_t)-1)
2059 ret = ux_string;
2062 ldap_value_free(values);
2063 return ret;
2067 * pull an array of strings from a ADS result
2068 * @param ads connection to ads server
2069 * @param mem_ctx TALLOC_CTX to use for allocating result string
2070 * @param msg Results of search
2071 * @param field Attribute to retrieve
2072 * @return Result strings in talloc context
2074 char **ads_pull_strings(ADS_STRUCT *ads,
2075 TALLOC_CTX *mem_ctx, void *msg, const char *field,
2076 size_t *num_values)
2078 char **values;
2079 char **ret = NULL;
2080 int i;
2082 values = ldap_get_values(ads->ld, msg, field);
2083 if (!values)
2084 return NULL;
2086 *num_values = ldap_count_values(values);
2088 ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
2089 if (!ret) {
2090 ldap_value_free(values);
2091 return NULL;
2094 for (i=0;i<*num_values;i++) {
2095 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
2096 ldap_value_free(values);
2097 return NULL;
2100 ret[i] = NULL;
2102 ldap_value_free(values);
2103 return ret;
2107 * pull an array of strings from a ADS result
2108 * (handle large multivalue attributes with range retrieval)
2109 * @param ads connection to ads server
2110 * @param mem_ctx TALLOC_CTX to use for allocating result string
2111 * @param msg Results of search
2112 * @param field Attribute to retrieve
2113 * @param current_strings strings returned by a previous call to this function
2114 * @param next_attribute The next query should ask for this attribute
2115 * @param num_values How many values did we get this time?
2116 * @param more_values Are there more values to get?
2117 * @return Result strings in talloc context
2119 char **ads_pull_strings_range(ADS_STRUCT *ads,
2120 TALLOC_CTX *mem_ctx,
2121 void *msg, const char *field,
2122 char **current_strings,
2123 const char **next_attribute,
2124 size_t *num_strings,
2125 BOOL *more_strings)
2127 char *attr;
2128 char *expected_range_attrib, *range_attr;
2129 BerElement *ptr = NULL;
2130 char **strings;
2131 char **new_strings;
2132 size_t num_new_strings;
2133 unsigned long int range_start;
2134 unsigned long int range_end;
2136 /* we might have been given the whole lot anyway */
2137 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2138 *more_strings = False;
2139 return strings;
2142 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2144 /* look for Range result */
2145 for (attr = ldap_first_attribute(ads->ld, (LDAPMessage *)msg, &ptr);
2146 attr;
2147 attr = ldap_next_attribute(ads->ld, (LDAPMessage *)msg, ptr)) {
2148 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2149 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2150 range_attr = attr;
2151 break;
2153 ldap_memfree(attr);
2155 if (!attr) {
2156 ber_free(ptr, 0);
2157 /* nothing here - this field is just empty */
2158 *more_strings = False;
2159 return NULL;
2162 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2163 &range_start, &range_end) == 2) {
2164 *more_strings = True;
2165 } else {
2166 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2167 &range_start) == 1) {
2168 *more_strings = False;
2169 } else {
2170 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2171 range_attr));
2172 ldap_memfree(range_attr);
2173 *more_strings = False;
2174 return NULL;
2178 if ((*num_strings) != range_start) {
2179 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2180 " - aborting range retreival\n",
2181 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2182 ldap_memfree(range_attr);
2183 *more_strings = False;
2184 return NULL;
2187 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2189 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2190 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2191 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2192 range_attr, (unsigned long int)range_end - range_start + 1,
2193 (unsigned long int)num_new_strings));
2194 ldap_memfree(range_attr);
2195 *more_strings = False;
2196 return NULL;
2199 strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2200 *num_strings + num_new_strings);
2202 if (strings == NULL) {
2203 ldap_memfree(range_attr);
2204 *more_strings = False;
2205 return NULL;
2208 memcpy(&strings[*num_strings], new_strings,
2209 sizeof(*new_strings) * num_new_strings);
2211 (*num_strings) += num_new_strings;
2213 if (*more_strings) {
2214 *next_attribute = talloc_asprintf(mem_ctx,
2215 "%s;range=%d-*",
2216 field,
2217 (int)*num_strings);
2219 if (!*next_attribute) {
2220 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2221 ldap_memfree(range_attr);
2222 *more_strings = False;
2223 return NULL;
2227 ldap_memfree(range_attr);
2229 return strings;
2233 * pull a single uint32 from a ADS result
2234 * @param ads connection to ads server
2235 * @param msg Results of search
2236 * @param field Attribute to retrieve
2237 * @param v Pointer to int to store result
2238 * @return boolean inidicating success
2240 BOOL ads_pull_uint32(ADS_STRUCT *ads,
2241 void *msg, const char *field, uint32 *v)
2243 char **values;
2245 values = ldap_get_values(ads->ld, msg, field);
2246 if (!values)
2247 return False;
2248 if (!values[0]) {
2249 ldap_value_free(values);
2250 return False;
2253 *v = atoi(values[0]);
2254 ldap_value_free(values);
2255 return True;
2259 * pull a single objectGUID from an ADS result
2260 * @param ads connection to ADS server
2261 * @param msg results of search
2262 * @param guid 37-byte area to receive text guid
2263 * @return boolean indicating success
2265 BOOL ads_pull_guid(ADS_STRUCT *ads,
2266 void *msg, struct uuid *guid)
2268 char **values;
2269 UUID_FLAT flat_guid;
2271 values = ldap_get_values(ads->ld, msg, "objectGUID");
2272 if (!values)
2273 return False;
2275 if (values[0]) {
2276 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2277 smb_uuid_unpack(flat_guid, guid);
2278 ldap_value_free(values);
2279 return True;
2281 ldap_value_free(values);
2282 return False;
2288 * pull a single DOM_SID from a ADS result
2289 * @param ads connection to ads server
2290 * @param msg Results of search
2291 * @param field Attribute to retrieve
2292 * @param sid Pointer to sid to store result
2293 * @return boolean inidicating success
2295 BOOL ads_pull_sid(ADS_STRUCT *ads,
2296 void *msg, const char *field, DOM_SID *sid)
2298 struct berval **values;
2299 BOOL ret = False;
2301 values = ldap_get_values_len(ads->ld, msg, field);
2303 if (!values)
2304 return False;
2306 if (values[0])
2307 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2309 ldap_value_free_len(values);
2310 return ret;
2314 * pull an array of DOM_SIDs from a ADS result
2315 * @param ads connection to ads server
2316 * @param mem_ctx TALLOC_CTX for allocating sid array
2317 * @param msg Results of search
2318 * @param field Attribute to retrieve
2319 * @param sids pointer to sid array to allocate
2320 * @return the count of SIDs pulled
2322 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2323 void *msg, const char *field, DOM_SID **sids)
2325 struct berval **values;
2326 BOOL ret;
2327 int count, i;
2329 values = ldap_get_values_len(ads->ld, msg, field);
2331 if (!values)
2332 return 0;
2334 for (i=0; values[i]; i++)
2335 /* nop */ ;
2337 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2338 if (!(*sids)) {
2339 ldap_value_free_len(values);
2340 return 0;
2343 count = 0;
2344 for (i=0; values[i]; i++) {
2345 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2346 if (ret) {
2347 fstring sid;
2348 DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid, &(*sids)[count])));
2349 count++;
2353 ldap_value_free_len(values);
2354 return count;
2358 * pull a SEC_DESC from a ADS result
2359 * @param ads connection to ads server
2360 * @param mem_ctx TALLOC_CTX for allocating sid array
2361 * @param msg Results of search
2362 * @param field Attribute to retrieve
2363 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2364 * @return boolean inidicating success
2366 BOOL ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2367 void *msg, const char *field, SEC_DESC **sd)
2369 struct berval **values;
2370 prs_struct ps;
2371 BOOL ret = False;
2373 values = ldap_get_values_len(ads->ld, msg, field);
2375 if (!values) return False;
2377 if (values[0]) {
2378 prs_init(&ps, values[0]->bv_len, mem_ctx, UNMARSHALL);
2379 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
2380 prs_set_offset(&ps,0);
2382 ret = sec_io_desc("sd", sd, &ps, 1);
2385 ldap_value_free_len(values);
2386 return ret;
2390 * in order to support usernames longer than 21 characters we need to
2391 * use both the sAMAccountName and the userPrincipalName attributes
2392 * It seems that not all users have the userPrincipalName attribute set
2394 * @param ads connection to ads server
2395 * @param mem_ctx TALLOC_CTX for allocating sid array
2396 * @param msg Results of search
2397 * @return the username
2399 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, void *msg)
2401 #if 0 /* JERRY */
2402 char *ret, *p;
2404 /* lookup_name() only works on the sAMAccountName to
2405 returning the username portion of userPrincipalName
2406 breaks winbindd_getpwnam() */
2408 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2409 if (ret && (p = strchr_m(ret, '@'))) {
2410 *p = 0;
2411 return ret;
2413 #endif
2414 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2419 * find the update serial number - this is the core of the ldap cache
2420 * @param ads connection to ads server
2421 * @param ads connection to ADS server
2422 * @param usn Pointer to retrieved update serial number
2423 * @return status of search
2425 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2427 const char *attrs[] = {"highestCommittedUSN", NULL};
2428 ADS_STATUS status;
2429 void *res;
2431 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2432 if (!ADS_ERR_OK(status))
2433 return status;
2435 if (ads_count_replies(ads, res) != 1) {
2436 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2439 ads_pull_uint32(ads, res, "highestCommittedUSN", usn);
2440 ads_msgfree(ads, res);
2441 return ADS_SUCCESS;
2444 /* parse a ADS timestring - typical string is
2445 '20020917091222.0Z0' which means 09:12.22 17th September
2446 2002, timezone 0 */
2447 static time_t ads_parse_time(const char *str)
2449 struct tm tm;
2451 ZERO_STRUCT(tm);
2453 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2454 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2455 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2456 return 0;
2458 tm.tm_year -= 1900;
2459 tm.tm_mon -= 1;
2461 return timegm(&tm);
2465 const char *ads_get_attrname_by_oid(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char * OID)
2467 ADS_STATUS rc;
2468 int count = 0;
2469 void *res = NULL;
2470 char *expr = NULL;
2471 const char *attrs[] = { "lDAPDisplayName", NULL };
2473 if (ads == NULL || mem_ctx == NULL || OID == NULL) {
2474 goto failed;
2477 expr = talloc_asprintf(mem_ctx, "(attributeId=%s)", OID);
2478 if (expr == NULL) {
2479 goto failed;
2482 rc = ads_do_search_retry(ads, ads->config.schema_path,
2483 LDAP_SCOPE_SUBTREE, expr, attrs, &res);
2484 if (!ADS_ERR_OK(rc)) {
2485 goto failed;
2488 count = ads_count_replies(ads, res);
2489 if (count == 0 || !res) {
2490 goto failed;
2493 return ads_pull_string(ads, mem_ctx, res, "lDAPDisplayName");
2495 failed:
2496 DEBUG(0,("ads_get_attrname_by_oid: failed to retrieve name for oid: %s\n",
2497 OID));
2499 return NULL;
2503 * Find the servers name and realm - this can be done before authentication
2504 * The ldapServiceName field on w2k looks like this:
2505 * vnet3.home.samba.org:win2000-vnet3$@VNET3.HOME.SAMBA.ORG
2506 * @param ads connection to ads server
2507 * @return status of search
2509 ADS_STATUS ads_server_info(ADS_STRUCT *ads)
2511 const char *attrs[] = {"ldapServiceName",
2512 "currentTime",
2513 "schemaNamingContext", NULL};
2514 ADS_STATUS status;
2515 void *res;
2516 char *value;
2517 char *p;
2518 char *timestr;
2519 char *schema_path;
2520 TALLOC_CTX *ctx;
2522 if (!(ctx = talloc_init("ads_server_info"))) {
2523 return ADS_ERROR(LDAP_NO_MEMORY);
2526 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2527 if (!ADS_ERR_OK(status)) {
2528 talloc_destroy(ctx);
2529 return status;
2532 value = ads_pull_string(ads, ctx, res, "ldapServiceName");
2533 if (!value) {
2534 ads_msgfree(ads, res);
2535 talloc_destroy(ctx);
2536 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2539 timestr = ads_pull_string(ads, ctx, res, "currentTime");
2540 if (!timestr) {
2541 ads_msgfree(ads, res);
2542 talloc_destroy(ctx);
2543 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2546 schema_path = ads_pull_string(ads, ctx, res, "schemaNamingContext");
2547 if (!schema_path) {
2548 ads_msgfree(ads, res);
2549 talloc_destroy(ctx);
2550 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2553 SAFE_FREE(ads->config.schema_path);
2554 ads->config.schema_path = SMB_STRDUP(schema_path);
2556 ads_msgfree(ads, res);
2558 p = strchr(value, ':');
2559 if (!p) {
2560 talloc_destroy(ctx);
2561 DEBUG(1, ("ads_server_info: returned ldap server name did not contain a ':' "
2562 "so was deemed invalid\n"));
2563 return ADS_ERROR(LDAP_DECODING_ERROR);
2566 SAFE_FREE(ads->config.ldap_server_name);
2568 ads->config.ldap_server_name = SMB_STRDUP(p+1);
2569 p = strchr(ads->config.ldap_server_name, '$');
2570 if (!p || p[1] != '@') {
2571 talloc_destroy(ctx);
2572 DEBUG(1, ("ads_server_info: returned ldap server name (%s) does not contain '$@'"
2573 " so was deemed invalid\n", ads->config.ldap_server_name));
2574 SAFE_FREE(ads->config.ldap_server_name);
2575 return ADS_ERROR(LDAP_DECODING_ERROR);
2578 *p = 0;
2580 SAFE_FREE(ads->config.realm);
2581 SAFE_FREE(ads->config.bind_path);
2583 ads->config.realm = SMB_STRDUP(p+2);
2584 ads->config.bind_path = ads_build_dn(ads->config.realm);
2586 DEBUG(3,("got ldap server name %s@%s, using bind path: %s\n",
2587 ads->config.ldap_server_name, ads->config.realm,
2588 ads->config.bind_path));
2590 ads->config.current_time = ads_parse_time(timestr);
2592 if (ads->config.current_time != 0) {
2593 ads->auth.time_offset = ads->config.current_time - time(NULL);
2594 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2597 talloc_destroy(ctx);
2599 return ADS_SUCCESS;
2603 * Check for "Services for Unix"-Schema and load some attributes into the ADS_STRUCT
2604 * @param ads connection to ads server
2605 * @return BOOL status of search (False if one or more attributes couldn't be
2606 * found in Active Directory)
2607 **/
2608 BOOL ads_check_sfu_mapping(ADS_STRUCT *ads)
2610 BOOL ret = False;
2611 TALLOC_CTX *ctx = NULL;
2612 const char *gidnumber, *uidnumber, *homedir, *shell, *gecos;
2614 ctx = talloc_init("ads_check_sfu_mapping");
2615 if (ctx == NULL)
2616 goto done;
2618 gidnumber = ads_get_attrname_by_oid(ads, ctx, ADS_ATTR_SFU_GIDNUMBER_OID);
2619 if (gidnumber == NULL)
2620 goto done;
2621 ads->schema.sfu_gidnumber_attr = SMB_STRDUP(gidnumber);
2623 uidnumber = ads_get_attrname_by_oid(ads, ctx, ADS_ATTR_SFU_UIDNUMBER_OID);
2624 if (uidnumber == NULL)
2625 goto done;
2626 ads->schema.sfu_uidnumber_attr = SMB_STRDUP(uidnumber);
2628 homedir = ads_get_attrname_by_oid(ads, ctx, ADS_ATTR_SFU_HOMEDIR_OID);
2629 if (homedir == NULL)
2630 goto done;
2631 ads->schema.sfu_homedir_attr = SMB_STRDUP(homedir);
2633 shell = ads_get_attrname_by_oid(ads, ctx, ADS_ATTR_SFU_SHELL_OID);
2634 if (shell == NULL)
2635 goto done;
2636 ads->schema.sfu_shell_attr = SMB_STRDUP(shell);
2638 gecos = ads_get_attrname_by_oid(ads, ctx, ADS_ATTR_SFU_GECOS_OID);
2639 if (gecos == NULL)
2640 goto done;
2641 ads->schema.sfu_gecos_attr = SMB_STRDUP(gecos);
2643 ret = True;
2644 done:
2645 if (ctx)
2646 talloc_destroy(ctx);
2648 return ret;
2652 * find the domain sid for our domain
2653 * @param ads connection to ads server
2654 * @param sid Pointer to domain sid
2655 * @return status of search
2657 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2659 const char *attrs[] = {"objectSid", NULL};
2660 void *res;
2661 ADS_STATUS rc;
2663 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2664 attrs, &res);
2665 if (!ADS_ERR_OK(rc)) return rc;
2666 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2667 ads_msgfree(ads, res);
2668 return ADS_ERROR_SYSTEM(ENOENT);
2670 ads_msgfree(ads, res);
2672 return ADS_SUCCESS;
2675 /* this is rather complex - we need to find the allternate (netbios) name
2676 for the domain, but there isn't a simple query to do this. Instead
2677 we look for the principle names on the DCs account and find one that has
2678 the right form, then extract the netbios name of the domain from that
2680 NOTE! better method is this:
2682 bin/net -Uadministrator%XXXXX ads search '(&(objectclass=crossref)(dnsroot=VNET3.HOME.SAMBA.ORG))' nETBIOSName
2684 but you need to force the bind path to match the configurationNamingContext from the rootDSE
2687 ADS_STATUS ads_workgroup_name(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **workgroup)
2689 char *expr;
2690 ADS_STATUS rc;
2691 char **principles;
2692 char *prefix;
2693 int prefix_length;
2694 int i;
2695 void *res;
2696 const char *attrs[] = {"servicePrincipalName", NULL};
2697 size_t num_principals;
2699 (*workgroup) = NULL;
2701 asprintf(&expr, "(&(objectclass=computer)(dnshostname=%s.%s))",
2702 ads->config.ldap_server_name, ads->config.realm);
2703 if (expr == NULL) {
2704 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2707 rc = ads_search(ads, &res, expr, attrs);
2708 free(expr);
2710 if (!ADS_ERR_OK(rc)) {
2711 return rc;
2714 principles = ads_pull_strings(ads, mem_ctx, res,
2715 "servicePrincipalName", &num_principals);
2717 ads_msgfree(ads, res);
2719 if (!principles) {
2720 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2723 asprintf(&prefix, "HOST/%s.%s/",
2724 ads->config.ldap_server_name,
2725 ads->config.realm);
2727 prefix_length = strlen(prefix);
2729 for (i=0;principles[i]; i++) {
2730 if (strnequal(principles[i], prefix, prefix_length) &&
2731 !strequal(ads->config.realm, principles[i]+prefix_length) &&
2732 !strchr(principles[i]+prefix_length, '.')) {
2733 /* found an alternate (short) name for the domain. */
2734 DEBUG(3,("Found alternate name '%s' for realm '%s'\n",
2735 principles[i]+prefix_length,
2736 ads->config.realm));
2737 (*workgroup) = talloc_strdup(mem_ctx, principles[i]+prefix_length);
2738 break;
2741 free(prefix);
2743 if (!*workgroup) {
2744 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2747 return ADS_SUCCESS;
2751 * find our site name
2752 * @param ads connection to ads server
2753 * @param mem_ctx Pointer to talloc context
2754 * @param site_name Pointer to the sitename
2755 * @return status of search
2757 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2759 ADS_STATUS status;
2760 void *res;
2761 const char *dn, *service_name;
2762 const char *attrs[] = { "dsServiceName", NULL };
2764 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2765 if (!ADS_ERR_OK(status)) {
2766 return status;
2769 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2770 if (service_name == NULL) {
2771 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2774 /* go up three levels */
2775 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2776 if (dn == NULL) {
2777 return ADS_ERROR(LDAP_NO_MEMORY);
2780 *site_name = talloc_strdup(mem_ctx, dn);
2781 if (*site_name == NULL) {
2782 return ADS_ERROR(LDAP_NO_MEMORY);
2785 ads_msgfree(ads, res);
2787 return status;
2789 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2794 * find the site dn where a machine resides
2795 * @param ads connection to ads server
2796 * @param mem_ctx Pointer to talloc context
2797 * @param computer_name name of the machine
2798 * @param site_name Pointer to the sitename
2799 * @return status of search
2801 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
2803 ADS_STATUS status;
2804 void *res;
2805 const char *parent, *config_context, *filter;
2806 const char *attrs[] = { "configurationNamingContext", NULL };
2807 char *dn;
2809 /* shortcut a query */
2810 if (strequal(computer_name, ads->config.ldap_server_name)) {
2811 return ads_site_dn(ads, mem_ctx, site_dn);
2814 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2815 if (!ADS_ERR_OK(status)) {
2816 return status;
2819 config_context = ads_pull_string(ads, mem_ctx, res, "configurationNamingContext");
2820 if (config_context == NULL) {
2821 return ADS_ERROR(LDAP_NO_MEMORY);
2824 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
2825 if (filter == NULL) {
2826 return ADS_ERROR(LDAP_NO_MEMORY);
2829 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE, filter, NULL, &res);
2830 if (!ADS_ERR_OK(status)) {
2831 return status;
2834 if (ads_count_replies(ads, res) != 1) {
2835 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2838 dn = ads_get_dn(ads, res);
2839 if (dn == NULL) {
2840 return ADS_ERROR(LDAP_NO_MEMORY);
2843 /* go up three levels */
2844 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
2845 if (parent == NULL) {
2846 ads_memfree(ads, dn);
2847 return ADS_ERROR(LDAP_NO_MEMORY);
2850 *site_dn = talloc_strdup(mem_ctx, parent);
2851 if (*site_dn == NULL) {
2852 ads_memfree(ads, dn);
2853 ADS_ERROR(LDAP_NO_MEMORY);
2856 ads_memfree(ads, dn);
2857 ads_msgfree(ads, res);
2859 return status;
2863 * get the upn suffixes for a domain
2864 * @param ads connection to ads server
2865 * @param mem_ctx Pointer to talloc context
2866 * @param suffixes Pointer to an array of suffixes
2867 * @param site_name Pointer to the number of suffixes
2868 * @return status of search
2870 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char **suffixes, size_t *num_suffixes)
2872 ADS_STATUS status;
2873 void *res;
2874 const char *config_context, *base;
2875 const char *attrs[] = { "configurationNamingContext", NULL };
2876 const char *attrs2[] = { "uPNSuffixes", NULL };
2878 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2879 if (!ADS_ERR_OK(status)) {
2880 return status;
2883 config_context = ads_pull_string(ads, mem_ctx, res, "configurationNamingContext");
2884 if (config_context == NULL) {
2885 return ADS_ERROR(LDAP_NO_MEMORY);
2888 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
2889 if (base == NULL) {
2890 return ADS_ERROR(LDAP_NO_MEMORY);
2893 status = ads_search_dn(ads, &res, base, attrs2);
2894 if (!ADS_ERR_OK(status)) {
2895 return status;
2898 if (ads_count_replies(ads, res) != 1) {
2899 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2902 suffixes = ads_pull_strings(ads, mem_ctx, &res, "uPNSuffixes", num_suffixes);
2903 if (suffixes == NULL) {
2904 ads_msgfree(ads, res);
2905 return ADS_ERROR(LDAP_NO_MEMORY);
2908 ads_msgfree(ads, res);
2910 return status;
2913 #endif