r13657: Let winbindd try to obtain the gecos field from the msSFU30Gecos
[Samba/bb.git] / source3 / libads / ldap.c
blobcb7dbc575bc95367b5b89be917fee3a25a35d821
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
828 * Get the parent dn from a search result
829 * @param ads connection to ads server
830 * @param msg Search result
831 * @return parent dn string
833 char *ads_get_parent_dn(ADS_STRUCT *ads, void *msg)
835 char *mydn, *p, *dn;
837 dn = ads_get_dn(ads, msg);
838 if (dn == NULL) {
839 return NULL;
842 mydn = dn;
843 ads_memfree(ads, dn);
845 p = strchr(mydn, ',');
847 if (p == NULL) {
848 return NULL;
851 return p+1;
855 * Get the parent from a dn
856 * @param dn the dn to return the parent from
857 * @return parent dn string
859 char *ads_parent_dn(const char *dn)
861 char *p = strchr(dn, ',');
863 if (p == NULL) {
864 return NULL;
867 return p+1;
871 * Find a machine account given a hostname
872 * @param ads connection to ads server
873 * @param res ** which will contain results - free res* with ads_msgfree()
874 * @param host Hostname to search for
875 * @return status of search
877 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, void **res, const char *machine)
879 ADS_STATUS status;
880 char *expr;
881 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
883 *res = NULL;
885 /* the easiest way to find a machine account anywhere in the tree
886 is to look for hostname$ */
887 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
888 DEBUG(1, ("asprintf failed!\n"));
889 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
892 status = ads_search(ads, res, expr, attrs);
893 SAFE_FREE(expr);
894 return status;
898 * Initialize a list of mods to be used in a modify request
899 * @param ctx An initialized TALLOC_CTX
900 * @return allocated ADS_MODLIST
902 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
904 #define ADS_MODLIST_ALLOC_SIZE 10
905 LDAPMod **mods;
907 if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
908 /* -1 is safety to make sure we don't go over the end.
909 need to reset it to NULL before doing ldap modify */
910 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
912 return (ADS_MODLIST)mods;
917 add an attribute to the list, with values list already constructed
919 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
920 int mod_op, const char *name,
921 const void **invals)
923 int curmod;
924 LDAPMod **modlist = (LDAPMod **) *mods;
925 struct berval **ber_values = NULL;
926 char **char_values = NULL;
928 if (!invals) {
929 mod_op = LDAP_MOD_DELETE;
930 } else {
931 if (mod_op & LDAP_MOD_BVALUES)
932 ber_values = ads_dup_values(ctx,
933 (const struct berval **)invals);
934 else
935 char_values = ads_push_strvals(ctx,
936 (const char **) invals);
939 /* find the first empty slot */
940 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
941 curmod++);
942 if (modlist[curmod] == (LDAPMod *) -1) {
943 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
944 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
945 return ADS_ERROR(LDAP_NO_MEMORY);
946 memset(&modlist[curmod], 0,
947 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
948 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
949 *mods = (ADS_MODLIST)modlist;
952 if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
953 return ADS_ERROR(LDAP_NO_MEMORY);
954 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
955 if (mod_op & LDAP_MOD_BVALUES) {
956 modlist[curmod]->mod_bvalues = ber_values;
957 } else if (mod_op & LDAP_MOD_DELETE) {
958 modlist[curmod]->mod_values = NULL;
959 } else {
960 modlist[curmod]->mod_values = char_values;
963 modlist[curmod]->mod_op = mod_op;
964 return ADS_ERROR(LDAP_SUCCESS);
968 * Add a single string value to a mod list
969 * @param ctx An initialized TALLOC_CTX
970 * @param mods An initialized ADS_MODLIST
971 * @param name The attribute name to add
972 * @param val The value to add - NULL means DELETE
973 * @return ADS STATUS indicating success of add
975 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
976 const char *name, const char *val)
978 const char *values[2];
980 values[0] = val;
981 values[1] = NULL;
983 if (!val)
984 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
985 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name,
986 (const void **) values);
990 * Add an array of string values to a mod list
991 * @param ctx An initialized TALLOC_CTX
992 * @param mods An initialized ADS_MODLIST
993 * @param name The attribute name to add
994 * @param vals The array of string values to add - NULL means DELETE
995 * @return ADS STATUS indicating success of add
997 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
998 const char *name, const char **vals)
1000 if (!vals)
1001 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1002 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1003 name, (const void **) vals);
1007 * Add a single ber-encoded value to a mod list
1008 * @param ctx An initialized TALLOC_CTX
1009 * @param mods An initialized ADS_MODLIST
1010 * @param name The attribute name to add
1011 * @param val The value to add - NULL means DELETE
1012 * @return ADS STATUS indicating success of add
1014 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1015 const char *name, const struct berval *val)
1017 const struct berval *values[2];
1019 values[0] = val;
1020 values[1] = NULL;
1021 if (!val)
1022 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1023 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1024 name, (const void **) values);
1028 * Perform an ldap modify
1029 * @param ads connection to ads server
1030 * @param mod_dn DistinguishedName to modify
1031 * @param mods list of modifications to perform
1032 * @return status of modify
1034 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1036 int ret,i;
1037 char *utf8_dn = NULL;
1039 this control is needed to modify that contains a currently
1040 non-existent attribute (but allowable for the object) to run
1042 LDAPControl PermitModify = {
1043 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
1044 {0, NULL},
1045 (char) 1};
1046 LDAPControl *controls[2];
1048 controls[0] = &PermitModify;
1049 controls[1] = NULL;
1051 if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) {
1052 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1055 /* find the end of the list, marked by NULL or -1 */
1056 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1057 /* make sure the end of the list is NULL */
1058 mods[i] = NULL;
1059 ret = ldap_modify_ext_s(ads->ld, utf8_dn,
1060 (LDAPMod **) mods, controls, NULL);
1061 SAFE_FREE(utf8_dn);
1062 return ADS_ERROR(ret);
1066 * Perform an ldap add
1067 * @param ads connection to ads server
1068 * @param new_dn DistinguishedName to add
1069 * @param mods list of attributes and values for DN
1070 * @return status of add
1072 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1074 int ret, i;
1075 char *utf8_dn = NULL;
1077 if (push_utf8_allocate(&utf8_dn, new_dn) == -1) {
1078 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1079 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1082 /* find the end of the list, marked by NULL or -1 */
1083 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1084 /* make sure the end of the list is NULL */
1085 mods[i] = NULL;
1087 ret = ldap_add_s(ads->ld, utf8_dn, (LDAPMod**)mods);
1088 SAFE_FREE(utf8_dn);
1089 return ADS_ERROR(ret);
1093 * Delete a DistinguishedName
1094 * @param ads connection to ads server
1095 * @param new_dn DistinguishedName to delete
1096 * @return status of delete
1098 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1100 int ret;
1101 char *utf8_dn = NULL;
1102 if (push_utf8_allocate(&utf8_dn, del_dn) == -1) {
1103 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1104 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1107 ret = ldap_delete_s(ads->ld, utf8_dn);
1108 return ADS_ERROR(ret);
1112 * Build an org unit string
1113 * if org unit is Computers or blank then assume a container, otherwise
1114 * assume a \ separated list of organisational units
1115 * @param ads connection to ads server
1116 * @param org_unit Organizational unit
1117 * @return org unit string - caller must free
1119 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1121 char *ret = NULL;
1123 if (!org_unit || !*org_unit) {
1125 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1127 /* samba4 might not yet respond to a wellknownobject-query */
1128 return ret ? ret : SMB_STRDUP("cn=Computers");
1131 if (strequal(org_unit, "Computers")) {
1132 return SMB_STRDUP("cn=Computers");
1135 return ads_build_path(org_unit, "\\/", "ou=", 1);
1139 * Get a org unit string for a well-known GUID
1140 * @param ads connection to ads server
1141 * @param wknguid Well known GUID
1142 * @return org unit string - caller must free
1144 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1146 ADS_STATUS status;
1147 void *res;
1148 char *base, *wkn_dn, *ret, **wkn_dn_exp, **bind_dn_exp;
1149 const char *attrs[] = {"distinguishedName", NULL};
1150 int new_ln, wkn_ln, bind_ln, i;
1152 if (wknguid == NULL) {
1153 return NULL;
1156 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1157 DEBUG(1, ("asprintf failed!\n"));
1158 return NULL;
1161 status = ads_search_dn(ads, &res, base, attrs);
1162 if (!ADS_ERR_OK(status)) {
1163 DEBUG(1,("Failed while searching for: %s\n", base));
1164 return NULL;
1166 free(base);
1168 if (ads_count_replies(ads, res) != 1) {
1169 return NULL;
1172 /* substitute the bind-path from the well-known-guid-search result */
1173 wkn_dn = ads_get_dn(ads, res);
1174 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1175 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1177 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1179 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1182 new_ln = wkn_ln - bind_ln;
1184 ret = wkn_dn_exp[0];
1186 for (i=1; i < new_ln; i++) {
1187 char *s;
1188 asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]);
1189 ret = SMB_STRDUP(s);
1190 free(s);
1193 return ret;
1197 * Adds (appends) an item to an attribute array, rather then
1198 * replacing the whole list
1199 * @param ctx An initialized TALLOC_CTX
1200 * @param mods An initialized ADS_MODLIST
1201 * @param name name of the ldap attribute to append to
1202 * @param vals an array of values to add
1203 * @return status of addition
1206 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1207 const char *name, const char **vals)
1209 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name, (const void **) vals);
1213 * Determines the computer account's current KVNO via an LDAP lookup
1214 * @param ads An initialized ADS_STRUCT
1215 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1216 * @return the kvno for the computer account, or -1 in case of a failure.
1219 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *machine_name)
1221 LDAPMessage *res = NULL;
1222 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1223 char *filter;
1224 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1225 char *dn_string = NULL;
1226 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1228 DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name));
1229 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1230 return kvno;
1232 ret = ads_search(ads, (void**)(void *)&res, filter, attrs);
1233 SAFE_FREE(filter);
1234 if (!ADS_ERR_OK(ret) && ads_count_replies(ads, res)) {
1235 DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name));
1236 ads_msgfree(ads, res);
1237 return kvno;
1240 dn_string = ads_get_dn(ads, res);
1241 if (!dn_string) {
1242 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1243 ads_msgfree(ads, res);
1244 return kvno;
1246 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1247 ads_memfree(ads, dn_string);
1249 /* ---------------------------------------------------------
1250 * 0 is returned as a default KVNO from this point on...
1251 * This is done because Windows 2000 does not support key
1252 * version numbers. Chances are that a failure in the next
1253 * step is simply due to Windows 2000 being used for a
1254 * domain controller. */
1255 kvno = 0;
1257 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1258 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1259 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1260 ads_msgfree(ads, res);
1261 return kvno;
1264 /* Success */
1265 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1266 ads_msgfree(ads, res);
1267 return kvno;
1271 * This clears out all registered spn's for a given hostname
1272 * @param ads An initilaized ADS_STRUCT
1273 * @param machine_name the NetBIOS name of the computer.
1274 * @return 0 upon success, non-zero otherwise.
1277 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1279 TALLOC_CTX *ctx;
1280 LDAPMessage *res = NULL;
1281 ADS_MODLIST mods;
1282 const char *servicePrincipalName[1] = {NULL};
1283 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1284 char *dn_string = NULL;
1286 ret = ads_find_machine_acct(ads, (void **)(void *)&res, machine_name);
1287 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1288 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1289 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1290 ads_msgfree(ads, res);
1291 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1294 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1295 ctx = talloc_init("ads_clear_service_principal_names");
1296 if (!ctx) {
1297 ads_msgfree(ads, res);
1298 return ADS_ERROR(LDAP_NO_MEMORY);
1301 if (!(mods = ads_init_mods(ctx))) {
1302 talloc_destroy(ctx);
1303 ads_msgfree(ads, res);
1304 return ADS_ERROR(LDAP_NO_MEMORY);
1306 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1307 if (!ADS_ERR_OK(ret)) {
1308 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1309 ads_msgfree(ads, res);
1310 talloc_destroy(ctx);
1311 return ret;
1313 dn_string = ads_get_dn(ads, res);
1314 if (!dn_string) {
1315 talloc_destroy(ctx);
1316 ads_msgfree(ads, res);
1317 return ADS_ERROR(LDAP_NO_MEMORY);
1319 ret = ads_gen_mod(ads, dn_string, mods);
1320 ads_memfree(ads,dn_string);
1321 if (!ADS_ERR_OK(ret)) {
1322 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1323 machine_name));
1324 ads_msgfree(ads, res);
1325 talloc_destroy(ctx);
1326 return ret;
1329 ads_msgfree(ads, res);
1330 talloc_destroy(ctx);
1331 return ret;
1335 * This adds a service principal name to an existing computer account
1336 * (found by hostname) in AD.
1337 * @param ads An initialized ADS_STRUCT
1338 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1339 * @param spn A string of the service principal to add, i.e. 'host'
1340 * @return 0 upon sucess, or non-zero if a failure occurs
1343 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name, const char *spn)
1345 ADS_STATUS ret;
1346 TALLOC_CTX *ctx;
1347 LDAPMessage *res = NULL;
1348 char *host_spn, *psp1, *psp2, *psp3;
1349 ADS_MODLIST mods;
1350 fstring my_fqdn;
1351 char *dn_string = NULL;
1352 const char *servicePrincipalName[4] = {NULL, NULL, NULL, NULL};
1354 ret = ads_find_machine_acct(ads, (void **)(void *)&res, machine_name);
1355 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1356 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1357 machine_name));
1358 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1359 spn, machine_name, ads->config.realm));
1360 ads_msgfree(ads, res);
1361 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1364 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1365 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1366 ads_msgfree(ads, res);
1367 return ADS_ERROR(LDAP_NO_MEMORY);
1370 name_to_fqdn(my_fqdn, machine_name);
1371 strlower_m(my_fqdn);
1373 if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", my_fqdn))) {
1374 talloc_destroy(ctx);
1375 ads_msgfree(ads, res);
1376 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1379 /* Add the extra principal */
1380 psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name);
1381 strupper_m(psp1);
1382 strlower_m(&psp1[strlen(spn)]);
1383 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp1, machine_name));
1384 servicePrincipalName[0] = psp1;
1385 psp2 = talloc_asprintf(ctx, "%s/%s.%s", spn, machine_name, ads->config.realm);
1386 strupper_m(psp2);
1387 strlower_m(&psp2[strlen(spn)]);
1388 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp2, machine_name));
1389 servicePrincipalName[1] = psp2;
1391 /* Add another principal in case the realm != the DNS domain, so that
1392 * the KDC doesn't send "server principal unknown" errors to clients
1393 * which use the DNS name in determining service principal names. */
1394 psp3 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn);
1395 strupper_m(psp3);
1396 strlower_m(&psp3[strlen(spn)]);
1397 if (strcmp(psp2, psp3) != 0) {
1398 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp3, machine_name));
1399 servicePrincipalName[2] = psp3;
1402 if (!(mods = ads_init_mods(ctx))) {
1403 talloc_destroy(ctx);
1404 ads_msgfree(ads, res);
1405 return ADS_ERROR(LDAP_NO_MEMORY);
1407 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1408 if (!ADS_ERR_OK(ret)) {
1409 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1410 talloc_destroy(ctx);
1411 ads_msgfree(ads, res);
1412 return ret;
1414 dn_string = ads_get_dn(ads, res);
1415 if (!dn_string) {
1416 talloc_destroy(ctx);
1417 ads_msgfree(ads, res);
1418 return ADS_ERROR(LDAP_NO_MEMORY);
1420 ret = ads_gen_mod(ads, dn_string, mods);
1421 ads_memfree(ads,dn_string);
1422 if (!ADS_ERR_OK(ret)) {
1423 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1424 talloc_destroy(ctx);
1425 ads_msgfree(ads, res);
1426 return ret;
1429 talloc_destroy(ctx);
1430 ads_msgfree(ads, res);
1431 return ret;
1435 * adds a machine account to the ADS server
1436 * @param ads An intialized ADS_STRUCT
1437 * @param machine_name - the NetBIOS machine name of this account.
1438 * @param account_type A number indicating the type of account to create
1439 * @param org_unit The LDAP path in which to place this account
1440 * @return 0 upon success, or non-zero otherwise
1443 static ADS_STATUS ads_add_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1444 uint32 account_type,
1445 const char *org_unit)
1447 ADS_STATUS ret, status;
1448 char *host_spn, *host_upn, *new_dn, *samAccountName, *controlstr;
1449 TALLOC_CTX *ctx;
1450 ADS_MODLIST mods;
1451 const char *objectClass[] = {"top", "person", "organizationalPerson",
1452 "user", "computer", NULL};
1453 const char *servicePrincipalName[7] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
1454 char *psp, *psp2, *psp3, *psp4;
1455 unsigned acct_control;
1456 unsigned exists=0;
1457 fstring my_fqdn;
1458 LDAPMessage *res = NULL;
1459 int i, next_spn;
1461 if (!(ctx = talloc_init("ads_add_machine_acct")))
1462 return ADS_ERROR(LDAP_NO_MEMORY);
1464 ret = ADS_ERROR(LDAP_NO_MEMORY);
1466 name_to_fqdn(my_fqdn, machine_name);
1468 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine_name);
1469 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1470 char *dn_string = ads_get_dn(ads, res);
1471 if (!dn_string) {
1472 DEBUG(1, ("ads_add_machine_acct: ads_get_dn returned NULL (malloc failure?)\n"));
1473 goto done;
1475 new_dn = talloc_strdup(ctx, dn_string);
1476 ads_memfree(ads,dn_string);
1477 DEBUG(0, ("ads_add_machine_acct: Host account for %s already exists - modifying old account\n",
1478 machine_name));
1479 exists=1;
1480 } else {
1481 char *ou_str = ads_ou_string(ads,org_unit);
1482 if (!ou_str) {
1483 DEBUG(1, ("ads_add_machine_acct: ads_ou_string returned NULL (malloc failure?)\n"));
1484 goto done;
1486 new_dn = talloc_asprintf(ctx, "cn=%s,%s,%s", machine_name, ou_str,
1487 ads->config.bind_path);
1489 SAFE_FREE(ou_str);
1492 if (!new_dn) {
1493 goto done;
1496 if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", machine_name)))
1497 goto done;
1498 if (!(host_upn = talloc_asprintf(ctx, "%s@%s", host_spn, ads->config.realm)))
1499 goto done;
1500 servicePrincipalName[0] = talloc_asprintf(ctx, "HOST/%s", machine_name);
1501 psp = talloc_asprintf(ctx, "HOST/%s.%s",
1502 machine_name,
1503 ads->config.realm);
1504 strlower_m(&psp[5]);
1505 servicePrincipalName[1] = psp;
1506 servicePrincipalName[2] = talloc_asprintf(ctx, "CIFS/%s", machine_name);
1507 psp2 = talloc_asprintf(ctx, "CIFS/%s.%s",
1508 machine_name,
1509 ads->config.realm);
1510 strlower_m(&psp2[5]);
1511 servicePrincipalName[3] = psp2;
1513 /* Ensure servicePrincipalName[4] and [5] are unique. */
1514 strlower_m(my_fqdn);
1515 psp3 = talloc_asprintf(ctx, "CIFS/%s", my_fqdn);
1516 strlower_m(&psp3[5]);
1518 next_spn = 4;
1519 for (i = 0; i < next_spn; i++) {
1520 if (strequal(servicePrincipalName[i], psp3))
1521 break;
1523 if (i == next_spn) {
1524 servicePrincipalName[next_spn++] = psp3;
1527 psp4 = talloc_asprintf(ctx, "HOST/%s", my_fqdn);
1528 strlower_m(&psp4[5]);
1529 for (i = 0; i < next_spn; i++) {
1530 if (strequal(servicePrincipalName[i], psp4))
1531 break;
1533 if (i == next_spn) {
1534 servicePrincipalName[next_spn++] = psp4;
1537 if (!(samAccountName = talloc_asprintf(ctx, "%s$", machine_name))) {
1538 goto done;
1541 acct_control = account_type | UF_DONT_EXPIRE_PASSWD;
1542 #ifndef ENCTYPE_ARCFOUR_HMAC
1543 acct_control |= UF_USE_DES_KEY_ONLY;
1544 #endif
1546 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
1547 goto done;
1550 if (!(mods = ads_init_mods(ctx))) {
1551 goto done;
1554 if (!exists) {
1555 ads_mod_str(ctx, &mods, "cn", machine_name);
1556 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1557 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1558 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1560 ads_mod_str(ctx, &mods, "dNSHostName", my_fqdn);
1561 ads_mod_str(ctx, &mods, "userPrincipalName", host_upn);
1562 ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1563 ads_mod_str(ctx, &mods, "operatingSystem", "Samba");
1564 ads_mod_str(ctx, &mods, "operatingSystemVersion", SAMBA_VERSION_STRING);
1566 if (!exists) {
1567 ret = ads_gen_add(ads, new_dn, mods);
1568 } else {
1569 ret = ads_gen_mod(ads, new_dn, mods);
1572 if (!ADS_ERR_OK(ret)) {
1573 goto done;
1576 /* Do not fail if we can't set security descriptor
1577 * it shouldn't be mandatory and probably we just
1578 * don't have enough rights to do it.
1580 if (!exists) {
1581 status = ads_set_machine_sd(ads, machine_name, new_dn);
1583 if (!ADS_ERR_OK(status)) {
1584 DEBUG(0, ("Warning: ads_set_machine_sd: %s\n",
1585 ads_errstr(status)));
1588 done:
1589 ads_msgfree(ads, res);
1590 talloc_destroy(ctx);
1591 return ret;
1595 dump a binary result from ldap
1597 static void dump_binary(const char *field, struct berval **values)
1599 int i, j;
1600 for (i=0; values[i]; i++) {
1601 printf("%s: ", field);
1602 for (j=0; j<values[i]->bv_len; j++) {
1603 printf("%02X", (unsigned char)values[i]->bv_val[j]);
1605 printf("\n");
1609 static void dump_guid(const char *field, struct berval **values)
1611 int i;
1612 UUID_FLAT guid;
1613 for (i=0; values[i]; i++) {
1614 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1615 printf("%s: %s\n", field,
1616 smb_uuid_string_static(smb_uuid_unpack_static(guid)));
1621 dump a sid result from ldap
1623 static void dump_sid(const char *field, struct berval **values)
1625 int i;
1626 for (i=0; values[i]; i++) {
1627 DOM_SID sid;
1628 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1629 printf("%s: %s\n", field, sid_string_static(&sid));
1634 dump ntSecurityDescriptor
1636 static void dump_sd(const char *filed, struct berval **values)
1638 prs_struct ps;
1640 SEC_DESC *psd = 0;
1641 TALLOC_CTX *ctx = 0;
1643 if (!(ctx = talloc_init("sec_io_desc")))
1644 return;
1646 /* prepare data */
1647 prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
1648 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
1649 prs_set_offset(&ps,0);
1651 /* parse secdesc */
1652 if (!sec_io_desc("sd", &psd, &ps, 1)) {
1653 prs_mem_free(&ps);
1654 talloc_destroy(ctx);
1655 return;
1657 if (psd) ads_disp_sd(psd);
1659 prs_mem_free(&ps);
1660 talloc_destroy(ctx);
1664 dump a string result from ldap
1666 static void dump_string(const char *field, char **values)
1668 int i;
1669 for (i=0; values[i]; i++) {
1670 printf("%s: %s\n", field, values[i]);
1675 dump a field from LDAP on stdout
1676 used for debugging
1679 static BOOL ads_dump_field(char *field, void **values, void *data_area)
1681 const struct {
1682 const char *name;
1683 BOOL string;
1684 void (*handler)(const char *, struct berval **);
1685 } handlers[] = {
1686 {"objectGUID", False, dump_guid},
1687 {"netbootGUID", False, dump_guid},
1688 {"nTSecurityDescriptor", False, dump_sd},
1689 {"dnsRecord", False, dump_binary},
1690 {"objectSid", False, dump_sid},
1691 {"tokenGroups", False, dump_sid},
1692 {NULL, True, NULL}
1694 int i;
1696 if (!field) { /* must be end of an entry */
1697 printf("\n");
1698 return False;
1701 for (i=0; handlers[i].name; i++) {
1702 if (StrCaseCmp(handlers[i].name, field) == 0) {
1703 if (!values) /* first time, indicate string or not */
1704 return handlers[i].string;
1705 handlers[i].handler(field, (struct berval **) values);
1706 break;
1709 if (!handlers[i].name) {
1710 if (!values) /* first time, indicate string conversion */
1711 return True;
1712 dump_string(field, (char **)values);
1714 return False;
1718 * Dump a result from LDAP on stdout
1719 * used for debugging
1720 * @param ads connection to ads server
1721 * @param res Results to dump
1724 void ads_dump(ADS_STRUCT *ads, void *res)
1726 ads_process_results(ads, res, ads_dump_field, NULL);
1730 * Walk through results, calling a function for each entry found.
1731 * The function receives a field name, a berval * array of values,
1732 * and a data area passed through from the start. The function is
1733 * called once with null for field and values at the end of each
1734 * entry.
1735 * @param ads connection to ads server
1736 * @param res Results to process
1737 * @param fn Function for processing each result
1738 * @param data_area user-defined area to pass to function
1740 void ads_process_results(ADS_STRUCT *ads, void *res,
1741 BOOL(*fn)(char *, void **, void *),
1742 void *data_area)
1744 void *msg;
1745 TALLOC_CTX *ctx;
1747 if (!(ctx = talloc_init("ads_process_results")))
1748 return;
1750 for (msg = ads_first_entry(ads, res); msg;
1751 msg = ads_next_entry(ads, msg)) {
1752 char *utf8_field;
1753 BerElement *b;
1755 for (utf8_field=ldap_first_attribute(ads->ld,
1756 (LDAPMessage *)msg,&b);
1757 utf8_field;
1758 utf8_field=ldap_next_attribute(ads->ld,
1759 (LDAPMessage *)msg,b)) {
1760 struct berval **ber_vals;
1761 char **str_vals, **utf8_vals;
1762 char *field;
1763 BOOL string;
1765 pull_utf8_talloc(ctx, &field, utf8_field);
1766 string = fn(field, NULL, data_area);
1768 if (string) {
1769 utf8_vals = ldap_get_values(ads->ld,
1770 (LDAPMessage *)msg, field);
1771 str_vals = ads_pull_strvals(ctx,
1772 (const char **) utf8_vals);
1773 fn(field, (void **) str_vals, data_area);
1774 ldap_value_free(utf8_vals);
1775 } else {
1776 ber_vals = ldap_get_values_len(ads->ld,
1777 (LDAPMessage *)msg, field);
1778 fn(field, (void **) ber_vals, data_area);
1780 ldap_value_free_len(ber_vals);
1782 ldap_memfree(utf8_field);
1784 ber_free(b, 0);
1785 talloc_free_children(ctx);
1786 fn(NULL, NULL, data_area); /* completed an entry */
1789 talloc_destroy(ctx);
1793 * count how many replies are in a LDAPMessage
1794 * @param ads connection to ads server
1795 * @param res Results to count
1796 * @return number of replies
1798 int ads_count_replies(ADS_STRUCT *ads, void *res)
1800 return ldap_count_entries(ads->ld, (LDAPMessage *)res);
1804 * Join a machine to a realm
1805 * Creates the machine account and sets the machine password
1806 * @param ads connection to ads server
1807 * @param machine name of host to add
1808 * @param org_unit Organizational unit to place machine in
1809 * @return status of join
1811 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
1812 uint32 account_type, const char *org_unit)
1814 ADS_STATUS status;
1815 LDAPMessage *res = NULL;
1816 char *machine;
1818 /* machine name must be lowercase */
1819 machine = SMB_STRDUP(machine_name);
1820 strlower_m(machine);
1823 status = ads_find_machine_acct(ads, (void **)&res, machine);
1824 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1825 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
1826 status = ads_leave_realm(ads, machine);
1827 if (!ADS_ERR_OK(status)) {
1828 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
1829 machine, ads->config.realm));
1830 return status;
1835 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
1836 if (!ADS_ERR_OK(status)) {
1837 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
1838 SAFE_FREE(machine);
1839 return status;
1842 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
1843 if (!ADS_ERR_OK(status)) {
1844 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
1845 SAFE_FREE(machine);
1846 return status;
1849 SAFE_FREE(machine);
1850 ads_msgfree(ads, res);
1852 return status;
1856 * Delete a machine from the realm
1857 * @param ads connection to ads server
1858 * @param hostname Machine to remove
1859 * @return status of delete
1861 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
1863 ADS_STATUS status;
1864 void *res, *msg;
1865 char *hostnameDN, *host;
1866 int rc;
1867 LDAPControl ldap_control;
1868 LDAPControl * pldap_control[2] = {NULL, NULL};
1870 pldap_control[0] = &ldap_control;
1871 memset(&ldap_control, 0, sizeof(LDAPControl));
1872 ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
1874 /* hostname must be lowercase */
1875 host = SMB_STRDUP(hostname);
1876 strlower_m(host);
1878 status = ads_find_machine_acct(ads, &res, host);
1879 if (!ADS_ERR_OK(status)) {
1880 DEBUG(0, ("Host account for %s does not exist.\n", host));
1881 return status;
1884 msg = ads_first_entry(ads, res);
1885 if (!msg) {
1886 return ADS_ERROR_SYSTEM(ENOENT);
1889 hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
1892 rc = ldap_delete_ext_s(ads->ld, hostnameDN, pldap_control, NULL);
1893 if (rc) {
1894 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
1895 }else {
1896 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
1899 ads_memfree(ads, hostnameDN);
1900 if (rc != LDAP_SUCCESS) {
1901 return ADS_ERROR(rc);
1904 status = ads_find_machine_acct(ads, &res, host);
1905 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1906 DEBUG(0, ("Failed to remove host account.\n"));
1907 return status;
1910 free(host);
1912 return status;
1916 * add machine account to existing security descriptor
1917 * @param ads connection to ads server
1918 * @param hostname machine to add
1919 * @param dn DN of security descriptor
1920 * @return status
1922 ADS_STATUS ads_set_machine_sd(ADS_STRUCT *ads, const char *hostname, char *dn)
1924 const char *attrs[] = {"nTSecurityDescriptor", "objectSid", 0};
1925 char *expr = 0;
1926 size_t sd_size = 0;
1927 struct berval bval = {0, NULL};
1928 prs_struct ps_wire;
1929 char *escaped_hostname = escape_ldap_string_alloc(hostname);
1931 LDAPMessage *res = 0;
1932 LDAPMessage *msg = 0;
1933 ADS_MODLIST mods = 0;
1935 NTSTATUS status;
1936 ADS_STATUS ret;
1937 DOM_SID sid;
1938 SEC_DESC *psd = NULL;
1939 TALLOC_CTX *ctx = NULL;
1941 /* Avoid segmentation fault in prs_mem_free if
1942 * we have to bail out before prs_init */
1943 ps_wire.is_dynamic = False;
1945 if (!ads) return ADS_ERROR(LDAP_SERVER_DOWN);
1947 ret = ADS_ERROR(LDAP_SUCCESS);
1949 if (!escaped_hostname) {
1950 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1953 if (asprintf(&expr, "(samAccountName=%s$)", escaped_hostname) == -1) {
1954 DEBUG(1, ("ads_set_machine_sd: asprintf failed!\n"));
1955 SAFE_FREE(escaped_hostname);
1956 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1959 SAFE_FREE(escaped_hostname);
1961 ret = ads_search(ads, (void *) &res, expr, attrs);
1963 if (!ADS_ERR_OK(ret)) return ret;
1965 if ( !(msg = ads_first_entry(ads, res) )) {
1966 ret = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1967 goto ads_set_sd_error;
1970 if (!ads_pull_sid(ads, msg, attrs[1], &sid)) {
1971 ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
1972 goto ads_set_sd_error;
1975 if (!(ctx = talloc_init("sec_io_desc"))) {
1976 ret = ADS_ERROR(LDAP_NO_MEMORY);
1977 goto ads_set_sd_error;
1980 if (!ads_pull_sd(ads, ctx, msg, attrs[0], &psd)) {
1981 ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
1982 goto ads_set_sd_error;
1985 status = sec_desc_add_sid(ctx, &psd, &sid, SEC_RIGHTS_FULL_CTRL, &sd_size);
1987 if (!NT_STATUS_IS_OK(status)) {
1988 ret = ADS_ERROR_NT(status);
1989 goto ads_set_sd_error;
1992 if (!prs_init(&ps_wire, sd_size, ctx, MARSHALL)) {
1993 ret = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1996 if (!sec_io_desc("sd_wire", &psd, &ps_wire, 1)) {
1997 ret = ADS_ERROR(LDAP_NO_MEMORY);
1998 goto ads_set_sd_error;
2001 #if 0
2002 file_save("/tmp/sec_desc.new", ps_wire.data_p, sd_size);
2003 #endif
2004 if (!(mods = ads_init_mods(ctx))) return ADS_ERROR(LDAP_NO_MEMORY);
2006 bval.bv_len = prs_offset(&ps_wire);
2007 bval.bv_val = TALLOC(ctx, bval.bv_len);
2008 if (!bval.bv_val) {
2009 ret = ADS_ERROR(LDAP_NO_MEMORY);
2010 goto ads_set_sd_error;
2013 prs_set_offset(&ps_wire, 0);
2015 if (!prs_copy_data_out(bval.bv_val, &ps_wire, bval.bv_len)) {
2016 ret = ADS_ERROR(LDAP_NO_MEMORY);
2017 goto ads_set_sd_error;
2020 ret = ads_mod_ber(ctx, &mods, attrs[0], &bval);
2021 if (ADS_ERR_OK(ret)) {
2022 ret = ads_gen_mod(ads, dn, mods);
2025 ads_set_sd_error:
2026 ads_msgfree(ads, res);
2027 prs_mem_free(&ps_wire);
2028 talloc_destroy(ctx);
2029 return ret;
2033 * pull the first entry from a ADS result
2034 * @param ads connection to ads server
2035 * @param res Results of search
2036 * @return first entry from result
2038 void *ads_first_entry(ADS_STRUCT *ads, void *res)
2040 return (void *)ldap_first_entry(ads->ld, (LDAPMessage *)res);
2044 * pull the next entry from a ADS result
2045 * @param ads connection to ads server
2046 * @param res Results of search
2047 * @return next entry from result
2049 void *ads_next_entry(ADS_STRUCT *ads, void *res)
2051 return (void *)ldap_next_entry(ads->ld, (LDAPMessage *)res);
2055 * pull a single string from a ADS result
2056 * @param ads connection to ads server
2057 * @param mem_ctx TALLOC_CTX to use for allocating result string
2058 * @param msg Results of search
2059 * @param field Attribute to retrieve
2060 * @return Result string in talloc context
2062 char *ads_pull_string(ADS_STRUCT *ads,
2063 TALLOC_CTX *mem_ctx, void *msg, const char *field)
2065 char **values;
2066 char *ret = NULL;
2067 char *ux_string;
2068 size_t rc;
2070 values = ldap_get_values(ads->ld, msg, field);
2071 if (!values)
2072 return NULL;
2074 if (values[0]) {
2075 rc = pull_utf8_talloc(mem_ctx, &ux_string,
2076 values[0]);
2077 if (rc != (size_t)-1)
2078 ret = ux_string;
2081 ldap_value_free(values);
2082 return ret;
2086 * pull an array of strings from a ADS result
2087 * @param ads connection to ads server
2088 * @param mem_ctx TALLOC_CTX to use for allocating result string
2089 * @param msg Results of search
2090 * @param field Attribute to retrieve
2091 * @return Result strings in talloc context
2093 char **ads_pull_strings(ADS_STRUCT *ads,
2094 TALLOC_CTX *mem_ctx, void *msg, const char *field,
2095 size_t *num_values)
2097 char **values;
2098 char **ret = NULL;
2099 int i;
2101 values = ldap_get_values(ads->ld, msg, field);
2102 if (!values)
2103 return NULL;
2105 *num_values = ldap_count_values(values);
2107 ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
2108 if (!ret) {
2109 ldap_value_free(values);
2110 return NULL;
2113 for (i=0;i<*num_values;i++) {
2114 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
2115 ldap_value_free(values);
2116 return NULL;
2119 ret[i] = NULL;
2121 ldap_value_free(values);
2122 return ret;
2126 * pull an array of strings from a ADS result
2127 * (handle large multivalue attributes with range retrieval)
2128 * @param ads connection to ads server
2129 * @param mem_ctx TALLOC_CTX to use for allocating result string
2130 * @param msg Results of search
2131 * @param field Attribute to retrieve
2132 * @param current_strings strings returned by a previous call to this function
2133 * @param next_attribute The next query should ask for this attribute
2134 * @param num_values How many values did we get this time?
2135 * @param more_values Are there more values to get?
2136 * @return Result strings in talloc context
2138 char **ads_pull_strings_range(ADS_STRUCT *ads,
2139 TALLOC_CTX *mem_ctx,
2140 void *msg, const char *field,
2141 char **current_strings,
2142 const char **next_attribute,
2143 size_t *num_strings,
2144 BOOL *more_strings)
2146 char *attr;
2147 char *expected_range_attrib, *range_attr;
2148 BerElement *ptr = NULL;
2149 char **strings;
2150 char **new_strings;
2151 size_t num_new_strings;
2152 unsigned long int range_start;
2153 unsigned long int range_end;
2155 /* we might have been given the whole lot anyway */
2156 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2157 *more_strings = False;
2158 return strings;
2161 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2163 /* look for Range result */
2164 for (attr = ldap_first_attribute(ads->ld, (LDAPMessage *)msg, &ptr);
2165 attr;
2166 attr = ldap_next_attribute(ads->ld, (LDAPMessage *)msg, ptr)) {
2167 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2168 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2169 range_attr = attr;
2170 break;
2172 ldap_memfree(attr);
2174 if (!attr) {
2175 ber_free(ptr, 0);
2176 /* nothing here - this field is just empty */
2177 *more_strings = False;
2178 return NULL;
2181 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2182 &range_start, &range_end) == 2) {
2183 *more_strings = True;
2184 } else {
2185 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2186 &range_start) == 1) {
2187 *more_strings = False;
2188 } else {
2189 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2190 range_attr));
2191 ldap_memfree(range_attr);
2192 *more_strings = False;
2193 return NULL;
2197 if ((*num_strings) != range_start) {
2198 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2199 " - aborting range retreival\n",
2200 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2201 ldap_memfree(range_attr);
2202 *more_strings = False;
2203 return NULL;
2206 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2208 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2209 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2210 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2211 range_attr, (unsigned long int)range_end - range_start + 1,
2212 (unsigned long int)num_new_strings));
2213 ldap_memfree(range_attr);
2214 *more_strings = False;
2215 return NULL;
2218 strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2219 *num_strings + num_new_strings);
2221 if (strings == NULL) {
2222 ldap_memfree(range_attr);
2223 *more_strings = False;
2224 return NULL;
2227 memcpy(&strings[*num_strings], new_strings,
2228 sizeof(*new_strings) * num_new_strings);
2230 (*num_strings) += num_new_strings;
2232 if (*more_strings) {
2233 *next_attribute = talloc_asprintf(mem_ctx,
2234 "%s;range=%d-*",
2235 field,
2236 (int)*num_strings);
2238 if (!*next_attribute) {
2239 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2240 ldap_memfree(range_attr);
2241 *more_strings = False;
2242 return NULL;
2246 ldap_memfree(range_attr);
2248 return strings;
2252 * pull a single uint32 from a ADS result
2253 * @param ads connection to ads server
2254 * @param msg Results of search
2255 * @param field Attribute to retrieve
2256 * @param v Pointer to int to store result
2257 * @return boolean inidicating success
2259 BOOL ads_pull_uint32(ADS_STRUCT *ads,
2260 void *msg, const char *field, uint32 *v)
2262 char **values;
2264 values = ldap_get_values(ads->ld, msg, field);
2265 if (!values)
2266 return False;
2267 if (!values[0]) {
2268 ldap_value_free(values);
2269 return False;
2272 *v = atoi(values[0]);
2273 ldap_value_free(values);
2274 return True;
2278 * pull a single objectGUID from an ADS result
2279 * @param ads connection to ADS server
2280 * @param msg results of search
2281 * @param guid 37-byte area to receive text guid
2282 * @return boolean indicating success
2284 BOOL ads_pull_guid(ADS_STRUCT *ads,
2285 void *msg, struct uuid *guid)
2287 char **values;
2288 UUID_FLAT flat_guid;
2290 values = ldap_get_values(ads->ld, msg, "objectGUID");
2291 if (!values)
2292 return False;
2294 if (values[0]) {
2295 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2296 smb_uuid_unpack(flat_guid, guid);
2297 ldap_value_free(values);
2298 return True;
2300 ldap_value_free(values);
2301 return False;
2307 * pull a single DOM_SID from a ADS result
2308 * @param ads connection to ads server
2309 * @param msg Results of search
2310 * @param field Attribute to retrieve
2311 * @param sid Pointer to sid to store result
2312 * @return boolean inidicating success
2314 BOOL ads_pull_sid(ADS_STRUCT *ads,
2315 void *msg, const char *field, DOM_SID *sid)
2317 struct berval **values;
2318 BOOL ret = False;
2320 values = ldap_get_values_len(ads->ld, msg, field);
2322 if (!values)
2323 return False;
2325 if (values[0])
2326 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2328 ldap_value_free_len(values);
2329 return ret;
2333 * pull an array of DOM_SIDs from a ADS result
2334 * @param ads connection to ads server
2335 * @param mem_ctx TALLOC_CTX for allocating sid array
2336 * @param msg Results of search
2337 * @param field Attribute to retrieve
2338 * @param sids pointer to sid array to allocate
2339 * @return the count of SIDs pulled
2341 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2342 void *msg, const char *field, DOM_SID **sids)
2344 struct berval **values;
2345 BOOL ret;
2346 int count, i;
2348 values = ldap_get_values_len(ads->ld, msg, field);
2350 if (!values)
2351 return 0;
2353 for (i=0; values[i]; i++)
2354 /* nop */ ;
2356 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2357 if (!(*sids)) {
2358 ldap_value_free_len(values);
2359 return 0;
2362 count = 0;
2363 for (i=0; values[i]; i++) {
2364 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2365 if (ret) {
2366 fstring sid;
2367 DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid, &(*sids)[count])));
2368 count++;
2372 ldap_value_free_len(values);
2373 return count;
2377 * pull a SEC_DESC from a ADS result
2378 * @param ads connection to ads server
2379 * @param mem_ctx TALLOC_CTX for allocating sid array
2380 * @param msg Results of search
2381 * @param field Attribute to retrieve
2382 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2383 * @return boolean inidicating success
2385 BOOL ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2386 void *msg, const char *field, SEC_DESC **sd)
2388 struct berval **values;
2389 prs_struct ps;
2390 BOOL ret = False;
2392 values = ldap_get_values_len(ads->ld, msg, field);
2394 if (!values) return False;
2396 if (values[0]) {
2397 prs_init(&ps, values[0]->bv_len, mem_ctx, UNMARSHALL);
2398 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
2399 prs_set_offset(&ps,0);
2401 ret = sec_io_desc("sd", sd, &ps, 1);
2404 ldap_value_free_len(values);
2405 return ret;
2409 * in order to support usernames longer than 21 characters we need to
2410 * use both the sAMAccountName and the userPrincipalName attributes
2411 * It seems that not all users have the userPrincipalName attribute set
2413 * @param ads connection to ads server
2414 * @param mem_ctx TALLOC_CTX for allocating sid array
2415 * @param msg Results of search
2416 * @return the username
2418 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, void *msg)
2420 #if 0 /* JERRY */
2421 char *ret, *p;
2423 /* lookup_name() only works on the sAMAccountName to
2424 returning the username portion of userPrincipalName
2425 breaks winbindd_getpwnam() */
2427 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2428 if (ret && (p = strchr_m(ret, '@'))) {
2429 *p = 0;
2430 return ret;
2432 #endif
2433 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2438 * find the update serial number - this is the core of the ldap cache
2439 * @param ads connection to ads server
2440 * @param ads connection to ADS server
2441 * @param usn Pointer to retrieved update serial number
2442 * @return status of search
2444 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2446 const char *attrs[] = {"highestCommittedUSN", NULL};
2447 ADS_STATUS status;
2448 void *res;
2450 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2451 if (!ADS_ERR_OK(status))
2452 return status;
2454 if (ads_count_replies(ads, res) != 1) {
2455 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2458 ads_pull_uint32(ads, res, "highestCommittedUSN", usn);
2459 ads_msgfree(ads, res);
2460 return ADS_SUCCESS;
2463 /* parse a ADS timestring - typical string is
2464 '20020917091222.0Z0' which means 09:12.22 17th September
2465 2002, timezone 0 */
2466 static time_t ads_parse_time(const char *str)
2468 struct tm tm;
2470 ZERO_STRUCT(tm);
2472 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2473 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2474 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2475 return 0;
2477 tm.tm_year -= 1900;
2478 tm.tm_mon -= 1;
2480 return timegm(&tm);
2484 const char *ads_get_attrname_by_oid(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char * OID)
2486 ADS_STATUS rc;
2487 int count = 0;
2488 void *res = NULL;
2489 char *expr = NULL;
2490 const char *attrs[] = { "lDAPDisplayName", NULL };
2492 if (ads == NULL || mem_ctx == NULL || OID == NULL) {
2493 goto failed;
2496 expr = talloc_asprintf(mem_ctx, "(attributeId=%s)", OID);
2497 if (expr == NULL) {
2498 goto failed;
2501 rc = ads_do_search_retry(ads, ads->config.schema_path,
2502 LDAP_SCOPE_SUBTREE, expr, attrs, &res);
2503 if (!ADS_ERR_OK(rc)) {
2504 goto failed;
2507 count = ads_count_replies(ads, res);
2508 if (count == 0 || !res) {
2509 goto failed;
2512 return ads_pull_string(ads, mem_ctx, res, "lDAPDisplayName");
2514 failed:
2515 DEBUG(0,("ads_get_attrname_by_oid: failed to retrieve name for oid: %s\n",
2516 OID));
2518 return NULL;
2522 * Find the servers name and realm - this can be done before authentication
2523 * The ldapServiceName field on w2k looks like this:
2524 * vnet3.home.samba.org:win2000-vnet3$@VNET3.HOME.SAMBA.ORG
2525 * @param ads connection to ads server
2526 * @return status of search
2528 ADS_STATUS ads_server_info(ADS_STRUCT *ads)
2530 const char *attrs[] = {"ldapServiceName",
2531 "currentTime",
2532 "schemaNamingContext", NULL};
2533 ADS_STATUS status;
2534 void *res;
2535 char *value;
2536 char *p;
2537 char *timestr;
2538 char *schema_path;
2539 TALLOC_CTX *ctx;
2541 if (!(ctx = talloc_init("ads_server_info"))) {
2542 return ADS_ERROR(LDAP_NO_MEMORY);
2545 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2546 if (!ADS_ERR_OK(status)) {
2547 talloc_destroy(ctx);
2548 return status;
2551 value = ads_pull_string(ads, ctx, res, "ldapServiceName");
2552 if (!value) {
2553 ads_msgfree(ads, res);
2554 talloc_destroy(ctx);
2555 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2558 timestr = ads_pull_string(ads, ctx, res, "currentTime");
2559 if (!timestr) {
2560 ads_msgfree(ads, res);
2561 talloc_destroy(ctx);
2562 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2565 schema_path = ads_pull_string(ads, ctx, res, "schemaNamingContext");
2566 if (!schema_path) {
2567 ads_msgfree(ads, res);
2568 talloc_destroy(ctx);
2569 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2572 SAFE_FREE(ads->config.schema_path);
2573 ads->config.schema_path = SMB_STRDUP(schema_path);
2575 ads_msgfree(ads, res);
2577 p = strchr(value, ':');
2578 if (!p) {
2579 talloc_destroy(ctx);
2580 DEBUG(1, ("ads_server_info: returned ldap server name did not contain a ':' "
2581 "so was deemed invalid\n"));
2582 return ADS_ERROR(LDAP_DECODING_ERROR);
2585 SAFE_FREE(ads->config.ldap_server_name);
2587 ads->config.ldap_server_name = SMB_STRDUP(p+1);
2588 p = strchr(ads->config.ldap_server_name, '$');
2589 if (!p || p[1] != '@') {
2590 talloc_destroy(ctx);
2591 DEBUG(1, ("ads_server_info: returned ldap server name (%s) does not contain '$@'"
2592 " so was deemed invalid\n", ads->config.ldap_server_name));
2593 SAFE_FREE(ads->config.ldap_server_name);
2594 return ADS_ERROR(LDAP_DECODING_ERROR);
2597 *p = 0;
2599 SAFE_FREE(ads->config.realm);
2600 SAFE_FREE(ads->config.bind_path);
2602 ads->config.realm = SMB_STRDUP(p+2);
2603 ads->config.bind_path = ads_build_dn(ads->config.realm);
2605 DEBUG(3,("got ldap server name %s@%s, using bind path: %s\n",
2606 ads->config.ldap_server_name, ads->config.realm,
2607 ads->config.bind_path));
2609 ads->config.current_time = ads_parse_time(timestr);
2611 if (ads->config.current_time != 0) {
2612 ads->auth.time_offset = ads->config.current_time - time(NULL);
2613 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2616 talloc_destroy(ctx);
2618 return ADS_SUCCESS;
2622 * Check for "Services for Unix"-Schema and load some attributes into the ADS_STRUCT
2623 * @param ads connection to ads server
2624 * @return BOOL status of search (False if one or more attributes couldn't be
2625 * found in Active Directory)
2626 **/
2627 BOOL ads_check_sfu_mapping(ADS_STRUCT *ads)
2629 BOOL ret = False;
2630 TALLOC_CTX *ctx = NULL;
2631 const char *gidnumber, *uidnumber, *homedir, *shell, *gecos;
2633 ctx = talloc_init("ads_check_sfu_mapping");
2634 if (ctx == NULL)
2635 goto done;
2637 gidnumber = ads_get_attrname_by_oid(ads, ctx, ADS_ATTR_SFU_GIDNUMBER_OID);
2638 if (gidnumber == NULL)
2639 goto done;
2640 ads->schema.sfu_gidnumber_attr = SMB_STRDUP(gidnumber);
2642 uidnumber = ads_get_attrname_by_oid(ads, ctx, ADS_ATTR_SFU_UIDNUMBER_OID);
2643 if (uidnumber == NULL)
2644 goto done;
2645 ads->schema.sfu_uidnumber_attr = SMB_STRDUP(uidnumber);
2647 homedir = ads_get_attrname_by_oid(ads, ctx, ADS_ATTR_SFU_HOMEDIR_OID);
2648 if (homedir == NULL)
2649 goto done;
2650 ads->schema.sfu_homedir_attr = SMB_STRDUP(homedir);
2652 shell = ads_get_attrname_by_oid(ads, ctx, ADS_ATTR_SFU_SHELL_OID);
2653 if (shell == NULL)
2654 goto done;
2655 ads->schema.sfu_shell_attr = SMB_STRDUP(shell);
2657 gecos = ads_get_attrname_by_oid(ads, ctx, ADS_ATTR_SFU_GECOS_OID);
2658 if (gecos == NULL)
2659 goto done;
2660 ads->schema.sfu_gecos_attr = SMB_STRDUP(gecos);
2662 ret = True;
2663 done:
2664 if (ctx)
2665 talloc_destroy(ctx);
2667 return ret;
2671 * find the domain sid for our domain
2672 * @param ads connection to ads server
2673 * @param sid Pointer to domain sid
2674 * @return status of search
2676 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2678 const char *attrs[] = {"objectSid", NULL};
2679 void *res;
2680 ADS_STATUS rc;
2682 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2683 attrs, &res);
2684 if (!ADS_ERR_OK(rc)) return rc;
2685 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2686 ads_msgfree(ads, res);
2687 return ADS_ERROR_SYSTEM(ENOENT);
2689 ads_msgfree(ads, res);
2691 return ADS_SUCCESS;
2694 /* this is rather complex - we need to find the allternate (netbios) name
2695 for the domain, but there isn't a simple query to do this. Instead
2696 we look for the principle names on the DCs account and find one that has
2697 the right form, then extract the netbios name of the domain from that
2699 NOTE! better method is this:
2701 bin/net -Uadministrator%XXXXX ads search '(&(objectclass=crossref)(dnsroot=VNET3.HOME.SAMBA.ORG))' nETBIOSName
2703 but you need to force the bind path to match the configurationNamingContext from the rootDSE
2706 ADS_STATUS ads_workgroup_name(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **workgroup)
2708 char *expr;
2709 ADS_STATUS rc;
2710 char **principles;
2711 char *prefix;
2712 int prefix_length;
2713 int i;
2714 void *res;
2715 const char *attrs[] = {"servicePrincipalName", NULL};
2716 size_t num_principals;
2718 (*workgroup) = NULL;
2720 asprintf(&expr, "(&(objectclass=computer)(dnshostname=%s.%s))",
2721 ads->config.ldap_server_name, ads->config.realm);
2722 if (expr == NULL) {
2723 ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2726 rc = ads_search(ads, &res, expr, attrs);
2727 free(expr);
2729 if (!ADS_ERR_OK(rc)) {
2730 return rc;
2733 principles = ads_pull_strings(ads, mem_ctx, res,
2734 "servicePrincipalName", &num_principals);
2736 ads_msgfree(ads, res);
2738 if (!principles) {
2739 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2742 asprintf(&prefix, "HOST/%s.%s/",
2743 ads->config.ldap_server_name,
2744 ads->config.realm);
2746 prefix_length = strlen(prefix);
2748 for (i=0;principles[i]; i++) {
2749 if (strnequal(principles[i], prefix, prefix_length) &&
2750 !strequal(ads->config.realm, principles[i]+prefix_length) &&
2751 !strchr(principles[i]+prefix_length, '.')) {
2752 /* found an alternate (short) name for the domain. */
2753 DEBUG(3,("Found alternate name '%s' for realm '%s'\n",
2754 principles[i]+prefix_length,
2755 ads->config.realm));
2756 (*workgroup) = talloc_strdup(mem_ctx, principles[i]+prefix_length);
2757 break;
2760 free(prefix);
2762 if (!*workgroup) {
2763 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2766 return ADS_SUCCESS;
2770 * find our site name
2771 * @param ads connection to ads server
2772 * @param mem_ctx Pointer to talloc context
2773 * @param site_name Pointer to the sitename
2774 * @return status of search
2776 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2778 ADS_STATUS status;
2779 void *res;
2780 const char *dn, *service_name;
2781 const char *attrs[] = { "dsServiceName", NULL };
2783 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2784 if (!ADS_ERR_OK(status)) {
2785 return status;
2788 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2789 if (service_name == NULL) {
2790 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2793 /* go up three levels */
2794 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2795 if (dn == NULL) {
2796 return ADS_ERROR(LDAP_NO_MEMORY);
2799 *site_name = talloc_strdup(mem_ctx, dn);
2800 if (*site_name == NULL) {
2801 return ADS_ERROR(LDAP_NO_MEMORY);
2804 ads_msgfree(ads, res);
2806 return status;
2808 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2813 * find the site dn where a machine resides
2814 * @param ads connection to ads server
2815 * @param mem_ctx Pointer to talloc context
2816 * @param computer_name name of the machine
2817 * @param site_name Pointer to the sitename
2818 * @return status of search
2820 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
2822 ADS_STATUS status;
2823 void *res;
2824 const char *parent, *config_context, *filter;
2825 const char *attrs[] = { "configurationNamingContext", NULL };
2826 char *dn;
2828 /* shortcut a query */
2829 if (strequal(computer_name, ads->config.ldap_server_name)) {
2830 return ads_site_dn(ads, mem_ctx, site_dn);
2833 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2834 if (!ADS_ERR_OK(status)) {
2835 return status;
2838 config_context = ads_pull_string(ads, mem_ctx, res, "configurationNamingContext");
2839 if (config_context == NULL) {
2840 return ADS_ERROR(LDAP_NO_MEMORY);
2843 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
2844 if (filter == NULL) {
2845 return ADS_ERROR(LDAP_NO_MEMORY);
2848 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE, filter, NULL, &res);
2849 if (!ADS_ERR_OK(status)) {
2850 return status;
2853 if (ads_count_replies(ads, res) != 1) {
2854 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2857 dn = ads_get_dn(ads, res);
2858 if (dn == NULL) {
2859 return ADS_ERROR(LDAP_NO_MEMORY);
2862 /* go up three levels */
2863 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
2864 if (parent == NULL) {
2865 ads_memfree(ads, dn);
2866 return ADS_ERROR(LDAP_NO_MEMORY);
2869 *site_dn = talloc_strdup(mem_ctx, parent);
2870 if (*site_dn == NULL) {
2871 ads_memfree(ads, dn);
2872 ADS_ERROR(LDAP_NO_MEMORY);
2875 ads_memfree(ads, dn);
2876 ads_msgfree(ads, res);
2878 return status;
2882 * get the upn suffixes for a domain
2883 * @param ads connection to ads server
2884 * @param mem_ctx Pointer to talloc context
2885 * @param suffixes Pointer to an array of suffixes
2886 * @param site_name Pointer to the number of suffixes
2887 * @return status of search
2889 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char **suffixes, size_t *num_suffixes)
2891 ADS_STATUS status;
2892 void *res;
2893 const char *config_context, *base;
2894 const char *attrs[] = { "configurationNamingContext", NULL };
2895 const char *attrs2[] = { "uPNSuffixes", NULL };
2897 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2898 if (!ADS_ERR_OK(status)) {
2899 return status;
2902 config_context = ads_pull_string(ads, mem_ctx, res, "configurationNamingContext");
2903 if (config_context == NULL) {
2904 return ADS_ERROR(LDAP_NO_MEMORY);
2907 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
2908 if (base == NULL) {
2909 return ADS_ERROR(LDAP_NO_MEMORY);
2912 status = ads_search_dn(ads, &res, base, attrs2);
2913 if (!ADS_ERR_OK(status)) {
2914 return status;
2917 if (ads_count_replies(ads, res) != 1) {
2918 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2921 suffixes = ads_pull_strings(ads, mem_ctx, &res, "uPNSuffixes", num_suffixes);
2922 if (suffixes == NULL) {
2923 ads_msgfree(ads, res);
2924 return ADS_ERROR(LDAP_NO_MEMORY);
2927 ads_msgfree(ads, res);
2929 return status;
2932 #endif