r11830: patch from Rashid N. Achilov <shelton@granch.ru> to add descriptions for...
[Samba.git] / source / libads / ldap.c
blobbf402b3499e27dafc3d74948579eb141e1bf5b07
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
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 #include "includes.h"
25 #ifdef HAVE_LDAP
27 /**
28 * @file ldap.c
29 * @brief basic ldap client-side routines for ads server communications
31 * The routines contained here should do the necessary ldap calls for
32 * ads setups.
34 * Important note: attribute names passed into ads_ routines must
35 * already be in UTF-8 format. We do not convert them because in almost
36 * all cases, they are just ascii (which is represented with the same
37 * codepoints in UTF-8). This may have to change at some point
38 **/
40 static SIG_ATOMIC_T gotalarm;
42 /***************************************************************
43 Signal function to tell us we timed out.
44 ****************************************************************/
46 static void gotalarm_sig(void)
48 gotalarm = 1;
51 LDAP *ldap_open_with_timeout(const char *server, int port, unsigned int to)
53 LDAP *ldp = NULL;
55 /* Setup timeout */
56 gotalarm = 0;
57 CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
58 alarm(to);
59 /* End setup timeout. */
61 ldp = ldap_open(server, port);
63 /* Teardown timeout. */
64 CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
65 alarm(0);
67 return ldp;
70 static int ldap_search_with_timeout(LDAP *ld,
71 LDAP_CONST char *base,
72 int scope,
73 LDAP_CONST char *filter,
74 char **attrs,
75 int attrsonly,
76 LDAPControl **sctrls,
77 LDAPControl **cctrls,
78 int sizelimit,
79 LDAPMessage **res )
81 struct timeval timeout;
82 int result;
84 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
85 timeout.tv_sec = lp_ldap_timeout();
86 timeout.tv_usec = 0;
88 /* Setup alarm timeout.... Do we need both of these ? JRA. */
89 gotalarm = 0;
90 CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
91 alarm(lp_ldap_timeout());
92 /* End setup timeout. */
94 result = ldap_search_ext_s(ld, base, scope, filter, attrs,
95 attrsonly, sctrls, cctrls, &timeout,
96 sizelimit, res);
98 /* Teardown timeout. */
99 CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
100 alarm(0);
102 if (gotalarm != 0)
103 return LDAP_TIMELIMIT_EXCEEDED;
105 return result;
109 try a connection to a given ldap server, returning True and setting the servers IP
110 in the ads struct if successful
112 TODO : add a negative connection cache in here leveraged off of the one
113 found in the rpc code. --jerry
115 BOOL ads_try_connect(ADS_STRUCT *ads, const char *server, unsigned port)
117 char *srv;
119 if (!server || !*server) {
120 return False;
123 DEBUG(5,("ads_try_connect: trying ldap server '%s' port %u\n", server, port));
125 /* this copes with inet_ntoa brokenness */
126 srv = SMB_STRDUP(server);
128 ads->ld = ldap_open_with_timeout(srv, port, lp_ldap_timeout());
129 if (!ads->ld) {
130 free(srv);
131 return False;
133 ads->ldap_port = port;
134 ads->ldap_ip = *interpret_addr2(srv);
135 free(srv);
137 return True;
141 try a connection to a given ldap server, based on URL, returning True if successful
143 static BOOL ads_try_connect_uri(ADS_STRUCT *ads)
145 #if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
146 DEBUG(5,("ads_try_connect: trying ldap server at URI '%s'\n",
147 ads->server.ldap_uri));
150 if (ldap_initialize((LDAP**)&(ads->ld), ads->server.ldap_uri) == LDAP_SUCCESS) {
151 return True;
153 DEBUG(0, ("ldap_initialize: %s\n", strerror(errno)));
155 #else
157 DEBUG(1, ("no URL support in LDAP libs!\n"));
158 #endif
160 return False;
163 /**********************************************************************
164 Try to find an AD dc using our internal name resolution routines
165 Try the realm first and then then workgroup name if netbios is not
166 disabled
167 **********************************************************************/
169 static BOOL ads_find_dc(ADS_STRUCT *ads)
171 const char *c_realm;
172 int count, i=0;
173 struct ip_service *ip_list;
174 pstring realm;
175 BOOL got_realm = False;
176 BOOL use_own_domain = False;
178 /* if the realm and workgroup are both empty, assume they are ours */
180 /* realm */
181 c_realm = ads->server.realm;
183 if ( !c_realm || !*c_realm ) {
184 /* special case where no realm and no workgroup means our own */
185 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
186 use_own_domain = True;
187 c_realm = lp_realm();
191 if (c_realm && *c_realm)
192 got_realm = True;
194 again:
195 /* we need to try once with the realm name and fallback to the
196 netbios domain name if we fail (if netbios has not been disabled */
198 if ( !got_realm && !lp_disable_netbios() ) {
199 c_realm = ads->server.workgroup;
200 if (!c_realm || !*c_realm) {
201 if ( use_own_domain )
202 c_realm = lp_workgroup();
205 if ( !c_realm || !*c_realm ) {
206 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
207 return False;
211 pstrcpy( realm, c_realm );
213 DEBUG(6,("ads_find_dc: looking for %s '%s'\n",
214 (got_realm ? "realm" : "domain"), realm));
216 if ( !get_sorted_dc_list(realm, &ip_list, &count, got_realm) ) {
217 /* fall back to netbios if we can */
218 if ( got_realm && !lp_disable_netbios() ) {
219 got_realm = False;
220 goto again;
223 return False;
226 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
227 for ( i=0; i<count; i++ ) {
228 /* since this is an ads conection request, default to LDAP_PORT is not set */
229 int port = (ip_list[i].port!=PORT_NONE) ? ip_list[i].port : LDAP_PORT;
230 fstring server;
232 fstrcpy( server, inet_ntoa(ip_list[i].ip) );
234 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
235 continue;
237 if ( ads_try_connect(ads, server, port) ) {
238 SAFE_FREE(ip_list);
239 return True;
242 /* keep track of failures */
243 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
246 SAFE_FREE(ip_list);
248 return False;
253 * Connect to the LDAP server
254 * @param ads Pointer to an existing ADS_STRUCT
255 * @return status of connection
257 ADS_STATUS ads_connect(ADS_STRUCT *ads)
259 int version = LDAP_VERSION3;
260 ADS_STATUS status;
262 ads->last_attempt = time(NULL);
263 ads->ld = NULL;
265 /* try with a URL based server */
267 if (ads->server.ldap_uri &&
268 ads_try_connect_uri(ads)) {
269 goto got_connection;
272 /* try with a user specified server */
273 if (ads->server.ldap_server &&
274 ads_try_connect(ads, ads->server.ldap_server, LDAP_PORT)) {
275 goto got_connection;
278 if (ads_find_dc(ads)) {
279 goto got_connection;
282 return ADS_ERROR_SYSTEM(errno?errno:ENOENT);
284 got_connection:
285 DEBUG(3,("Connected to LDAP server %s\n", inet_ntoa(ads->ldap_ip)));
287 status = ads_server_info(ads);
288 if (!ADS_ERR_OK(status)) {
289 DEBUG(1,("Failed to get ldap server info\n"));
290 return status;
293 ldap_set_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
295 if (!ads->auth.user_name) {
296 /* have to use the userPrincipalName value here and
297 not servicePrincipalName; found by Guenther Deschner @ Sernet */
299 asprintf(&ads->auth.user_name, "host/%s", global_myname() );
302 if (!ads->auth.realm) {
303 ads->auth.realm = SMB_STRDUP(ads->config.realm);
306 if (!ads->auth.kdc_server) {
307 ads->auth.kdc_server = SMB_STRDUP(inet_ntoa(ads->ldap_ip));
310 #if KRB5_DNS_HACK
311 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
312 to MIT kerberos to work (tridge) */
314 char *env;
315 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
316 setenv(env, ads->auth.kdc_server, 1);
317 free(env);
319 #endif
321 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
322 return ADS_SUCCESS;
325 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
326 return ADS_ERROR(ldap_simple_bind_s( ads->ld, NULL, NULL));
329 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
330 return ADS_ERROR(ldap_simple_bind_s( ads->ld, ads->auth.user_name, ads->auth.password));
333 return ads_sasl_bind(ads);
337 Duplicate a struct berval into talloc'ed memory
339 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
341 struct berval *value;
343 if (!in_val) return NULL;
345 value = TALLOC_ZERO_P(ctx, struct berval);
346 if (value == NULL)
347 return NULL;
348 if (in_val->bv_len == 0) return value;
350 value->bv_len = in_val->bv_len;
351 value->bv_val = TALLOC_MEMDUP(ctx, in_val->bv_val, in_val->bv_len);
352 return value;
356 Make a values list out of an array of (struct berval *)
358 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
359 const struct berval **in_vals)
361 struct berval **values;
362 int i;
364 if (!in_vals) return NULL;
365 for (i=0; in_vals[i]; i++)
366 ; /* count values */
367 values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
368 if (!values) return NULL;
370 for (i=0; in_vals[i]; i++) {
371 values[i] = dup_berval(ctx, in_vals[i]);
373 return values;
377 UTF8-encode a values list out of an array of (char *)
379 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
381 char **values;
382 int i;
384 if (!in_vals) return NULL;
385 for (i=0; in_vals[i]; i++)
386 ; /* count values */
387 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
388 if (!values) return NULL;
390 for (i=0; in_vals[i]; i++) {
391 push_utf8_talloc(ctx, &values[i], in_vals[i]);
393 return values;
397 Pull a (char *) array out of a UTF8-encoded values list
399 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
401 char **values;
402 int i;
404 if (!in_vals) return NULL;
405 for (i=0; in_vals[i]; i++)
406 ; /* count values */
407 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
408 if (!values) return NULL;
410 for (i=0; in_vals[i]; i++) {
411 pull_utf8_talloc(ctx, &values[i], in_vals[i]);
413 return values;
417 * Do a search with paged results. cookie must be null on the first
418 * call, and then returned on each subsequent call. It will be null
419 * again when the entire search is complete
420 * @param ads connection to ads server
421 * @param bind_path Base dn for the search
422 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
423 * @param expr Search expression - specified in local charset
424 * @param attrs Attributes to retrieve - specified in utf8 or ascii
425 * @param res ** which will contain results - free res* with ads_msgfree()
426 * @param count Number of entries retrieved on this page
427 * @param cookie The paged results cookie to be returned on subsequent calls
428 * @return status of search
430 ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
431 int scope, const char *expr,
432 const char **attrs, void **res,
433 int *count, void **cookie)
435 int rc, i, version;
436 char *utf8_expr, *utf8_path, **search_attrs;
437 LDAPControl PagedResults, NoReferrals, *controls[3], **rcontrols;
438 BerElement *cookie_be = NULL;
439 struct berval *cookie_bv= NULL;
440 TALLOC_CTX *ctx;
442 *res = NULL;
444 if (!(ctx = talloc_init("ads_do_paged_search")))
445 return ADS_ERROR(LDAP_NO_MEMORY);
447 /* 0 means the conversion worked but the result was empty
448 so we only fail if it's -1. In any case, it always
449 at least nulls out the dest */
450 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
451 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
452 rc = LDAP_NO_MEMORY;
453 goto done;
456 if (!attrs || !(*attrs))
457 search_attrs = NULL;
458 else {
459 /* This would be the utf8-encoded version...*/
460 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
461 if (!(str_list_copy(&search_attrs, attrs))) {
462 rc = LDAP_NO_MEMORY;
463 goto done;
468 /* Paged results only available on ldap v3 or later */
469 ldap_get_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
470 if (version < LDAP_VERSION3) {
471 rc = LDAP_NOT_SUPPORTED;
472 goto done;
475 cookie_be = ber_alloc_t(LBER_USE_DER);
476 if (cookie && *cookie) {
477 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
478 ber_bvfree(*cookie); /* don't need it from last time */
479 *cookie = NULL;
480 } else {
481 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
483 ber_flatten(cookie_be, &cookie_bv);
484 PagedResults.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID);
485 PagedResults.ldctl_iscritical = (char) 1;
486 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
487 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
489 NoReferrals.ldctl_oid = CONST_DISCARD(char *, ADS_NO_REFERRALS_OID);
490 NoReferrals.ldctl_iscritical = (char) 0;
491 NoReferrals.ldctl_value.bv_len = 0;
492 NoReferrals.ldctl_value.bv_val = CONST_DISCARD(char *, "");
495 controls[0] = &NoReferrals;
496 controls[1] = &PagedResults;
497 controls[2] = NULL;
499 /* we need to disable referrals as the openldap libs don't
500 handle them and paged results at the same time. Using them
501 together results in the result record containing the server
502 page control being removed from the result list (tridge/jmcd)
504 leaving this in despite the control that says don't generate
505 referrals, in case the server doesn't support it (jmcd)
507 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
509 rc = ldap_search_with_timeout(ads->ld, utf8_path, scope, utf8_expr,
510 search_attrs, 0, controls,
511 NULL, LDAP_NO_LIMIT,
512 (LDAPMessage **)res);
514 ber_free(cookie_be, 1);
515 ber_bvfree(cookie_bv);
517 if (rc) {
518 DEBUG(3,("ads_do_paged_search: ldap_search_with_timeout(%s) -> %s\n", expr,
519 ldap_err2string(rc)));
520 goto done;
523 rc = ldap_parse_result(ads->ld, *res, NULL, NULL, NULL,
524 NULL, &rcontrols, 0);
526 if (!rcontrols) {
527 goto done;
530 for (i=0; rcontrols[i]; i++) {
531 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
532 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
533 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
534 &cookie_bv);
535 /* the berval is the cookie, but must be freed when
536 it is all done */
537 if (cookie_bv->bv_len) /* still more to do */
538 *cookie=ber_bvdup(cookie_bv);
539 else
540 *cookie=NULL;
541 ber_bvfree(cookie_bv);
542 ber_free(cookie_be, 1);
543 break;
546 ldap_controls_free(rcontrols);
548 done:
549 talloc_destroy(ctx);
550 /* if/when we decide to utf8-encode attrs, take out this next line */
551 str_list_free(&search_attrs);
553 return ADS_ERROR(rc);
558 * Get all results for a search. This uses ads_do_paged_search() to return
559 * all entries in a large search.
560 * @param ads connection to ads server
561 * @param bind_path Base dn for the search
562 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
563 * @param expr Search expression
564 * @param attrs Attributes to retrieve
565 * @param res ** which will contain results - free res* with ads_msgfree()
566 * @return status of search
568 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
569 int scope, const char *expr,
570 const char **attrs, void **res)
572 void *cookie = NULL;
573 int count = 0;
574 ADS_STATUS status;
576 *res = NULL;
577 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, res,
578 &count, &cookie);
580 if (!ADS_ERR_OK(status)) return status;
582 while (cookie) {
583 void *res2 = NULL;
584 ADS_STATUS status2;
585 LDAPMessage *msg, *next;
587 status2 = ads_do_paged_search(ads, bind_path, scope, expr,
588 attrs, &res2, &count, &cookie);
590 if (!ADS_ERR_OK(status2)) break;
592 /* this relies on the way that ldap_add_result_entry() works internally. I hope
593 that this works on all ldap libs, but I have only tested with openldap */
594 for (msg = ads_first_entry(ads, res2); msg; msg = next) {
595 next = ads_next_entry(ads, msg);
596 ldap_add_result_entry((LDAPMessage **)res, msg);
598 /* note that we do not free res2, as the memory is now
599 part of the main returned list */
602 return status;
606 * Run a function on all results for a search. Uses ads_do_paged_search() and
607 * runs the function as each page is returned, using ads_process_results()
608 * @param ads connection to ads server
609 * @param bind_path Base dn for the search
610 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
611 * @param expr Search expression - specified in local charset
612 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
613 * @param fn Function which takes attr name, values list, and data_area
614 * @param data_area Pointer which is passed to function on each call
615 * @return status of search
617 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
618 int scope, const char *expr, const char **attrs,
619 BOOL(*fn)(char *, void **, void *),
620 void *data_area)
622 void *cookie = NULL;
623 int count = 0;
624 ADS_STATUS status;
625 void *res;
627 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
628 &count, &cookie);
630 if (!ADS_ERR_OK(status)) return status;
632 ads_process_results(ads, res, fn, data_area);
633 ads_msgfree(ads, res);
635 while (cookie) {
636 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
637 &res, &count, &cookie);
639 if (!ADS_ERR_OK(status)) break;
641 ads_process_results(ads, res, fn, data_area);
642 ads_msgfree(ads, res);
645 return status;
649 * Do a search with a timeout.
650 * @param ads connection to ads server
651 * @param bind_path Base dn for the search
652 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
653 * @param expr Search expression
654 * @param attrs Attributes to retrieve
655 * @param res ** which will contain results - free res* with ads_msgfree()
656 * @return status of search
658 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
659 const char *expr,
660 const char **attrs, void **res)
662 int rc;
663 char *utf8_expr, *utf8_path, **search_attrs = NULL;
664 TALLOC_CTX *ctx;
666 *res = NULL;
667 if (!(ctx = talloc_init("ads_do_search"))) {
668 DEBUG(1,("ads_do_search: talloc_init() failed!"));
669 return ADS_ERROR(LDAP_NO_MEMORY);
672 /* 0 means the conversion worked but the result was empty
673 so we only fail if it's negative. In any case, it always
674 at least nulls out the dest */
675 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
676 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
677 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
678 rc = LDAP_NO_MEMORY;
679 goto done;
682 if (!attrs || !(*attrs))
683 search_attrs = NULL;
684 else {
685 /* This would be the utf8-encoded version...*/
686 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
687 if (!(str_list_copy(&search_attrs, attrs)))
689 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
690 rc = LDAP_NO_MEMORY;
691 goto done;
695 /* see the note in ads_do_paged_search - we *must* disable referrals */
696 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
698 rc = ldap_search_with_timeout(ads->ld, utf8_path, scope, utf8_expr,
699 search_attrs, 0, NULL, NULL,
700 LDAP_NO_LIMIT,
701 (LDAPMessage **)res);
703 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
704 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
705 rc = 0;
708 done:
709 talloc_destroy(ctx);
710 /* if/when we decide to utf8-encode attrs, take out this next line */
711 str_list_free(&search_attrs);
712 return ADS_ERROR(rc);
715 * Do a general ADS search
716 * @param ads connection to ads server
717 * @param res ** which will contain results - free res* with ads_msgfree()
718 * @param expr Search expression
719 * @param attrs Attributes to retrieve
720 * @return status of search
722 ADS_STATUS ads_search(ADS_STRUCT *ads, void **res,
723 const char *expr,
724 const char **attrs)
726 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
727 expr, attrs, res);
731 * Do a search on a specific DistinguishedName
732 * @param ads connection to ads server
733 * @param res ** which will contain results - free res* with ads_msgfree()
734 * @param dn DistinguishName to search
735 * @param attrs Attributes to retrieve
736 * @return status of search
738 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, void **res,
739 const char *dn,
740 const char **attrs)
742 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)", attrs, res);
746 * Free up memory from a ads_search
747 * @param ads connection to ads server
748 * @param msg Search results to free
750 void ads_msgfree(ADS_STRUCT *ads, void *msg)
752 if (!msg) return;
753 ldap_msgfree(msg);
757 * Free up memory from various ads requests
758 * @param ads connection to ads server
759 * @param mem Area to free
761 void ads_memfree(ADS_STRUCT *ads, void *mem)
763 SAFE_FREE(mem);
767 * Get a dn from search results
768 * @param ads connection to ads server
769 * @param msg Search result
770 * @return dn string
772 char *ads_get_dn(ADS_STRUCT *ads, void *msg)
774 char *utf8_dn, *unix_dn;
776 utf8_dn = ldap_get_dn(ads->ld, msg);
778 if (!utf8_dn) {
779 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
780 return NULL;
783 if (pull_utf8_allocate(&unix_dn, utf8_dn) == (size_t)-1) {
784 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
785 utf8_dn ));
786 return NULL;
788 ldap_memfree(utf8_dn);
789 return unix_dn;
793 * Find a machine account given a hostname
794 * @param ads connection to ads server
795 * @param res ** which will contain results - free res* with ads_msgfree()
796 * @param host Hostname to search for
797 * @return status of search
799 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, void **res, const char *machine)
801 ADS_STATUS status;
802 char *expr;
803 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
805 *res = NULL;
807 /* the easiest way to find a machine account anywhere in the tree
808 is to look for hostname$ */
809 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
810 DEBUG(1, ("asprintf failed!\n"));
811 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
814 status = ads_search(ads, res, expr, attrs);
815 SAFE_FREE(expr);
816 return status;
820 * Initialize a list of mods to be used in a modify request
821 * @param ctx An initialized TALLOC_CTX
822 * @return allocated ADS_MODLIST
824 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
826 #define ADS_MODLIST_ALLOC_SIZE 10
827 LDAPMod **mods;
829 if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
830 /* -1 is safety to make sure we don't go over the end.
831 need to reset it to NULL before doing ldap modify */
832 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
834 return (ADS_MODLIST)mods;
839 add an attribute to the list, with values list already constructed
841 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
842 int mod_op, const char *name,
843 const void **invals)
845 int curmod;
846 LDAPMod **modlist = (LDAPMod **) *mods;
847 struct berval **ber_values = NULL;
848 char **char_values = NULL;
850 if (!invals) {
851 mod_op = LDAP_MOD_DELETE;
852 } else {
853 if (mod_op & LDAP_MOD_BVALUES)
854 ber_values = ads_dup_values(ctx,
855 (const struct berval **)invals);
856 else
857 char_values = ads_push_strvals(ctx,
858 (const char **) invals);
861 /* find the first empty slot */
862 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
863 curmod++);
864 if (modlist[curmod] == (LDAPMod *) -1) {
865 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
866 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
867 return ADS_ERROR(LDAP_NO_MEMORY);
868 memset(&modlist[curmod], 0,
869 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
870 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
871 *mods = (ADS_MODLIST)modlist;
874 if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
875 return ADS_ERROR(LDAP_NO_MEMORY);
876 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
877 if (mod_op & LDAP_MOD_BVALUES) {
878 modlist[curmod]->mod_bvalues = ber_values;
879 } else if (mod_op & LDAP_MOD_DELETE) {
880 modlist[curmod]->mod_values = NULL;
881 } else {
882 modlist[curmod]->mod_values = char_values;
885 modlist[curmod]->mod_op = mod_op;
886 return ADS_ERROR(LDAP_SUCCESS);
890 * Add a single string value to a mod list
891 * @param ctx An initialized TALLOC_CTX
892 * @param mods An initialized ADS_MODLIST
893 * @param name The attribute name to add
894 * @param val The value to add - NULL means DELETE
895 * @return ADS STATUS indicating success of add
897 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
898 const char *name, const char *val)
900 const char *values[2];
902 values[0] = val;
903 values[1] = NULL;
905 if (!val)
906 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
907 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name,
908 (const void **) values);
912 * Add an array of string values to a mod list
913 * @param ctx An initialized TALLOC_CTX
914 * @param mods An initialized ADS_MODLIST
915 * @param name The attribute name to add
916 * @param vals The array of string values to add - NULL means DELETE
917 * @return ADS STATUS indicating success of add
919 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
920 const char *name, const char **vals)
922 if (!vals)
923 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
924 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
925 name, (const void **) vals);
929 * Add a single ber-encoded value to a mod list
930 * @param ctx An initialized TALLOC_CTX
931 * @param mods An initialized ADS_MODLIST
932 * @param name The attribute name to add
933 * @param val The value to add - NULL means DELETE
934 * @return ADS STATUS indicating success of add
936 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
937 const char *name, const struct berval *val)
939 const struct berval *values[2];
941 values[0] = val;
942 values[1] = NULL;
943 if (!val)
944 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
945 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
946 name, (const void **) values);
950 * Perform an ldap modify
951 * @param ads connection to ads server
952 * @param mod_dn DistinguishedName to modify
953 * @param mods list of modifications to perform
954 * @return status of modify
956 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
958 int ret,i;
959 char *utf8_dn = NULL;
961 this control is needed to modify that contains a currently
962 non-existent attribute (but allowable for the object) to run
964 LDAPControl PermitModify = {
965 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
966 {0, NULL},
967 (char) 1};
968 LDAPControl *controls[2];
970 controls[0] = &PermitModify;
971 controls[1] = NULL;
973 if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) {
974 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
977 /* find the end of the list, marked by NULL or -1 */
978 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
979 /* make sure the end of the list is NULL */
980 mods[i] = NULL;
981 ret = ldap_modify_ext_s(ads->ld, utf8_dn,
982 (LDAPMod **) mods, controls, NULL);
983 SAFE_FREE(utf8_dn);
984 return ADS_ERROR(ret);
988 * Perform an ldap add
989 * @param ads connection to ads server
990 * @param new_dn DistinguishedName to add
991 * @param mods list of attributes and values for DN
992 * @return status of add
994 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
996 int ret, i;
997 char *utf8_dn = NULL;
999 if (push_utf8_allocate(&utf8_dn, new_dn) == -1) {
1000 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1001 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1004 /* find the end of the list, marked by NULL or -1 */
1005 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1006 /* make sure the end of the list is NULL */
1007 mods[i] = NULL;
1009 ret = ldap_add_s(ads->ld, utf8_dn, (LDAPMod**)mods);
1010 SAFE_FREE(utf8_dn);
1011 return ADS_ERROR(ret);
1015 * Delete a DistinguishedName
1016 * @param ads connection to ads server
1017 * @param new_dn DistinguishedName to delete
1018 * @return status of delete
1020 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1022 int ret;
1023 char *utf8_dn = NULL;
1024 if (push_utf8_allocate(&utf8_dn, del_dn) == -1) {
1025 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1026 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1029 ret = ldap_delete_s(ads->ld, utf8_dn);
1030 return ADS_ERROR(ret);
1034 * Build an org unit string
1035 * if org unit is Computers or blank then assume a container, otherwise
1036 * assume a \ separated list of organisational units
1037 * @param ads connection to ads server
1038 * @param org_unit Organizational unit
1039 * @return org unit string - caller must free
1041 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1043 char *ret = NULL;
1045 if (!org_unit || !*org_unit) {
1047 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1049 /* samba4 might not yet respond to a wellknownobject-query */
1050 return ret ? ret : SMB_STRDUP("cn=Computers");
1053 if (strequal(org_unit, "Computers")) {
1054 return SMB_STRDUP("cn=Computers");
1057 return ads_build_path(org_unit, "\\/", "ou=", 1);
1061 * Get a org unit string for a well-known GUID
1062 * @param ads connection to ads server
1063 * @param wknguid Well known GUID
1064 * @return org unit string - caller must free
1066 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1068 ADS_STATUS status;
1069 void *res;
1070 char *base, *wkn_dn, *ret, **wkn_dn_exp, **bind_dn_exp;
1071 const char *attrs[] = {"distinguishedName", NULL};
1072 int new_ln, wkn_ln, bind_ln, i;
1074 if (wknguid == NULL) {
1075 return NULL;
1078 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1079 DEBUG(1, ("asprintf failed!\n"));
1080 return NULL;
1083 status = ads_search_dn(ads, &res, base, attrs);
1084 if (!ADS_ERR_OK(status)) {
1085 DEBUG(1,("Failed while searching for: %s\n", base));
1086 return NULL;
1088 free(base);
1090 if (ads_count_replies(ads, res) != 1) {
1091 return NULL;
1094 /* substitute the bind-path from the well-known-guid-search result */
1095 wkn_dn = ads_get_dn(ads, res);
1096 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1097 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1099 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1101 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1104 new_ln = wkn_ln - bind_ln;
1106 ret = wkn_dn_exp[0];
1108 for (i=1; i < new_ln; i++) {
1109 char *s;
1110 asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]);
1111 ret = SMB_STRDUP(s);
1112 free(s);
1115 return ret;
1119 * Adds (appends) an item to an attribute array, rather then
1120 * replacing the whole list
1121 * @param ctx An initialized TALLOC_CTX
1122 * @param mods An initialized ADS_MODLIST
1123 * @param name name of the ldap attribute to append to
1124 * @param vals an array of values to add
1125 * @return status of addition
1128 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1129 const char *name, const char **vals)
1131 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name, (const void **) vals);
1135 * Determines the computer account's current KVNO via an LDAP lookup
1136 * @param ads An initialized ADS_STRUCT
1137 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1138 * @return the kvno for the computer account, or -1 in case of a failure.
1141 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *machine_name)
1143 LDAPMessage *res = NULL;
1144 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1145 char *filter;
1146 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1147 char *dn_string = NULL;
1148 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1150 DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name));
1151 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1152 return kvno;
1154 ret = ads_search(ads, (void**) &res, filter, attrs);
1155 SAFE_FREE(filter);
1156 if (!ADS_ERR_OK(ret) && ads_count_replies(ads, res)) {
1157 DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name));
1158 ads_msgfree(ads, res);
1159 return kvno;
1162 dn_string = ads_get_dn(ads, res);
1163 if (!dn_string) {
1164 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1165 ads_msgfree(ads, res);
1166 return kvno;
1168 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1169 ads_memfree(ads, dn_string);
1171 /* ---------------------------------------------------------
1172 * 0 is returned as a default KVNO from this point on...
1173 * This is done because Windows 2000 does not support key
1174 * version numbers. Chances are that a failure in the next
1175 * step is simply due to Windows 2000 being used for a
1176 * domain controller. */
1177 kvno = 0;
1179 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1180 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1181 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1182 ads_msgfree(ads, res);
1183 return kvno;
1186 /* Success */
1187 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1188 ads_msgfree(ads, res);
1189 return kvno;
1193 * This clears out all registered spn's for a given hostname
1194 * @param ads An initilaized ADS_STRUCT
1195 * @param machine_name the NetBIOS name of the computer.
1196 * @return 0 upon success, non-zero otherwise.
1199 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1201 TALLOC_CTX *ctx;
1202 LDAPMessage *res = NULL;
1203 ADS_MODLIST mods;
1204 const char *servicePrincipalName[1] = {NULL};
1205 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1206 char *dn_string = NULL;
1208 ret = ads_find_machine_acct(ads, (void **)&res, machine_name);
1209 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1210 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1211 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1212 ads_msgfree(ads, res);
1213 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1216 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1217 ctx = talloc_init("ads_clear_service_principal_names");
1218 if (!ctx) {
1219 ads_msgfree(ads, res);
1220 return ADS_ERROR(LDAP_NO_MEMORY);
1223 if (!(mods = ads_init_mods(ctx))) {
1224 talloc_destroy(ctx);
1225 ads_msgfree(ads, res);
1226 return ADS_ERROR(LDAP_NO_MEMORY);
1228 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1229 if (!ADS_ERR_OK(ret)) {
1230 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1231 ads_msgfree(ads, res);
1232 talloc_destroy(ctx);
1233 return ret;
1235 dn_string = ads_get_dn(ads, res);
1236 if (!dn_string) {
1237 talloc_destroy(ctx);
1238 ads_msgfree(ads, res);
1239 return ADS_ERROR(LDAP_NO_MEMORY);
1241 ret = ads_gen_mod(ads, dn_string, mods);
1242 ads_memfree(ads,dn_string);
1243 if (!ADS_ERR_OK(ret)) {
1244 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1245 machine_name));
1246 ads_msgfree(ads, res);
1247 talloc_destroy(ctx);
1248 return ret;
1251 ads_msgfree(ads, res);
1252 talloc_destroy(ctx);
1253 return ret;
1257 * This adds a service principal name to an existing computer account
1258 * (found by hostname) in AD.
1259 * @param ads An initialized ADS_STRUCT
1260 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1261 * @param spn A string of the service principal to add, i.e. 'host'
1262 * @return 0 upon sucess, or non-zero if a failure occurs
1265 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name, const char *spn)
1267 ADS_STATUS ret;
1268 TALLOC_CTX *ctx;
1269 LDAPMessage *res = NULL;
1270 char *host_spn, *psp1, *psp2, *psp3;
1271 ADS_MODLIST mods;
1272 fstring my_fqdn;
1273 char *dn_string = NULL;
1274 const char *servicePrincipalName[4] = {NULL, NULL, NULL, NULL};
1276 ret = ads_find_machine_acct(ads, (void **)&res, machine_name);
1277 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1278 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1279 machine_name));
1280 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1281 spn, machine_name, ads->config.realm));
1282 ads_msgfree(ads, res);
1283 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1286 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1287 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1288 ads_msgfree(ads, res);
1289 return ADS_ERROR(LDAP_NO_MEMORY);
1292 name_to_fqdn(my_fqdn, machine_name);
1293 strlower_m(my_fqdn);
1295 if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", my_fqdn))) {
1296 talloc_destroy(ctx);
1297 ads_msgfree(ads, res);
1298 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1301 /* Add the extra principal */
1302 psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name);
1303 strupper_m(psp1);
1304 strlower_m(&psp1[strlen(spn)]);
1305 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp1, machine_name));
1306 servicePrincipalName[0] = psp1;
1307 psp2 = talloc_asprintf(ctx, "%s/%s.%s", spn, machine_name, ads->config.realm);
1308 strupper_m(psp2);
1309 strlower_m(&psp2[strlen(spn)]);
1310 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp2, machine_name));
1311 servicePrincipalName[1] = psp2;
1313 /* Add another principal in case the realm != the DNS domain, so that
1314 * the KDC doesn't send "server principal unknown" errors to clients
1315 * which use the DNS name in determining service principal names. */
1316 psp3 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn);
1317 strupper_m(psp3);
1318 strlower_m(&psp3[strlen(spn)]);
1319 if (strcmp(psp2, psp3) != 0) {
1320 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp3, machine_name));
1321 servicePrincipalName[2] = psp3;
1324 if (!(mods = ads_init_mods(ctx))) {
1325 talloc_destroy(ctx);
1326 ads_msgfree(ads, res);
1327 return ADS_ERROR(LDAP_NO_MEMORY);
1329 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1330 if (!ADS_ERR_OK(ret)) {
1331 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1332 talloc_destroy(ctx);
1333 ads_msgfree(ads, res);
1334 return ret;
1336 dn_string = ads_get_dn(ads, res);
1337 if (!dn_string) {
1338 talloc_destroy(ctx);
1339 ads_msgfree(ads, res);
1340 return ADS_ERROR(LDAP_NO_MEMORY);
1342 ret = ads_gen_mod(ads, dn_string, mods);
1343 ads_memfree(ads,dn_string);
1344 if (!ADS_ERR_OK(ret)) {
1345 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1346 talloc_destroy(ctx);
1347 ads_msgfree(ads, res);
1348 return ret;
1351 talloc_destroy(ctx);
1352 ads_msgfree(ads, res);
1353 return ret;
1357 * adds a machine account to the ADS server
1358 * @param ads An intialized ADS_STRUCT
1359 * @param machine_name - the NetBIOS machine name of this account.
1360 * @param account_type A number indicating the type of account to create
1361 * @param org_unit The LDAP path in which to place this account
1362 * @return 0 upon success, or non-zero otherwise
1365 static ADS_STATUS ads_add_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1366 uint32 account_type,
1367 const char *org_unit)
1369 ADS_STATUS ret, status;
1370 char *host_spn, *host_upn, *new_dn, *samAccountName, *controlstr;
1371 TALLOC_CTX *ctx;
1372 ADS_MODLIST mods;
1373 const char *objectClass[] = {"top", "person", "organizationalPerson",
1374 "user", "computer", NULL};
1375 const char *servicePrincipalName[7] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
1376 char *psp, *psp2, *psp3, *psp4;
1377 unsigned acct_control;
1378 unsigned exists=0;
1379 fstring my_fqdn;
1380 LDAPMessage *res = NULL;
1381 int i, next_spn;
1383 if (!(ctx = talloc_init("ads_add_machine_acct")))
1384 return ADS_ERROR(LDAP_NO_MEMORY);
1386 ret = ADS_ERROR(LDAP_NO_MEMORY);
1388 name_to_fqdn(my_fqdn, machine_name);
1390 status = ads_find_machine_acct(ads, (void **)&res, machine_name);
1391 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1392 char *dn_string = ads_get_dn(ads, res);
1393 if (!dn_string) {
1394 DEBUG(1, ("ads_add_machine_acct: ads_get_dn returned NULL (malloc failure?)\n"));
1395 goto done;
1397 new_dn = talloc_strdup(ctx, dn_string);
1398 ads_memfree(ads,dn_string);
1399 DEBUG(0, ("ads_add_machine_acct: Host account for %s already exists - modifying old account\n",
1400 machine_name));
1401 exists=1;
1402 } else {
1403 char *ou_str = ads_ou_string(ads,org_unit);
1404 if (!ou_str) {
1405 DEBUG(1, ("ads_add_machine_acct: ads_ou_string returned NULL (malloc failure?)\n"));
1406 goto done;
1408 new_dn = talloc_asprintf(ctx, "cn=%s,%s,%s", machine_name, ou_str,
1409 ads->config.bind_path);
1411 SAFE_FREE(ou_str);
1414 if (!new_dn) {
1415 goto done;
1418 if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", machine_name)))
1419 goto done;
1420 if (!(host_upn = talloc_asprintf(ctx, "%s@%s", host_spn, ads->config.realm)))
1421 goto done;
1422 servicePrincipalName[0] = talloc_asprintf(ctx, "HOST/%s", machine_name);
1423 psp = talloc_asprintf(ctx, "HOST/%s.%s",
1424 machine_name,
1425 ads->config.realm);
1426 strlower_m(&psp[5]);
1427 servicePrincipalName[1] = psp;
1428 servicePrincipalName[2] = talloc_asprintf(ctx, "CIFS/%s", machine_name);
1429 psp2 = talloc_asprintf(ctx, "CIFS/%s.%s",
1430 machine_name,
1431 ads->config.realm);
1432 strlower_m(&psp2[5]);
1433 servicePrincipalName[3] = psp2;
1435 /* Ensure servicePrincipalName[4] and [5] are unique. */
1436 strlower_m(my_fqdn);
1437 psp3 = talloc_asprintf(ctx, "CIFS/%s", my_fqdn);
1438 strlower_m(&psp3[5]);
1440 next_spn = 4;
1441 for (i = 0; i < next_spn; i++) {
1442 if (strequal(servicePrincipalName[i], psp3))
1443 break;
1445 if (i == next_spn) {
1446 servicePrincipalName[next_spn++] = psp3;
1449 psp4 = talloc_asprintf(ctx, "HOST/%s", my_fqdn);
1450 strlower_m(&psp4[5]);
1451 for (i = 0; i < next_spn; i++) {
1452 if (strequal(servicePrincipalName[i], psp4))
1453 break;
1455 if (i == next_spn) {
1456 servicePrincipalName[next_spn++] = psp4;
1459 if (!(samAccountName = talloc_asprintf(ctx, "%s$", machine_name))) {
1460 goto done;
1463 acct_control = account_type | UF_DONT_EXPIRE_PASSWD;
1464 #ifndef ENCTYPE_ARCFOUR_HMAC
1465 acct_control |= UF_USE_DES_KEY_ONLY;
1466 #endif
1468 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
1469 goto done;
1472 if (!(mods = ads_init_mods(ctx))) {
1473 goto done;
1476 if (!exists) {
1477 ads_mod_str(ctx, &mods, "cn", machine_name);
1478 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1479 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1480 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1482 ads_mod_str(ctx, &mods, "dNSHostName", my_fqdn);
1483 ads_mod_str(ctx, &mods, "userPrincipalName", host_upn);
1484 ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1485 ads_mod_str(ctx, &mods, "operatingSystem", "Samba");
1486 ads_mod_str(ctx, &mods, "operatingSystemVersion", SAMBA_VERSION_STRING);
1488 if (!exists) {
1489 ret = ads_gen_add(ads, new_dn, mods);
1490 } else {
1491 ret = ads_gen_mod(ads, new_dn, mods);
1494 if (!ADS_ERR_OK(ret)) {
1495 goto done;
1498 /* Do not fail if we can't set security descriptor
1499 * it shouldn't be mandatory and probably we just
1500 * don't have enough rights to do it.
1502 if (!exists) {
1503 status = ads_set_machine_sd(ads, machine_name, new_dn);
1505 if (!ADS_ERR_OK(status)) {
1506 DEBUG(0, ("Warning: ads_set_machine_sd: %s\n",
1507 ads_errstr(status)));
1510 done:
1511 ads_msgfree(ads, res);
1512 talloc_destroy(ctx);
1513 return ret;
1517 dump a binary result from ldap
1519 static void dump_binary(const char *field, struct berval **values)
1521 int i, j;
1522 for (i=0; values[i]; i++) {
1523 printf("%s: ", field);
1524 for (j=0; j<values[i]->bv_len; j++) {
1525 printf("%02X", (unsigned char)values[i]->bv_val[j]);
1527 printf("\n");
1531 static void dump_guid(const char *field, struct berval **values)
1533 int i;
1534 UUID_FLAT guid;
1535 for (i=0; values[i]; i++) {
1536 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1537 printf("%s: %s\n", field,
1538 smb_uuid_string_static(smb_uuid_unpack_static(guid)));
1543 dump a sid result from ldap
1545 static void dump_sid(const char *field, struct berval **values)
1547 int i;
1548 for (i=0; values[i]; i++) {
1549 DOM_SID sid;
1550 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1551 printf("%s: %s\n", field, sid_string_static(&sid));
1556 dump ntSecurityDescriptor
1558 static void dump_sd(const char *filed, struct berval **values)
1560 prs_struct ps;
1562 SEC_DESC *psd = 0;
1563 TALLOC_CTX *ctx = 0;
1565 if (!(ctx = talloc_init("sec_io_desc")))
1566 return;
1568 /* prepare data */
1569 prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
1570 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
1571 prs_set_offset(&ps,0);
1573 /* parse secdesc */
1574 if (!sec_io_desc("sd", &psd, &ps, 1)) {
1575 prs_mem_free(&ps);
1576 talloc_destroy(ctx);
1577 return;
1579 if (psd) ads_disp_sd(psd);
1581 prs_mem_free(&ps);
1582 talloc_destroy(ctx);
1586 dump a string result from ldap
1588 static void dump_string(const char *field, char **values)
1590 int i;
1591 for (i=0; values[i]; i++) {
1592 printf("%s: %s\n", field, values[i]);
1597 dump a field from LDAP on stdout
1598 used for debugging
1601 static BOOL ads_dump_field(char *field, void **values, void *data_area)
1603 const struct {
1604 const char *name;
1605 BOOL string;
1606 void (*handler)(const char *, struct berval **);
1607 } handlers[] = {
1608 {"objectGUID", False, dump_guid},
1609 {"nTSecurityDescriptor", False, dump_sd},
1610 {"dnsRecord", False, dump_binary},
1611 {"objectSid", False, dump_sid},
1612 {"tokenGroups", False, dump_sid},
1613 {NULL, True, NULL}
1615 int i;
1617 if (!field) { /* must be end of an entry */
1618 printf("\n");
1619 return False;
1622 for (i=0; handlers[i].name; i++) {
1623 if (StrCaseCmp(handlers[i].name, field) == 0) {
1624 if (!values) /* first time, indicate string or not */
1625 return handlers[i].string;
1626 handlers[i].handler(field, (struct berval **) values);
1627 break;
1630 if (!handlers[i].name) {
1631 if (!values) /* first time, indicate string conversion */
1632 return True;
1633 dump_string(field, (char **)values);
1635 return False;
1639 * Dump a result from LDAP on stdout
1640 * used for debugging
1641 * @param ads connection to ads server
1642 * @param res Results to dump
1645 void ads_dump(ADS_STRUCT *ads, void *res)
1647 ads_process_results(ads, res, ads_dump_field, NULL);
1651 * Walk through results, calling a function for each entry found.
1652 * The function receives a field name, a berval * array of values,
1653 * and a data area passed through from the start. The function is
1654 * called once with null for field and values at the end of each
1655 * entry.
1656 * @param ads connection to ads server
1657 * @param res Results to process
1658 * @param fn Function for processing each result
1659 * @param data_area user-defined area to pass to function
1661 void ads_process_results(ADS_STRUCT *ads, void *res,
1662 BOOL(*fn)(char *, void **, void *),
1663 void *data_area)
1665 void *msg;
1666 TALLOC_CTX *ctx;
1668 if (!(ctx = talloc_init("ads_process_results")))
1669 return;
1671 for (msg = ads_first_entry(ads, res); msg;
1672 msg = ads_next_entry(ads, msg)) {
1673 char *utf8_field;
1674 BerElement *b;
1676 for (utf8_field=ldap_first_attribute(ads->ld,
1677 (LDAPMessage *)msg,&b);
1678 utf8_field;
1679 utf8_field=ldap_next_attribute(ads->ld,
1680 (LDAPMessage *)msg,b)) {
1681 struct berval **ber_vals;
1682 char **str_vals, **utf8_vals;
1683 char *field;
1684 BOOL string;
1686 pull_utf8_talloc(ctx, &field, utf8_field);
1687 string = fn(field, NULL, data_area);
1689 if (string) {
1690 utf8_vals = ldap_get_values(ads->ld,
1691 (LDAPMessage *)msg, field);
1692 str_vals = ads_pull_strvals(ctx,
1693 (const char **) utf8_vals);
1694 fn(field, (void **) str_vals, data_area);
1695 ldap_value_free(utf8_vals);
1696 } else {
1697 ber_vals = ldap_get_values_len(ads->ld,
1698 (LDAPMessage *)msg, field);
1699 fn(field, (void **) ber_vals, data_area);
1701 ldap_value_free_len(ber_vals);
1703 ldap_memfree(utf8_field);
1705 ber_free(b, 0);
1706 talloc_free_children(ctx);
1707 fn(NULL, NULL, data_area); /* completed an entry */
1710 talloc_destroy(ctx);
1714 * count how many replies are in a LDAPMessage
1715 * @param ads connection to ads server
1716 * @param res Results to count
1717 * @return number of replies
1719 int ads_count_replies(ADS_STRUCT *ads, void *res)
1721 return ldap_count_entries(ads->ld, (LDAPMessage *)res);
1725 * Join a machine to a realm
1726 * Creates the machine account and sets the machine password
1727 * @param ads connection to ads server
1728 * @param machine name of host to add
1729 * @param org_unit Organizational unit to place machine in
1730 * @return status of join
1732 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
1733 uint32 account_type, const char *org_unit)
1735 ADS_STATUS status;
1736 LDAPMessage *res = NULL;
1737 char *machine;
1739 /* machine name must be lowercase */
1740 machine = SMB_STRDUP(machine_name);
1741 strlower_m(machine);
1744 status = ads_find_machine_acct(ads, (void **)&res, machine);
1745 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1746 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
1747 status = ads_leave_realm(ads, machine);
1748 if (!ADS_ERR_OK(status)) {
1749 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
1750 machine, ads->config.realm));
1751 return status;
1756 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
1757 if (!ADS_ERR_OK(status)) {
1758 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
1759 SAFE_FREE(machine);
1760 return status;
1763 status = ads_find_machine_acct(ads, (void **)&res, machine);
1764 if (!ADS_ERR_OK(status)) {
1765 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
1766 SAFE_FREE(machine);
1767 return status;
1770 SAFE_FREE(machine);
1771 ads_msgfree(ads, res);
1773 return status;
1777 * Delete a machine from the realm
1778 * @param ads connection to ads server
1779 * @param hostname Machine to remove
1780 * @return status of delete
1782 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
1784 ADS_STATUS status;
1785 void *res, *msg;
1786 char *hostnameDN, *host;
1787 int rc;
1789 /* hostname must be lowercase */
1790 host = SMB_STRDUP(hostname);
1791 strlower_m(host);
1793 status = ads_find_machine_acct(ads, &res, host);
1794 if (!ADS_ERR_OK(status)) {
1795 DEBUG(0, ("Host account for %s does not exist.\n", host));
1796 return status;
1799 msg = ads_first_entry(ads, res);
1800 if (!msg) {
1801 return ADS_ERROR_SYSTEM(ENOENT);
1804 hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
1805 rc = ldap_delete_s(ads->ld, hostnameDN);
1806 ads_memfree(ads, hostnameDN);
1807 if (rc != LDAP_SUCCESS) {
1808 return ADS_ERROR(rc);
1811 status = ads_find_machine_acct(ads, &res, host);
1812 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1813 DEBUG(0, ("Failed to remove host account.\n"));
1814 return status;
1817 free(host);
1819 return status;
1823 * add machine account to existing security descriptor
1824 * @param ads connection to ads server
1825 * @param hostname machine to add
1826 * @param dn DN of security descriptor
1827 * @return status
1829 ADS_STATUS ads_set_machine_sd(ADS_STRUCT *ads, const char *hostname, char *dn)
1831 const char *attrs[] = {"nTSecurityDescriptor", "objectSid", 0};
1832 char *expr = 0;
1833 size_t sd_size = 0;
1834 struct berval bval = {0, NULL};
1835 prs_struct ps_wire;
1836 char *escaped_hostname = escape_ldap_string_alloc(hostname);
1838 LDAPMessage *res = 0;
1839 LDAPMessage *msg = 0;
1840 ADS_MODLIST mods = 0;
1842 NTSTATUS status;
1843 ADS_STATUS ret;
1844 DOM_SID sid;
1845 SEC_DESC *psd = NULL;
1846 TALLOC_CTX *ctx = NULL;
1848 /* Avoid segmentation fault in prs_mem_free if
1849 * we have to bail out before prs_init */
1850 ps_wire.is_dynamic = False;
1852 if (!ads) return ADS_ERROR(LDAP_SERVER_DOWN);
1854 ret = ADS_ERROR(LDAP_SUCCESS);
1856 if (!escaped_hostname) {
1857 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1860 if (asprintf(&expr, "(samAccountName=%s$)", escaped_hostname) == -1) {
1861 DEBUG(1, ("ads_set_machine_sd: asprintf failed!\n"));
1862 SAFE_FREE(escaped_hostname);
1863 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1866 SAFE_FREE(escaped_hostname);
1868 ret = ads_search(ads, (void *) &res, expr, attrs);
1870 if (!ADS_ERR_OK(ret)) return ret;
1872 if ( !(msg = ads_first_entry(ads, res) )) {
1873 ret = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1874 goto ads_set_sd_error;
1877 if (!ads_pull_sid(ads, msg, attrs[1], &sid)) {
1878 ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
1879 goto ads_set_sd_error;
1882 if (!(ctx = talloc_init("sec_io_desc"))) {
1883 ret = ADS_ERROR(LDAP_NO_MEMORY);
1884 goto ads_set_sd_error;
1887 if (!ads_pull_sd(ads, ctx, msg, attrs[0], &psd)) {
1888 ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
1889 goto ads_set_sd_error;
1892 status = sec_desc_add_sid(ctx, &psd, &sid, SEC_RIGHTS_FULL_CTRL, &sd_size);
1894 if (!NT_STATUS_IS_OK(status)) {
1895 ret = ADS_ERROR_NT(status);
1896 goto ads_set_sd_error;
1899 if (!prs_init(&ps_wire, sd_size, ctx, MARSHALL)) {
1900 ret = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1903 if (!sec_io_desc("sd_wire", &psd, &ps_wire, 1)) {
1904 ret = ADS_ERROR(LDAP_NO_MEMORY);
1905 goto ads_set_sd_error;
1908 #if 0
1909 file_save("/tmp/sec_desc.new", ps_wire.data_p, sd_size);
1910 #endif
1911 if (!(mods = ads_init_mods(ctx))) return ADS_ERROR(LDAP_NO_MEMORY);
1913 bval.bv_len = prs_offset(&ps_wire);
1914 bval.bv_val = TALLOC(ctx, bval.bv_len);
1915 if (!bval.bv_val) {
1916 ret = ADS_ERROR(LDAP_NO_MEMORY);
1917 goto ads_set_sd_error;
1920 prs_set_offset(&ps_wire, 0);
1922 if (!prs_copy_data_out(bval.bv_val, &ps_wire, bval.bv_len)) {
1923 ret = ADS_ERROR(LDAP_NO_MEMORY);
1924 goto ads_set_sd_error;
1927 ret = ads_mod_ber(ctx, &mods, attrs[0], &bval);
1928 if (ADS_ERR_OK(ret)) {
1929 ret = ads_gen_mod(ads, dn, mods);
1932 ads_set_sd_error:
1933 ads_msgfree(ads, res);
1934 prs_mem_free(&ps_wire);
1935 talloc_destroy(ctx);
1936 return ret;
1940 * pull the first entry from a ADS result
1941 * @param ads connection to ads server
1942 * @param res Results of search
1943 * @return first entry from result
1945 void *ads_first_entry(ADS_STRUCT *ads, void *res)
1947 return (void *)ldap_first_entry(ads->ld, (LDAPMessage *)res);
1951 * pull the next entry from a ADS result
1952 * @param ads connection to ads server
1953 * @param res Results of search
1954 * @return next entry from result
1956 void *ads_next_entry(ADS_STRUCT *ads, void *res)
1958 return (void *)ldap_next_entry(ads->ld, (LDAPMessage *)res);
1962 * pull a single string from a ADS result
1963 * @param ads connection to ads server
1964 * @param mem_ctx TALLOC_CTX to use for allocating result string
1965 * @param msg Results of search
1966 * @param field Attribute to retrieve
1967 * @return Result string in talloc context
1969 char *ads_pull_string(ADS_STRUCT *ads,
1970 TALLOC_CTX *mem_ctx, void *msg, const char *field)
1972 char **values;
1973 char *ret = NULL;
1974 char *ux_string;
1975 size_t rc;
1977 values = ldap_get_values(ads->ld, msg, field);
1978 if (!values)
1979 return NULL;
1981 if (values[0]) {
1982 rc = pull_utf8_talloc(mem_ctx, &ux_string,
1983 values[0]);
1984 if (rc != (size_t)-1)
1985 ret = ux_string;
1988 ldap_value_free(values);
1989 return ret;
1993 * pull an array of strings from a ADS result
1994 * @param ads connection to ads server
1995 * @param mem_ctx TALLOC_CTX to use for allocating result string
1996 * @param msg Results of search
1997 * @param field Attribute to retrieve
1998 * @return Result strings in talloc context
2000 char **ads_pull_strings(ADS_STRUCT *ads,
2001 TALLOC_CTX *mem_ctx, void *msg, const char *field,
2002 size_t *num_values)
2004 char **values;
2005 char **ret = NULL;
2006 int i;
2008 values = ldap_get_values(ads->ld, msg, field);
2009 if (!values)
2010 return NULL;
2012 *num_values = ldap_count_values(values);
2014 ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
2015 if (!ret) {
2016 ldap_value_free(values);
2017 return NULL;
2020 for (i=0;i<*num_values;i++) {
2021 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
2022 ldap_value_free(values);
2023 return NULL;
2026 ret[i] = NULL;
2028 ldap_value_free(values);
2029 return ret;
2033 * pull an array of strings from a ADS result
2034 * (handle large multivalue attributes with range retrieval)
2035 * @param ads connection to ads server
2036 * @param mem_ctx TALLOC_CTX to use for allocating result string
2037 * @param msg Results of search
2038 * @param field Attribute to retrieve
2039 * @param current_strings strings returned by a previous call to this function
2040 * @param next_attribute The next query should ask for this attribute
2041 * @param num_values How many values did we get this time?
2042 * @param more_values Are there more values to get?
2043 * @return Result strings in talloc context
2045 char **ads_pull_strings_range(ADS_STRUCT *ads,
2046 TALLOC_CTX *mem_ctx,
2047 void *msg, const char *field,
2048 char **current_strings,
2049 const char **next_attribute,
2050 size_t *num_strings,
2051 BOOL *more_strings)
2053 char *attr;
2054 char *expected_range_attrib, *range_attr;
2055 BerElement *ptr = NULL;
2056 char **strings;
2057 char **new_strings;
2058 size_t num_new_strings;
2059 unsigned long int range_start;
2060 unsigned long int range_end;
2062 /* we might have been given the whole lot anyway */
2063 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2064 *more_strings = False;
2065 return strings;
2068 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2070 /* look for Range result */
2071 for (attr = ldap_first_attribute(ads->ld, (LDAPMessage *)msg, &ptr);
2072 attr;
2073 attr = ldap_next_attribute(ads->ld, (LDAPMessage *)msg, ptr)) {
2074 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2075 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2076 range_attr = attr;
2077 break;
2079 ldap_memfree(attr);
2081 if (!attr) {
2082 ber_free(ptr, 0);
2083 /* nothing here - this field is just empty */
2084 *more_strings = False;
2085 return NULL;
2088 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2089 &range_start, &range_end) == 2) {
2090 *more_strings = True;
2091 } else {
2092 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2093 &range_start) == 1) {
2094 *more_strings = False;
2095 } else {
2096 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2097 range_attr));
2098 ldap_memfree(range_attr);
2099 *more_strings = False;
2100 return NULL;
2104 if ((*num_strings) != range_start) {
2105 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2106 " - aborting range retreival\n",
2107 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2108 ldap_memfree(range_attr);
2109 *more_strings = False;
2110 return NULL;
2113 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2115 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2116 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2117 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2118 range_attr, (unsigned long int)range_end - range_start + 1,
2119 (unsigned long int)num_new_strings));
2120 ldap_memfree(range_attr);
2121 *more_strings = False;
2122 return NULL;
2125 strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2126 *num_strings + num_new_strings);
2128 if (strings == NULL) {
2129 ldap_memfree(range_attr);
2130 *more_strings = False;
2131 return NULL;
2134 memcpy(&strings[*num_strings], new_strings,
2135 sizeof(*new_strings) * num_new_strings);
2137 (*num_strings) += num_new_strings;
2139 if (*more_strings) {
2140 *next_attribute = talloc_asprintf(mem_ctx,
2141 "%s;range=%d-*",
2142 field,
2143 (int)*num_strings);
2145 if (!*next_attribute) {
2146 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2147 ldap_memfree(range_attr);
2148 *more_strings = False;
2149 return NULL;
2153 ldap_memfree(range_attr);
2155 return strings;
2159 * pull a single uint32 from a ADS result
2160 * @param ads connection to ads server
2161 * @param msg Results of search
2162 * @param field Attribute to retrieve
2163 * @param v Pointer to int to store result
2164 * @return boolean inidicating success
2166 BOOL ads_pull_uint32(ADS_STRUCT *ads,
2167 void *msg, const char *field, uint32 *v)
2169 char **values;
2171 values = ldap_get_values(ads->ld, msg, field);
2172 if (!values)
2173 return False;
2174 if (!values[0]) {
2175 ldap_value_free(values);
2176 return False;
2179 *v = atoi(values[0]);
2180 ldap_value_free(values);
2181 return True;
2185 * pull a single objectGUID from an ADS result
2186 * @param ads connection to ADS server
2187 * @param msg results of search
2188 * @param guid 37-byte area to receive text guid
2189 * @return boolean indicating success
2191 BOOL ads_pull_guid(ADS_STRUCT *ads,
2192 void *msg, struct uuid *guid)
2194 char **values;
2195 UUID_FLAT flat_guid;
2197 values = ldap_get_values(ads->ld, msg, "objectGUID");
2198 if (!values)
2199 return False;
2201 if (values[0]) {
2202 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2203 smb_uuid_unpack(flat_guid, guid);
2204 ldap_value_free(values);
2205 return True;
2207 ldap_value_free(values);
2208 return False;
2214 * pull a single DOM_SID from a ADS result
2215 * @param ads connection to ads server
2216 * @param msg Results of search
2217 * @param field Attribute to retrieve
2218 * @param sid Pointer to sid to store result
2219 * @return boolean inidicating success
2221 BOOL ads_pull_sid(ADS_STRUCT *ads,
2222 void *msg, const char *field, DOM_SID *sid)
2224 struct berval **values;
2225 BOOL ret = False;
2227 values = ldap_get_values_len(ads->ld, msg, field);
2229 if (!values)
2230 return False;
2232 if (values[0])
2233 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2235 ldap_value_free_len(values);
2236 return ret;
2240 * pull an array of DOM_SIDs from a ADS result
2241 * @param ads connection to ads server
2242 * @param mem_ctx TALLOC_CTX for allocating sid array
2243 * @param msg Results of search
2244 * @param field Attribute to retrieve
2245 * @param sids pointer to sid array to allocate
2246 * @return the count of SIDs pulled
2248 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2249 void *msg, const char *field, DOM_SID **sids)
2251 struct berval **values;
2252 BOOL ret;
2253 int count, i;
2255 values = ldap_get_values_len(ads->ld, msg, field);
2257 if (!values)
2258 return 0;
2260 for (i=0; values[i]; i++)
2261 /* nop */ ;
2263 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2264 if (!(*sids)) {
2265 ldap_value_free_len(values);
2266 return 0;
2269 count = 0;
2270 for (i=0; values[i]; i++) {
2271 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2272 if (ret) {
2273 fstring sid;
2274 DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid, &(*sids)[count])));
2275 count++;
2279 ldap_value_free_len(values);
2280 return count;
2284 * pull a SEC_DESC from a ADS result
2285 * @param ads connection to ads server
2286 * @param mem_ctx TALLOC_CTX for allocating sid array
2287 * @param msg Results of search
2288 * @param field Attribute to retrieve
2289 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2290 * @return boolean inidicating success
2292 BOOL ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2293 void *msg, const char *field, SEC_DESC **sd)
2295 struct berval **values;
2296 prs_struct ps;
2297 BOOL ret = False;
2299 values = ldap_get_values_len(ads->ld, msg, field);
2301 if (!values) return False;
2303 if (values[0]) {
2304 prs_init(&ps, values[0]->bv_len, mem_ctx, UNMARSHALL);
2305 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
2306 prs_set_offset(&ps,0);
2308 ret = sec_io_desc("sd", sd, &ps, 1);
2311 ldap_value_free_len(values);
2312 return ret;
2316 * in order to support usernames longer than 21 characters we need to
2317 * use both the sAMAccountName and the userPrincipalName attributes
2318 * It seems that not all users have the userPrincipalName attribute set
2320 * @param ads connection to ads server
2321 * @param mem_ctx TALLOC_CTX for allocating sid array
2322 * @param msg Results of search
2323 * @return the username
2325 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, void *msg)
2327 #if 0 /* JERRY */
2328 char *ret, *p;
2330 /* lookup_name() only works on the sAMAccountName to
2331 returning the username portion of userPrincipalName
2332 breaks winbindd_getpwnam() */
2334 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2335 if (ret && (p = strchr_m(ret, '@'))) {
2336 *p = 0;
2337 return ret;
2339 #endif
2340 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2345 * find the update serial number - this is the core of the ldap cache
2346 * @param ads connection to ads server
2347 * @param ads connection to ADS server
2348 * @param usn Pointer to retrieved update serial number
2349 * @return status of search
2351 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2353 const char *attrs[] = {"highestCommittedUSN", NULL};
2354 ADS_STATUS status;
2355 void *res;
2357 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2358 if (!ADS_ERR_OK(status))
2359 return status;
2361 if (ads_count_replies(ads, res) != 1) {
2362 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2365 ads_pull_uint32(ads, res, "highestCommittedUSN", usn);
2366 ads_msgfree(ads, res);
2367 return ADS_SUCCESS;
2370 /* parse a ADS timestring - typical string is
2371 '20020917091222.0Z0' which means 09:12.22 17th September
2372 2002, timezone 0 */
2373 static time_t ads_parse_time(const char *str)
2375 struct tm tm;
2377 ZERO_STRUCT(tm);
2379 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2380 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2381 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2382 return 0;
2384 tm.tm_year -= 1900;
2385 tm.tm_mon -= 1;
2387 return timegm(&tm);
2391 const char *ads_get_attrname_by_oid(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char * OID)
2393 ADS_STATUS rc;
2394 int count = 0;
2395 void *res = NULL;
2396 char *expr = NULL;
2397 const char *attrs[] = { "lDAPDisplayName", NULL };
2399 if (ads == NULL || mem_ctx == NULL || OID == NULL) {
2400 goto failed;
2403 expr = talloc_asprintf(mem_ctx, "(attributeId=%s)", OID);
2404 if (expr == NULL) {
2405 goto failed;
2408 rc = ads_do_search_retry(ads, ads->config.schema_path,
2409 LDAP_SCOPE_SUBTREE, expr, attrs, &res);
2410 if (!ADS_ERR_OK(rc)) {
2411 goto failed;
2414 count = ads_count_replies(ads, res);
2415 if (count == 0 || !res) {
2416 goto failed;
2419 return ads_pull_string(ads, mem_ctx, res, "lDAPDisplayName");
2421 failed:
2422 DEBUG(0,("ads_get_attrname_by_oid: failed to retrieve name for oid: %s\n",
2423 OID));
2425 return NULL;
2429 * Find the servers name and realm - this can be done before authentication
2430 * The ldapServiceName field on w2k looks like this:
2431 * vnet3.home.samba.org:win2000-vnet3$@VNET3.HOME.SAMBA.ORG
2432 * @param ads connection to ads server
2433 * @return status of search
2435 ADS_STATUS ads_server_info(ADS_STRUCT *ads)
2437 const char *attrs[] = {"ldapServiceName",
2438 "currentTime",
2439 "schemaNamingContext", NULL};
2440 ADS_STATUS status;
2441 void *res;
2442 char *value;
2443 char *p;
2444 char *timestr;
2445 char *schema_path;
2446 TALLOC_CTX *ctx;
2448 if (!(ctx = talloc_init("ads_server_info"))) {
2449 return ADS_ERROR(LDAP_NO_MEMORY);
2452 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2453 if (!ADS_ERR_OK(status)) {
2454 talloc_destroy(ctx);
2455 return status;
2458 value = ads_pull_string(ads, ctx, res, "ldapServiceName");
2459 if (!value) {
2460 ads_msgfree(ads, res);
2461 talloc_destroy(ctx);
2462 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2465 timestr = ads_pull_string(ads, ctx, res, "currentTime");
2466 if (!timestr) {
2467 ads_msgfree(ads, res);
2468 talloc_destroy(ctx);
2469 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2472 schema_path = ads_pull_string(ads, ctx, res, "schemaNamingContext");
2473 if (!schema_path) {
2474 ads_msgfree(ads, res);
2475 talloc_destroy(ctx);
2476 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2479 SAFE_FREE(ads->config.schema_path);
2480 ads->config.schema_path = SMB_STRDUP(schema_path);
2482 ads_msgfree(ads, res);
2484 p = strchr(value, ':');
2485 if (!p) {
2486 talloc_destroy(ctx);
2487 DEBUG(1, ("ads_server_info: returned ldap server name did not contain a ':' "
2488 "so was deemed invalid\n"));
2489 return ADS_ERROR(LDAP_DECODING_ERROR);
2492 SAFE_FREE(ads->config.ldap_server_name);
2494 ads->config.ldap_server_name = SMB_STRDUP(p+1);
2495 p = strchr(ads->config.ldap_server_name, '$');
2496 if (!p || p[1] != '@') {
2497 talloc_destroy(ctx);
2498 DEBUG(1, ("ads_server_info: returned ldap server name (%s) does not contain '$@'"
2499 " so was deemed invalid\n", ads->config.ldap_server_name));
2500 SAFE_FREE(ads->config.ldap_server_name);
2501 return ADS_ERROR(LDAP_DECODING_ERROR);
2504 *p = 0;
2506 SAFE_FREE(ads->config.realm);
2507 SAFE_FREE(ads->config.bind_path);
2509 ads->config.realm = SMB_STRDUP(p+2);
2510 ads->config.bind_path = ads_build_dn(ads->config.realm);
2512 DEBUG(3,("got ldap server name %s@%s, using bind path: %s\n",
2513 ads->config.ldap_server_name, ads->config.realm,
2514 ads->config.bind_path));
2516 ads->config.current_time = ads_parse_time(timestr);
2518 if (ads->config.current_time != 0) {
2519 ads->auth.time_offset = ads->config.current_time - time(NULL);
2520 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2523 talloc_destroy(ctx);
2525 return ADS_SUCCESS;
2529 * Check for "Services for Unix"-Schema and load some attributes into the ADS_STRUCT
2530 * @param ads connection to ads server
2531 * @return BOOL status of search (False if one or more attributes couldn't be
2532 * found in Active Directory)
2533 **/
2534 BOOL ads_check_sfu_mapping(ADS_STRUCT *ads)
2536 BOOL ret = False;
2537 TALLOC_CTX *ctx = NULL;
2538 const char *gidnumber, *uidnumber, *homedir, *shell;
2540 ctx = talloc_init("ads_check_sfu_mapping");
2541 if (ctx == NULL)
2542 goto done;
2544 gidnumber = ads_get_attrname_by_oid(ads, ctx, ADS_ATTR_SFU_GIDNUMBER_OID);
2545 if (gidnumber == NULL)
2546 goto done;
2547 ads->schema.sfu_gidnumber_attr = SMB_STRDUP(gidnumber);
2549 uidnumber = ads_get_attrname_by_oid(ads, ctx, ADS_ATTR_SFU_UIDNUMBER_OID);
2550 if (uidnumber == NULL)
2551 goto done;
2552 ads->schema.sfu_uidnumber_attr = SMB_STRDUP(uidnumber);
2554 homedir = ads_get_attrname_by_oid(ads, ctx, ADS_ATTR_SFU_HOMEDIR_OID);
2555 if (homedir == NULL)
2556 goto done;
2557 ads->schema.sfu_homedir_attr = SMB_STRDUP(homedir);
2559 shell = ads_get_attrname_by_oid(ads, ctx, ADS_ATTR_SFU_SHELL_OID);
2560 if (shell == NULL)
2561 goto done;
2562 ads->schema.sfu_shell_attr = SMB_STRDUP(shell);
2564 ret = True;
2565 done:
2566 if (ctx)
2567 talloc_destroy(ctx);
2569 return ret;
2573 * find the domain sid for our domain
2574 * @param ads connection to ads server
2575 * @param sid Pointer to domain sid
2576 * @return status of search
2578 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2580 const char *attrs[] = {"objectSid", NULL};
2581 void *res;
2582 ADS_STATUS rc;
2584 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2585 attrs, &res);
2586 if (!ADS_ERR_OK(rc)) return rc;
2587 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2588 ads_msgfree(ads, res);
2589 return ADS_ERROR_SYSTEM(ENOENT);
2591 ads_msgfree(ads, res);
2593 return ADS_SUCCESS;
2596 /* this is rather complex - we need to find the allternate (netbios) name
2597 for the domain, but there isn't a simple query to do this. Instead
2598 we look for the principle names on the DCs account and find one that has
2599 the right form, then extract the netbios name of the domain from that
2601 NOTE! better method is this:
2603 bin/net -Uadministrator%XXXXX ads search '(&(objectclass=crossref)(dnsroot=VNET3.HOME.SAMBA.ORG))' nETBIOSName
2605 but you need to force the bind path to match the configurationNamingContext from the rootDSE
2608 ADS_STATUS ads_workgroup_name(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **workgroup)
2610 char *expr;
2611 ADS_STATUS rc;
2612 char **principles;
2613 char *prefix;
2614 int prefix_length;
2615 int i;
2616 void *res;
2617 const char *attrs[] = {"servicePrincipalName", NULL};
2618 size_t num_principals;
2620 (*workgroup) = NULL;
2622 asprintf(&expr, "(&(objectclass=computer)(dnshostname=%s.%s))",
2623 ads->config.ldap_server_name, ads->config.realm);
2624 if (expr == NULL) {
2625 ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2628 rc = ads_search(ads, &res, expr, attrs);
2629 free(expr);
2631 if (!ADS_ERR_OK(rc)) {
2632 return rc;
2635 principles = ads_pull_strings(ads, mem_ctx, res,
2636 "servicePrincipalName", &num_principals);
2638 ads_msgfree(ads, res);
2640 if (!principles) {
2641 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2644 asprintf(&prefix, "HOST/%s.%s/",
2645 ads->config.ldap_server_name,
2646 ads->config.realm);
2648 prefix_length = strlen(prefix);
2650 for (i=0;principles[i]; i++) {
2651 if (strnequal(principles[i], prefix, prefix_length) &&
2652 !strequal(ads->config.realm, principles[i]+prefix_length) &&
2653 !strchr(principles[i]+prefix_length, '.')) {
2654 /* found an alternate (short) name for the domain. */
2655 DEBUG(3,("Found alternate name '%s' for realm '%s'\n",
2656 principles[i]+prefix_length,
2657 ads->config.realm));
2658 (*workgroup) = talloc_strdup(mem_ctx, principles[i]+prefix_length);
2659 break;
2662 free(prefix);
2664 if (!*workgroup) {
2665 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2668 return ADS_SUCCESS;
2671 #endif