r4348: syncing up for 3.0.11pre1
[Samba.git] / source / libads / ldap.c
blobc18e253f7b2e463dbf49a78d3554770fbd045761
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 struct timeval *timeout,
79 int sizelimit,
80 LDAPMessage **res )
82 int result;
84 /* Setup timeout */
85 gotalarm = 0;
86 CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
87 alarm(lp_ldap_timeout());
88 /* End setup timeout. */
90 result = ldap_search_ext_s(ld, base, scope, filter, attrs,
91 attrsonly, sctrls, cctrls, timeout,
92 sizelimit, res);
94 /* Teardown timeout. */
95 CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
96 alarm(0);
98 if (gotalarm != 0)
99 return LDAP_TIMELIMIT_EXCEEDED;
101 return result;
105 try a connection to a given ldap server, returning True and setting the servers IP
106 in the ads struct if successful
108 TODO : add a negative connection cache in here leveraged off of the one
109 found in the rpc code. --jerry
111 static BOOL ads_try_connect(ADS_STRUCT *ads, const char *server, unsigned port)
113 char *srv;
115 if (!server || !*server) {
116 return False;
119 DEBUG(5,("ads_try_connect: trying ldap server '%s' port %u\n", server, port));
121 /* this copes with inet_ntoa brokenness */
122 srv = SMB_STRDUP(server);
124 ads->ld = ldap_open_with_timeout(srv, port, lp_ldap_timeout());
125 if (!ads->ld) {
126 free(srv);
127 return False;
129 ads->ldap_port = port;
130 ads->ldap_ip = *interpret_addr2(srv);
131 free(srv);
133 return True;
137 try a connection to a given ldap server, based on URL, returning True if successful
139 static BOOL ads_try_connect_uri(ADS_STRUCT *ads)
141 #if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
142 DEBUG(5,("ads_try_connect: trying ldap server at URI '%s'\n",
143 ads->server.ldap_uri));
146 if (ldap_initialize((LDAP**)&(ads->ld), ads->server.ldap_uri) == LDAP_SUCCESS) {
147 return True;
149 DEBUG(0, ("ldap_initialize: %s\n", strerror(errno)));
151 #else
153 DEBUG(1, ("no URL support in LDAP libs!\n"));
154 #endif
156 return False;
159 /**********************************************************************
160 Try to find an AD dc using our internal name resolution routines
161 Try the realm first and then then workgroup name if netbios is not
162 disabled
163 **********************************************************************/
165 static BOOL ads_find_dc(ADS_STRUCT *ads)
167 const char *c_realm;
168 int count, i=0;
169 struct ip_service *ip_list;
170 pstring realm;
171 BOOL got_realm = False;
172 BOOL use_own_domain = False;
174 /* if the realm and workgroup are both empty, assume they are ours */
176 /* realm */
177 c_realm = ads->server.realm;
179 if ( !c_realm || !*c_realm ) {
180 /* special case where no realm and no workgroup means our own */
181 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
182 use_own_domain = True;
183 c_realm = lp_realm();
187 if (c_realm && *c_realm)
188 got_realm = True;
190 again:
191 /* we need to try once with the realm name and fallback to the
192 netbios domain name if we fail (if netbios has not been disabled */
194 if ( !got_realm && !lp_disable_netbios() ) {
195 c_realm = ads->server.workgroup;
196 if (!c_realm || !*c_realm) {
197 if ( use_own_domain )
198 c_realm = lp_workgroup();
201 if ( !c_realm || !*c_realm ) {
202 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
203 return False;
207 pstrcpy( realm, c_realm );
209 DEBUG(6,("ads_find_dc: looking for %s '%s'\n",
210 (got_realm ? "realm" : "domain"), realm));
212 if ( !get_sorted_dc_list(realm, &ip_list, &count, got_realm) ) {
213 /* fall back to netbios if we can */
214 if ( got_realm && !lp_disable_netbios() ) {
215 got_realm = False;
216 goto again;
219 return False;
222 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
223 for ( i=0; i<count; i++ ) {
224 /* since this is an ads conection request, default to LDAP_PORT is not set */
225 int port = (ip_list[i].port!=PORT_NONE) ? ip_list[i].port : LDAP_PORT;
226 fstring server;
228 fstrcpy( server, inet_ntoa(ip_list[i].ip) );
230 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
231 continue;
233 if ( ads_try_connect(ads, server, port) ) {
234 SAFE_FREE(ip_list);
235 return True;
238 /* keep track of failures */
239 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
242 SAFE_FREE(ip_list);
244 return False;
249 * Connect to the LDAP server
250 * @param ads Pointer to an existing ADS_STRUCT
251 * @return status of connection
253 ADS_STATUS ads_connect(ADS_STRUCT *ads)
255 int version = LDAP_VERSION3;
256 ADS_STATUS status;
258 ads->last_attempt = time(NULL);
259 ads->ld = NULL;
261 /* try with a URL based server */
263 if (ads->server.ldap_uri &&
264 ads_try_connect_uri(ads)) {
265 goto got_connection;
268 /* try with a user specified server */
269 if (ads->server.ldap_server &&
270 ads_try_connect(ads, ads->server.ldap_server, LDAP_PORT)) {
271 goto got_connection;
274 if (ads_find_dc(ads)) {
275 goto got_connection;
278 return ADS_ERROR_SYSTEM(errno?errno:ENOENT);
280 got_connection:
281 DEBUG(3,("Connected to LDAP server %s\n", inet_ntoa(ads->ldap_ip)));
283 status = ads_server_info(ads);
284 if (!ADS_ERR_OK(status)) {
285 DEBUG(1,("Failed to get ldap server info\n"));
286 return status;
289 ldap_set_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
291 if (!ads->auth.user_name) {
292 /* have to use the userPrincipalName value here and
293 not servicePrincipalName; found by Guenther Deschner @ Sernet */
295 asprintf(&ads->auth.user_name, "host/%s", global_myname() );
298 if (!ads->auth.realm) {
299 ads->auth.realm = SMB_STRDUP(ads->config.realm);
302 if (!ads->auth.kdc_server) {
303 ads->auth.kdc_server = SMB_STRDUP(inet_ntoa(ads->ldap_ip));
306 #if KRB5_DNS_HACK
307 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
308 to MIT kerberos to work (tridge) */
310 char *env;
311 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
312 setenv(env, ads->auth.kdc_server, 1);
313 free(env);
315 #endif
317 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
318 return ADS_SUCCESS;
321 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
322 return ADS_ERROR(ldap_simple_bind_s( ads->ld, NULL, NULL));
325 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
326 return ADS_ERROR(ldap_simple_bind_s( ads->ld, ads->auth.user_name, ads->auth.password));
329 return ads_sasl_bind(ads);
333 Duplicate a struct berval into talloc'ed memory
335 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
337 struct berval *value;
339 if (!in_val) return NULL;
341 value = TALLOC_ZERO_P(ctx, struct berval);
342 if (value == NULL)
343 return NULL;
344 if (in_val->bv_len == 0) return value;
346 value->bv_len = in_val->bv_len;
347 value->bv_val = TALLOC_MEMDUP(ctx, in_val->bv_val, in_val->bv_len);
348 return value;
352 Make a values list out of an array of (struct berval *)
354 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
355 const struct berval **in_vals)
357 struct berval **values;
358 int i;
360 if (!in_vals) return NULL;
361 for (i=0; in_vals[i]; i++)
362 ; /* count values */
363 values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
364 if (!values) return NULL;
366 for (i=0; in_vals[i]; i++) {
367 values[i] = dup_berval(ctx, in_vals[i]);
369 return values;
373 UTF8-encode a values list out of an array of (char *)
375 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
377 char **values;
378 int i;
380 if (!in_vals) return NULL;
381 for (i=0; in_vals[i]; i++)
382 ; /* count values */
383 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
384 if (!values) return NULL;
386 for (i=0; in_vals[i]; i++) {
387 push_utf8_talloc(ctx, &values[i], in_vals[i]);
389 return values;
393 Pull a (char *) array out of a UTF8-encoded values list
395 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
397 char **values;
398 int i;
400 if (!in_vals) return NULL;
401 for (i=0; in_vals[i]; i++)
402 ; /* count values */
403 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
404 if (!values) return NULL;
406 for (i=0; in_vals[i]; i++) {
407 pull_utf8_talloc(ctx, &values[i], in_vals[i]);
409 return values;
413 * Do a search with paged results. cookie must be null on the first
414 * call, and then returned on each subsequent call. It will be null
415 * again when the entire search is complete
416 * @param ads connection to ads server
417 * @param bind_path Base dn for the search
418 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
419 * @param expr Search expression - specified in local charset
420 * @param attrs Attributes to retrieve - specified in utf8 or ascii
421 * @param res ** which will contain results - free res* with ads_msgfree()
422 * @param count Number of entries retrieved on this page
423 * @param cookie The paged results cookie to be returned on subsequent calls
424 * @return status of search
426 ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
427 int scope, const char *expr,
428 const char **attrs, void **res,
429 int *count, void **cookie)
431 int rc, i, version;
432 char *utf8_expr, *utf8_path, **search_attrs;
433 LDAPControl PagedResults, NoReferrals, *controls[3], **rcontrols;
434 BerElement *cookie_be = NULL;
435 struct berval *cookie_bv= NULL;
436 TALLOC_CTX *ctx;
438 *res = NULL;
440 if (!(ctx = talloc_init("ads_do_paged_search")))
441 return ADS_ERROR(LDAP_NO_MEMORY);
443 /* 0 means the conversion worked but the result was empty
444 so we only fail if it's -1. In any case, it always
445 at least nulls out the dest */
446 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
447 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
448 rc = LDAP_NO_MEMORY;
449 goto done;
452 if (!attrs || !(*attrs))
453 search_attrs = NULL;
454 else {
455 /* This would be the utf8-encoded version...*/
456 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
457 if (!(str_list_copy(&search_attrs, attrs))) {
458 rc = LDAP_NO_MEMORY;
459 goto done;
464 /* Paged results only available on ldap v3 or later */
465 ldap_get_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
466 if (version < LDAP_VERSION3) {
467 rc = LDAP_NOT_SUPPORTED;
468 goto done;
471 cookie_be = ber_alloc_t(LBER_USE_DER);
472 if (cookie && *cookie) {
473 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
474 ber_bvfree(*cookie); /* don't need it from last time */
475 *cookie = NULL;
476 } else {
477 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
479 ber_flatten(cookie_be, &cookie_bv);
480 PagedResults.ldctl_oid = ADS_PAGE_CTL_OID;
481 PagedResults.ldctl_iscritical = (char) 1;
482 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
483 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
485 NoReferrals.ldctl_oid = ADS_NO_REFERRALS_OID;
486 NoReferrals.ldctl_iscritical = (char) 0;
487 NoReferrals.ldctl_value.bv_len = 0;
488 NoReferrals.ldctl_value.bv_val = "";
491 controls[0] = &NoReferrals;
492 controls[1] = &PagedResults;
493 controls[2] = NULL;
495 /* we need to disable referrals as the openldap libs don't
496 handle them and paged results at the same time. Using them
497 together results in the result record containing the server
498 page control being removed from the result list (tridge/jmcd)
500 leaving this in despite the control that says don't generate
501 referrals, in case the server doesn't support it (jmcd)
503 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
505 rc = ldap_search_with_timeout(ads->ld, utf8_path, scope, utf8_expr,
506 search_attrs, 0, controls,
507 NULL, NULL, LDAP_NO_LIMIT,
508 (LDAPMessage **)res);
510 ber_free(cookie_be, 1);
511 ber_bvfree(cookie_bv);
513 if (rc) {
514 DEBUG(3,("ldap_search_with_timeout(%s) -> %s\n", expr,
515 ldap_err2string(rc)));
516 goto done;
519 rc = ldap_parse_result(ads->ld, *res, NULL, NULL, NULL,
520 NULL, &rcontrols, 0);
522 if (!rcontrols) {
523 goto done;
526 for (i=0; rcontrols[i]; i++) {
527 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
528 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
529 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
530 &cookie_bv);
531 /* the berval is the cookie, but must be freed when
532 it is all done */
533 if (cookie_bv->bv_len) /* still more to do */
534 *cookie=ber_bvdup(cookie_bv);
535 else
536 *cookie=NULL;
537 ber_bvfree(cookie_bv);
538 ber_free(cookie_be, 1);
539 break;
542 ldap_controls_free(rcontrols);
544 done:
545 talloc_destroy(ctx);
546 /* if/when we decide to utf8-encode attrs, take out this next line */
547 str_list_free(&search_attrs);
549 return ADS_ERROR(rc);
554 * Get all results for a search. This uses ads_do_paged_search() to return
555 * all entries in a large search.
556 * @param ads connection to ads server
557 * @param bind_path Base dn for the search
558 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
559 * @param expr Search expression
560 * @param attrs Attributes to retrieve
561 * @param res ** which will contain results - free res* with ads_msgfree()
562 * @return status of search
564 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
565 int scope, const char *expr,
566 const char **attrs, void **res)
568 void *cookie = NULL;
569 int count = 0;
570 ADS_STATUS status;
572 *res = NULL;
573 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, res,
574 &count, &cookie);
576 if (!ADS_ERR_OK(status)) return status;
578 while (cookie) {
579 void *res2 = NULL;
580 ADS_STATUS status2;
581 LDAPMessage *msg, *next;
583 status2 = ads_do_paged_search(ads, bind_path, scope, expr,
584 attrs, &res2, &count, &cookie);
586 if (!ADS_ERR_OK(status2)) break;
588 /* this relies on the way that ldap_add_result_entry() works internally. I hope
589 that this works on all ldap libs, but I have only tested with openldap */
590 for (msg = ads_first_entry(ads, res2); msg; msg = next) {
591 next = ads_next_entry(ads, msg);
592 ldap_add_result_entry((LDAPMessage **)res, msg);
594 /* note that we do not free res2, as the memory is now
595 part of the main returned list */
598 return status;
602 * Run a function on all results for a search. Uses ads_do_paged_search() and
603 * runs the function as each page is returned, using ads_process_results()
604 * @param ads connection to ads server
605 * @param bind_path Base dn for the search
606 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
607 * @param expr Search expression - specified in local charset
608 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
609 * @param fn Function which takes attr name, values list, and data_area
610 * @param data_area Pointer which is passed to function on each call
611 * @return status of search
613 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
614 int scope, const char *expr, const char **attrs,
615 BOOL(*fn)(char *, void **, void *),
616 void *data_area)
618 void *cookie = NULL;
619 int count = 0;
620 ADS_STATUS status;
621 void *res;
623 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
624 &count, &cookie);
626 if (!ADS_ERR_OK(status)) return status;
628 ads_process_results(ads, res, fn, data_area);
629 ads_msgfree(ads, res);
631 while (cookie) {
632 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
633 &res, &count, &cookie);
635 if (!ADS_ERR_OK(status)) break;
637 ads_process_results(ads, res, fn, data_area);
638 ads_msgfree(ads, res);
641 return status;
645 * Do a search with a timeout.
646 * @param ads connection to ads server
647 * @param bind_path Base dn for the search
648 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
649 * @param expr Search expression
650 * @param attrs Attributes to retrieve
651 * @param res ** which will contain results - free res* with ads_msgfree()
652 * @return status of search
654 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
655 const char *expr,
656 const char **attrs, void **res)
658 struct timeval timeout;
659 int rc;
660 char *utf8_expr, *utf8_path, **search_attrs = NULL;
661 TALLOC_CTX *ctx;
663 *res = NULL;
664 if (!(ctx = talloc_init("ads_do_search"))) {
665 DEBUG(1,("ads_do_search: talloc_init() failed!"));
666 return ADS_ERROR(LDAP_NO_MEMORY);
669 /* 0 means the conversion worked but the result was empty
670 so we only fail if it's negative. In any case, it always
671 at least nulls out the dest */
672 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
673 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
674 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
675 rc = LDAP_NO_MEMORY;
676 goto done;
679 if (!attrs || !(*attrs))
680 search_attrs = NULL;
681 else {
682 /* This would be the utf8-encoded version...*/
683 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
684 if (!(str_list_copy(&search_attrs, attrs)))
686 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
687 rc = LDAP_NO_MEMORY;
688 goto done;
692 timeout.tv_sec = ADS_SEARCH_TIMEOUT;
693 timeout.tv_usec = 0;
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 &timeout, 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 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 = 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 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, 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, *host_upn, *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);
1300 if (!(host_upn = talloc_asprintf(ctx, "%s@%s", host_spn, ads->config.realm))) {
1301 talloc_destroy(ctx);
1302 ads_msgfree(ads, res);
1303 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1306 /* Add the extra principal */
1307 psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name);
1308 strupper_m(psp1);
1309 strlower_m(&psp1[strlen(spn)]);
1310 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp1, machine_name));
1311 servicePrincipalName[0] = psp1;
1312 psp2 = talloc_asprintf(ctx, "%s/%s.%s", spn, machine_name, ads->config.realm);
1313 strupper_m(psp2);
1314 strlower_m(&psp2[strlen(spn)]);
1315 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp2, machine_name));
1316 servicePrincipalName[1] = psp2;
1318 /* Add another principal in case the realm != the DNS domain, so that
1319 * the KDC doesn't send "server principal unknown" errors to clients
1320 * which use the DNS name in determining service principal names. */
1321 psp3 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn);
1322 strupper_m(psp3);
1323 strlower_m(&psp3[strlen(spn)]);
1324 if (strcmp(psp2, psp3) != 0) {
1325 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp3, machine_name));
1326 servicePrincipalName[2] = psp3;
1329 if (!(mods = ads_init_mods(ctx))) {
1330 talloc_destroy(ctx);
1331 ads_msgfree(ads, res);
1332 return ADS_ERROR(LDAP_NO_MEMORY);
1334 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1335 if (!ADS_ERR_OK(ret)) {
1336 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1337 talloc_destroy(ctx);
1338 ads_msgfree(ads, res);
1339 return ret;
1341 dn_string = ads_get_dn(ads, res);
1342 if (!dn_string) {
1343 talloc_destroy(ctx);
1344 ads_msgfree(ads, res);
1345 return ADS_ERROR(LDAP_NO_MEMORY);
1347 ret = ads_gen_mod(ads, dn_string, mods);
1348 ads_memfree(ads,dn_string);
1349 if (!ADS_ERR_OK(ret)) {
1350 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1351 talloc_destroy(ctx);
1352 ads_msgfree(ads, res);
1353 return ret;
1356 talloc_destroy(ctx);
1357 ads_msgfree(ads, res);
1358 return ret;
1362 * adds a machine account to the ADS server
1363 * @param ads An intialized ADS_STRUCT
1364 * @param machine_name - the NetBIOS machine name of this account.
1365 * @param account_type A number indicating the type of account to create
1366 * @param org_unit The LDAP path in which to place this account
1367 * @return 0 upon success, or non-zero otherwise
1370 static ADS_STATUS ads_add_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1371 uint32 account_type,
1372 const char *org_unit)
1374 ADS_STATUS ret, status;
1375 char *host_spn, *host_upn, *new_dn, *samAccountName, *controlstr;
1376 TALLOC_CTX *ctx;
1377 ADS_MODLIST mods;
1378 const char *objectClass[] = {"top", "person", "organizationalPerson",
1379 "user", "computer", NULL};
1380 const char *servicePrincipalName[7] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
1381 char *psp, *psp2, *psp3, *psp4;
1382 unsigned acct_control;
1383 unsigned exists=0;
1384 fstring my_fqdn;
1385 LDAPMessage *res = NULL;
1386 int i, next_spn;
1388 if (!(ctx = talloc_init("ads_add_machine_acct")))
1389 return ADS_ERROR(LDAP_NO_MEMORY);
1391 ret = ADS_ERROR(LDAP_NO_MEMORY);
1393 name_to_fqdn(my_fqdn, machine_name);
1395 status = ads_find_machine_acct(ads, (void **)&res, machine_name);
1396 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1397 char *dn_string = ads_get_dn(ads, res);
1398 if (!dn_string) {
1399 DEBUG(1, ("ads_add_machine_acct: ads_get_dn returned NULL (malloc failure?)\n"));
1400 goto done;
1402 new_dn = talloc_strdup(ctx, dn_string);
1403 ads_memfree(ads,dn_string);
1404 DEBUG(0, ("ads_add_machine_acct: Host account for %s already exists - modifying old account\n",
1405 machine_name));
1406 exists=1;
1407 } else {
1408 char *ou_str = ads_ou_string(ads,org_unit);
1409 if (!ou_str) {
1410 DEBUG(1, ("ads_add_machine_acct: ads_ou_string returned NULL (malloc failure?)\n"));
1411 goto done;
1413 new_dn = talloc_asprintf(ctx, "cn=%s,%s,%s", machine_name, ou_str,
1414 ads->config.bind_path);
1416 SAFE_FREE(ou_str);
1419 if (!new_dn) {
1420 goto done;
1423 if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", machine_name)))
1424 goto done;
1425 if (!(host_upn = talloc_asprintf(ctx, "%s@%s", host_spn, ads->config.realm)))
1426 goto done;
1427 servicePrincipalName[0] = talloc_asprintf(ctx, "HOST/%s", machine_name);
1428 psp = talloc_asprintf(ctx, "HOST/%s.%s",
1429 machine_name,
1430 ads->config.realm);
1431 strlower_m(&psp[5]);
1432 servicePrincipalName[1] = psp;
1433 servicePrincipalName[2] = talloc_asprintf(ctx, "CIFS/%s", machine_name);
1434 psp2 = talloc_asprintf(ctx, "CIFS/%s.%s",
1435 machine_name,
1436 ads->config.realm);
1437 strlower_m(&psp2[5]);
1438 servicePrincipalName[3] = psp2;
1440 /* Ensure servicePrincipalName[4] and [5] are unique. */
1441 strlower_m(my_fqdn);
1442 psp3 = talloc_asprintf(ctx, "CIFS/%s", my_fqdn);
1443 strlower_m(&psp3[5]);
1445 next_spn = 4;
1446 for (i = 0; i < next_spn; i++) {
1447 if (strequal(servicePrincipalName[i], psp3))
1448 break;
1450 if (i == next_spn) {
1451 servicePrincipalName[next_spn++] = psp3;
1454 psp4 = talloc_asprintf(ctx, "HOST/%s", my_fqdn);
1455 strlower_m(&psp4[5]);
1456 for (i = 0; i < next_spn; i++) {
1457 if (strequal(servicePrincipalName[i], psp3))
1458 break;
1460 if (i == next_spn) {
1461 servicePrincipalName[next_spn++] = psp4;
1464 if (!(samAccountName = talloc_asprintf(ctx, "%s$", machine_name))) {
1465 goto done;
1468 acct_control = account_type | UF_DONT_EXPIRE_PASSWD;
1469 #ifndef ENCTYPE_ARCFOUR_HMAC
1470 acct_control |= UF_USE_DES_KEY_ONLY;
1471 #endif
1473 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
1474 goto done;
1477 if (!(mods = ads_init_mods(ctx))) {
1478 goto done;
1481 if (!exists) {
1482 ads_mod_str(ctx, &mods, "cn", machine_name);
1483 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1484 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1485 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1487 ads_mod_str(ctx, &mods, "dNSHostName", my_fqdn);
1488 ads_mod_str(ctx, &mods, "userPrincipalName", host_upn);
1489 ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1490 ads_mod_str(ctx, &mods, "operatingSystem", "Samba");
1491 ads_mod_str(ctx, &mods, "operatingSystemVersion", SAMBA_VERSION_STRING);
1493 if (!exists) {
1494 ret = ads_gen_add(ads, new_dn, mods);
1495 } else {
1496 ret = ads_gen_mod(ads, new_dn, mods);
1499 if (!ADS_ERR_OK(ret)) {
1500 goto done;
1503 /* Do not fail if we can't set security descriptor
1504 * it shouldn't be mandatory and probably we just
1505 * don't have enough rights to do it.
1507 if (!exists) {
1508 status = ads_set_machine_sd(ads, machine_name, new_dn);
1510 if (!ADS_ERR_OK(status)) {
1511 DEBUG(0, ("Warning: ads_set_machine_sd: %s\n",
1512 ads_errstr(status)));
1515 done:
1516 ads_msgfree(ads, res);
1517 talloc_destroy(ctx);
1518 return ret;
1522 dump a binary result from ldap
1524 static void dump_binary(const char *field, struct berval **values)
1526 int i, j;
1527 for (i=0; values[i]; i++) {
1528 printf("%s: ", field);
1529 for (j=0; j<values[i]->bv_len; j++) {
1530 printf("%02X", (unsigned char)values[i]->bv_val[j]);
1532 printf("\n");
1536 static void dump_guid(const char *field, struct berval **values)
1538 int i;
1539 UUID_FLAT guid;
1540 for (i=0; values[i]; i++) {
1541 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1542 printf("%s: %s\n", field,
1543 smb_uuid_string_static(smb_uuid_unpack_static(guid)));
1548 dump a sid result from ldap
1550 static void dump_sid(const char *field, struct berval **values)
1552 int i;
1553 for (i=0; values[i]; i++) {
1554 DOM_SID sid;
1555 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1556 printf("%s: %s\n", field, sid_string_static(&sid));
1561 dump ntSecurityDescriptor
1563 static void dump_sd(const char *filed, struct berval **values)
1565 prs_struct ps;
1567 SEC_DESC *psd = 0;
1568 TALLOC_CTX *ctx = 0;
1570 if (!(ctx = talloc_init("sec_io_desc")))
1571 return;
1573 /* prepare data */
1574 prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
1575 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
1576 prs_set_offset(&ps,0);
1578 /* parse secdesc */
1579 if (!sec_io_desc("sd", &psd, &ps, 1)) {
1580 prs_mem_free(&ps);
1581 talloc_destroy(ctx);
1582 return;
1584 if (psd) ads_disp_sd(psd);
1586 prs_mem_free(&ps);
1587 talloc_destroy(ctx);
1591 dump a string result from ldap
1593 static void dump_string(const char *field, char **values)
1595 int i;
1596 for (i=0; values[i]; i++) {
1597 printf("%s: %s\n", field, values[i]);
1602 dump a field from LDAP on stdout
1603 used for debugging
1606 static BOOL ads_dump_field(char *field, void **values, void *data_area)
1608 const struct {
1609 const char *name;
1610 BOOL string;
1611 void (*handler)(const char *, struct berval **);
1612 } handlers[] = {
1613 {"objectGUID", False, dump_guid},
1614 {"nTSecurityDescriptor", False, dump_sd},
1615 {"dnsRecord", False, dump_binary},
1616 {"objectSid", False, dump_sid},
1617 {"tokenGroups", False, dump_sid},
1618 {NULL, True, NULL}
1620 int i;
1622 if (!field) { /* must be end of an entry */
1623 printf("\n");
1624 return False;
1627 for (i=0; handlers[i].name; i++) {
1628 if (StrCaseCmp(handlers[i].name, field) == 0) {
1629 if (!values) /* first time, indicate string or not */
1630 return handlers[i].string;
1631 handlers[i].handler(field, (struct berval **) values);
1632 break;
1635 if (!handlers[i].name) {
1636 if (!values) /* first time, indicate string conversion */
1637 return True;
1638 dump_string(field, (char **)values);
1640 return False;
1644 * Dump a result from LDAP on stdout
1645 * used for debugging
1646 * @param ads connection to ads server
1647 * @param res Results to dump
1650 void ads_dump(ADS_STRUCT *ads, void *res)
1652 ads_process_results(ads, res, ads_dump_field, NULL);
1656 * Walk through results, calling a function for each entry found.
1657 * The function receives a field name, a berval * array of values,
1658 * and a data area passed through from the start. The function is
1659 * called once with null for field and values at the end of each
1660 * entry.
1661 * @param ads connection to ads server
1662 * @param res Results to process
1663 * @param fn Function for processing each result
1664 * @param data_area user-defined area to pass to function
1666 void ads_process_results(ADS_STRUCT *ads, void *res,
1667 BOOL(*fn)(char *, void **, void *),
1668 void *data_area)
1670 void *msg;
1671 TALLOC_CTX *ctx;
1673 if (!(ctx = talloc_init("ads_process_results")))
1674 return;
1676 for (msg = ads_first_entry(ads, res); msg;
1677 msg = ads_next_entry(ads, msg)) {
1678 char *utf8_field;
1679 BerElement *b;
1681 for (utf8_field=ldap_first_attribute(ads->ld,
1682 (LDAPMessage *)msg,&b);
1683 utf8_field;
1684 utf8_field=ldap_next_attribute(ads->ld,
1685 (LDAPMessage *)msg,b)) {
1686 struct berval **ber_vals;
1687 char **str_vals, **utf8_vals;
1688 char *field;
1689 BOOL string;
1691 pull_utf8_talloc(ctx, &field, utf8_field);
1692 string = fn(field, NULL, data_area);
1694 if (string) {
1695 utf8_vals = ldap_get_values(ads->ld,
1696 (LDAPMessage *)msg, field);
1697 str_vals = ads_pull_strvals(ctx,
1698 (const char **) utf8_vals);
1699 fn(field, (void **) str_vals, data_area);
1700 ldap_value_free(utf8_vals);
1701 } else {
1702 ber_vals = ldap_get_values_len(ads->ld,
1703 (LDAPMessage *)msg, field);
1704 fn(field, (void **) ber_vals, data_area);
1706 ldap_value_free_len(ber_vals);
1708 ldap_memfree(utf8_field);
1710 ber_free(b, 0);
1711 talloc_destroy_pool(ctx);
1712 fn(NULL, NULL, data_area); /* completed an entry */
1715 talloc_destroy(ctx);
1719 * count how many replies are in a LDAPMessage
1720 * @param ads connection to ads server
1721 * @param res Results to count
1722 * @return number of replies
1724 int ads_count_replies(ADS_STRUCT *ads, void *res)
1726 return ldap_count_entries(ads->ld, (LDAPMessage *)res);
1730 * Join a machine to a realm
1731 * Creates the machine account and sets the machine password
1732 * @param ads connection to ads server
1733 * @param machine name of host to add
1734 * @param org_unit Organizational unit to place machine in
1735 * @return status of join
1737 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
1738 uint32 account_type, const char *org_unit)
1740 ADS_STATUS status;
1741 LDAPMessage *res = NULL;
1742 char *machine;
1744 /* machine name must be lowercase */
1745 machine = SMB_STRDUP(machine_name);
1746 strlower_m(machine);
1749 status = ads_find_machine_acct(ads, (void **)&res, machine);
1750 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1751 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
1752 status = ads_leave_realm(ads, machine);
1753 if (!ADS_ERR_OK(status)) {
1754 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
1755 machine, ads->config.realm));
1756 return status;
1761 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
1762 if (!ADS_ERR_OK(status)) {
1763 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
1764 SAFE_FREE(machine);
1765 return status;
1768 status = ads_find_machine_acct(ads, (void **)&res, machine);
1769 if (!ADS_ERR_OK(status)) {
1770 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
1771 SAFE_FREE(machine);
1772 return status;
1775 SAFE_FREE(machine);
1776 ads_msgfree(ads, res);
1778 return status;
1782 * Delete a machine from the realm
1783 * @param ads connection to ads server
1784 * @param hostname Machine to remove
1785 * @return status of delete
1787 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
1789 ADS_STATUS status;
1790 void *res, *msg;
1791 char *hostnameDN, *host;
1792 int rc;
1794 /* hostname must be lowercase */
1795 host = SMB_STRDUP(hostname);
1796 strlower_m(host);
1798 status = ads_find_machine_acct(ads, &res, host);
1799 if (!ADS_ERR_OK(status)) {
1800 DEBUG(0, ("Host account for %s does not exist.\n", host));
1801 return status;
1804 msg = ads_first_entry(ads, res);
1805 if (!msg) {
1806 return ADS_ERROR_SYSTEM(ENOENT);
1809 hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
1810 rc = ldap_delete_s(ads->ld, hostnameDN);
1811 ads_memfree(ads, hostnameDN);
1812 if (rc != LDAP_SUCCESS) {
1813 return ADS_ERROR(rc);
1816 status = ads_find_machine_acct(ads, &res, host);
1817 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1818 DEBUG(0, ("Failed to remove host account.\n"));
1819 return status;
1822 free(host);
1824 return status;
1828 * add machine account to existing security descriptor
1829 * @param ads connection to ads server
1830 * @param hostname machine to add
1831 * @param dn DN of security descriptor
1832 * @return status
1834 ADS_STATUS ads_set_machine_sd(ADS_STRUCT *ads, const char *hostname, char *dn)
1836 const char *attrs[] = {"nTSecurityDescriptor", "objectSid", 0};
1837 char *expr = 0;
1838 size_t sd_size = 0;
1839 struct berval bval = {0, NULL};
1840 prs_struct ps_wire;
1841 char *escaped_hostname = escape_ldap_string_alloc(hostname);
1843 LDAPMessage *res = 0;
1844 LDAPMessage *msg = 0;
1845 ADS_MODLIST mods = 0;
1847 NTSTATUS status;
1848 ADS_STATUS ret;
1849 DOM_SID sid;
1850 SEC_DESC *psd = NULL;
1851 TALLOC_CTX *ctx = NULL;
1853 /* Avoid segmentation fault in prs_mem_free if
1854 * we have to bail out before prs_init */
1855 ps_wire.is_dynamic = False;
1857 if (!ads) return ADS_ERROR(LDAP_SERVER_DOWN);
1859 ret = ADS_ERROR(LDAP_SUCCESS);
1861 if (!escaped_hostname) {
1862 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1865 if (asprintf(&expr, "(samAccountName=%s$)", escaped_hostname) == -1) {
1866 DEBUG(1, ("ads_set_machine_sd: asprintf failed!\n"));
1867 SAFE_FREE(escaped_hostname);
1868 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1871 SAFE_FREE(escaped_hostname);
1873 ret = ads_search(ads, (void *) &res, expr, attrs);
1875 if (!ADS_ERR_OK(ret)) return ret;
1877 if ( !(msg = ads_first_entry(ads, res) )) {
1878 ret = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1879 goto ads_set_sd_error;
1882 if (!ads_pull_sid(ads, msg, attrs[1], &sid)) {
1883 ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
1884 goto ads_set_sd_error;
1887 if (!(ctx = talloc_init("sec_io_desc"))) {
1888 ret = ADS_ERROR(LDAP_NO_MEMORY);
1889 goto ads_set_sd_error;
1892 if (!ads_pull_sd(ads, ctx, msg, attrs[0], &psd)) {
1893 ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
1894 goto ads_set_sd_error;
1897 status = sec_desc_add_sid(ctx, &psd, &sid, SEC_RIGHTS_FULL_CTRL, &sd_size);
1899 if (!NT_STATUS_IS_OK(status)) {
1900 ret = ADS_ERROR_NT(status);
1901 goto ads_set_sd_error;
1904 if (!prs_init(&ps_wire, sd_size, ctx, MARSHALL)) {
1905 ret = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1908 if (!sec_io_desc("sd_wire", &psd, &ps_wire, 1)) {
1909 ret = ADS_ERROR(LDAP_NO_MEMORY);
1910 goto ads_set_sd_error;
1913 #if 0
1914 file_save("/tmp/sec_desc.new", ps_wire.data_p, sd_size);
1915 #endif
1916 if (!(mods = ads_init_mods(ctx))) return ADS_ERROR(LDAP_NO_MEMORY);
1918 bval.bv_len = prs_offset(&ps_wire);
1919 bval.bv_val = TALLOC(ctx, bval.bv_len);
1920 if (!bval.bv_val) {
1921 ret = ADS_ERROR(LDAP_NO_MEMORY);
1922 goto ads_set_sd_error;
1925 prs_set_offset(&ps_wire, 0);
1927 if (!prs_copy_data_out(bval.bv_val, &ps_wire, bval.bv_len)) {
1928 ret = ADS_ERROR(LDAP_NO_MEMORY);
1929 goto ads_set_sd_error;
1932 ret = ads_mod_ber(ctx, &mods, attrs[0], &bval);
1933 if (ADS_ERR_OK(ret)) {
1934 ret = ads_gen_mod(ads, dn, mods);
1937 ads_set_sd_error:
1938 ads_msgfree(ads, res);
1939 prs_mem_free(&ps_wire);
1940 talloc_destroy(ctx);
1941 return ret;
1945 * pull the first entry from a ADS result
1946 * @param ads connection to ads server
1947 * @param res Results of search
1948 * @return first entry from result
1950 void *ads_first_entry(ADS_STRUCT *ads, void *res)
1952 return (void *)ldap_first_entry(ads->ld, (LDAPMessage *)res);
1956 * pull the next entry from a ADS result
1957 * @param ads connection to ads server
1958 * @param res Results of search
1959 * @return next entry from result
1961 void *ads_next_entry(ADS_STRUCT *ads, void *res)
1963 return (void *)ldap_next_entry(ads->ld, (LDAPMessage *)res);
1967 * pull a single string from a ADS result
1968 * @param ads connection to ads server
1969 * @param mem_ctx TALLOC_CTX to use for allocating result string
1970 * @param msg Results of search
1971 * @param field Attribute to retrieve
1972 * @return Result string in talloc context
1974 char *ads_pull_string(ADS_STRUCT *ads,
1975 TALLOC_CTX *mem_ctx, void *msg, const char *field)
1977 char **values;
1978 char *ret = NULL;
1979 char *ux_string;
1980 size_t rc;
1982 values = ldap_get_values(ads->ld, msg, field);
1983 if (!values)
1984 return NULL;
1986 if (values[0]) {
1987 rc = pull_utf8_talloc(mem_ctx, &ux_string,
1988 values[0]);
1989 if (rc != (size_t)-1)
1990 ret = ux_string;
1993 ldap_value_free(values);
1994 return ret;
1998 * pull an array of strings from a ADS result
1999 * @param ads connection to ads server
2000 * @param mem_ctx TALLOC_CTX to use for allocating result string
2001 * @param msg Results of search
2002 * @param field Attribute to retrieve
2003 * @return Result strings in talloc context
2005 char **ads_pull_strings(ADS_STRUCT *ads,
2006 TALLOC_CTX *mem_ctx, void *msg, const char *field,
2007 size_t *num_values)
2009 char **values;
2010 char **ret = NULL;
2011 int i;
2013 values = ldap_get_values(ads->ld, msg, field);
2014 if (!values)
2015 return NULL;
2017 *num_values = ldap_count_values(values);
2019 ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
2020 if (!ret) {
2021 ldap_value_free(values);
2022 return NULL;
2025 for (i=0;i<*num_values;i++) {
2026 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
2027 ldap_value_free(values);
2028 return NULL;
2031 ret[i] = NULL;
2033 ldap_value_free(values);
2034 return ret;
2038 * pull an array of strings from a ADS result
2039 * (handle large multivalue attributes with range retrieval)
2040 * @param ads connection to ads server
2041 * @param mem_ctx TALLOC_CTX to use for allocating result string
2042 * @param msg Results of search
2043 * @param field Attribute to retrieve
2044 * @param current_strings strings returned by a previous call to this function
2045 * @param next_attribute The next query should ask for this attribute
2046 * @param num_values How many values did we get this time?
2047 * @param more_values Are there more values to get?
2048 * @return Result strings in talloc context
2050 char **ads_pull_strings_range(ADS_STRUCT *ads,
2051 TALLOC_CTX *mem_ctx,
2052 void *msg, const char *field,
2053 char **current_strings,
2054 const char **next_attribute,
2055 size_t *num_strings,
2056 BOOL *more_strings)
2058 char *attr;
2059 char *expected_range_attrib, *range_attr;
2060 BerElement *ptr = NULL;
2061 char **strings;
2062 char **new_strings;
2063 size_t num_new_strings;
2064 unsigned long int range_start;
2065 unsigned long int range_end;
2067 /* we might have been given the whole lot anyway */
2068 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2069 *more_strings = False;
2070 return strings;
2073 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2075 /* look for Range result */
2076 for (attr = ldap_first_attribute(ads->ld, (LDAPMessage *)msg, &ptr);
2077 attr;
2078 attr = ldap_next_attribute(ads->ld, (LDAPMessage *)msg, ptr)) {
2079 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2080 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2081 range_attr = attr;
2082 break;
2084 ldap_memfree(attr);
2086 if (!attr) {
2087 ber_free(ptr, 0);
2088 /* nothing here - this field is just empty */
2089 *more_strings = False;
2090 return NULL;
2093 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2094 &range_start, &range_end) == 2) {
2095 *more_strings = True;
2096 } else {
2097 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2098 &range_start) == 1) {
2099 *more_strings = False;
2100 } else {
2101 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2102 range_attr));
2103 ldap_memfree(range_attr);
2104 *more_strings = False;
2105 return NULL;
2109 if ((*num_strings) != range_start) {
2110 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2111 " - aborting range retreival\n",
2112 range_attr, *num_strings + 1, range_start));
2113 ldap_memfree(range_attr);
2114 *more_strings = False;
2115 return NULL;
2118 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2120 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2121 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2122 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2123 range_attr, (unsigned long int)range_end - range_start + 1,
2124 (unsigned long int)num_new_strings));
2125 ldap_memfree(range_attr);
2126 *more_strings = False;
2127 return NULL;
2130 strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2131 *num_strings + num_new_strings);
2133 if (strings == NULL) {
2134 ldap_memfree(range_attr);
2135 *more_strings = False;
2136 return NULL;
2139 memcpy(&strings[*num_strings], new_strings,
2140 sizeof(*new_strings) * num_new_strings);
2142 (*num_strings) += num_new_strings;
2144 if (*more_strings) {
2145 *next_attribute = talloc_asprintf(mem_ctx,
2146 "%s;range=%d-*",
2147 field,
2148 *num_strings);
2150 if (!*next_attribute) {
2151 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2152 ldap_memfree(range_attr);
2153 *more_strings = False;
2154 return NULL;
2158 ldap_memfree(range_attr);
2160 return strings;
2164 * pull a single uint32 from a ADS result
2165 * @param ads connection to ads server
2166 * @param msg Results of search
2167 * @param field Attribute to retrieve
2168 * @param v Pointer to int to store result
2169 * @return boolean inidicating success
2171 BOOL ads_pull_uint32(ADS_STRUCT *ads,
2172 void *msg, const char *field, uint32 *v)
2174 char **values;
2176 values = ldap_get_values(ads->ld, msg, field);
2177 if (!values)
2178 return False;
2179 if (!values[0]) {
2180 ldap_value_free(values);
2181 return False;
2184 *v = atoi(values[0]);
2185 ldap_value_free(values);
2186 return True;
2190 * pull a single objectGUID from an ADS result
2191 * @param ads connection to ADS server
2192 * @param msg results of search
2193 * @param guid 37-byte area to receive text guid
2194 * @return boolean indicating success
2196 BOOL ads_pull_guid(ADS_STRUCT *ads,
2197 void *msg, struct uuid *guid)
2199 char **values;
2200 UUID_FLAT flat_guid;
2202 values = ldap_get_values(ads->ld, msg, "objectGUID");
2203 if (!values)
2204 return False;
2206 if (values[0]) {
2207 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2208 smb_uuid_unpack(flat_guid, guid);
2209 ldap_value_free(values);
2210 return True;
2212 ldap_value_free(values);
2213 return False;
2219 * pull a single DOM_SID from a ADS result
2220 * @param ads connection to ads server
2221 * @param msg Results of search
2222 * @param field Attribute to retrieve
2223 * @param sid Pointer to sid to store result
2224 * @return boolean inidicating success
2226 BOOL ads_pull_sid(ADS_STRUCT *ads,
2227 void *msg, const char *field, DOM_SID *sid)
2229 struct berval **values;
2230 BOOL ret = False;
2232 values = ldap_get_values_len(ads->ld, msg, field);
2234 if (!values)
2235 return False;
2237 if (values[0])
2238 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2240 ldap_value_free_len(values);
2241 return ret;
2245 * pull an array of DOM_SIDs from a ADS result
2246 * @param ads connection to ads server
2247 * @param mem_ctx TALLOC_CTX for allocating sid array
2248 * @param msg Results of search
2249 * @param field Attribute to retrieve
2250 * @param sids pointer to sid array to allocate
2251 * @return the count of SIDs pulled
2253 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2254 void *msg, const char *field, DOM_SID **sids)
2256 struct berval **values;
2257 BOOL ret;
2258 int count, i;
2260 values = ldap_get_values_len(ads->ld, msg, field);
2262 if (!values)
2263 return 0;
2265 for (i=0; values[i]; i++)
2266 /* nop */ ;
2268 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2269 if (!(*sids)) {
2270 ldap_value_free_len(values);
2271 return 0;
2274 count = 0;
2275 for (i=0; values[i]; i++) {
2276 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2277 if (ret) {
2278 fstring sid;
2279 DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid, &(*sids)[count])));
2280 count++;
2284 ldap_value_free_len(values);
2285 return count;
2289 * pull a SEC_DESC from a ADS result
2290 * @param ads connection to ads server
2291 * @param mem_ctx TALLOC_CTX for allocating sid array
2292 * @param msg Results of search
2293 * @param field Attribute to retrieve
2294 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2295 * @return boolean inidicating success
2297 BOOL ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2298 void *msg, const char *field, SEC_DESC **sd)
2300 struct berval **values;
2301 prs_struct ps;
2302 BOOL ret = False;
2304 values = ldap_get_values_len(ads->ld, msg, field);
2306 if (!values) return False;
2308 if (values[0]) {
2309 prs_init(&ps, values[0]->bv_len, mem_ctx, UNMARSHALL);
2310 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
2311 prs_set_offset(&ps,0);
2313 ret = sec_io_desc("sd", sd, &ps, 1);
2316 ldap_value_free_len(values);
2317 return ret;
2321 * in order to support usernames longer than 21 characters we need to
2322 * use both the sAMAccountName and the userPrincipalName attributes
2323 * It seems that not all users have the userPrincipalName attribute set
2325 * @param ads connection to ads server
2326 * @param mem_ctx TALLOC_CTX for allocating sid array
2327 * @param msg Results of search
2328 * @return the username
2330 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, void *msg)
2332 #if 0 /* JERRY */
2333 char *ret, *p;
2335 /* lookup_name() only works on the sAMAccountName to
2336 returning the username portion of userPrincipalName
2337 breaks winbindd_getpwnam() */
2339 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2340 if (ret && (p = strchr_m(ret, '@'))) {
2341 *p = 0;
2342 return ret;
2344 #endif
2345 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2350 * find the update serial number - this is the core of the ldap cache
2351 * @param ads connection to ads server
2352 * @param ads connection to ADS server
2353 * @param usn Pointer to retrieved update serial number
2354 * @return status of search
2356 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2358 const char *attrs[] = {"highestCommittedUSN", NULL};
2359 ADS_STATUS status;
2360 void *res;
2362 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2363 if (!ADS_ERR_OK(status))
2364 return status;
2366 if (ads_count_replies(ads, res) != 1) {
2367 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2370 ads_pull_uint32(ads, res, "highestCommittedUSN", usn);
2371 ads_msgfree(ads, res);
2372 return ADS_SUCCESS;
2375 /* parse a ADS timestring - typical string is
2376 '20020917091222.0Z0' which means 09:12.22 17th September
2377 2002, timezone 0 */
2378 static time_t ads_parse_time(const char *str)
2380 struct tm tm;
2382 ZERO_STRUCT(tm);
2384 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2385 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2386 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2387 return 0;
2389 tm.tm_year -= 1900;
2390 tm.tm_mon -= 1;
2392 return timegm(&tm);
2397 * Find the servers name and realm - this can be done before authentication
2398 * The ldapServiceName field on w2k looks like this:
2399 * vnet3.home.samba.org:win2000-vnet3$@VNET3.HOME.SAMBA.ORG
2400 * @param ads connection to ads server
2401 * @return status of search
2403 ADS_STATUS ads_server_info(ADS_STRUCT *ads)
2405 const char *attrs[] = {"ldapServiceName", "currentTime", NULL};
2406 ADS_STATUS status;
2407 void *res;
2408 char *value;
2409 char *p;
2410 char *timestr;
2411 TALLOC_CTX *ctx;
2413 if (!(ctx = talloc_init("ads_server_info"))) {
2414 return ADS_ERROR(LDAP_NO_MEMORY);
2417 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2418 if (!ADS_ERR_OK(status)) {
2419 talloc_destroy(ctx);
2420 return status;
2423 value = ads_pull_string(ads, ctx, res, "ldapServiceName");
2424 if (!value) {
2425 ads_msgfree(ads, res);
2426 talloc_destroy(ctx);
2427 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2430 timestr = ads_pull_string(ads, ctx, res, "currentTime");
2431 if (!timestr) {
2432 ads_msgfree(ads, res);
2433 talloc_destroy(ctx);
2434 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2437 ads_msgfree(ads, res);
2439 p = strchr(value, ':');
2440 if (!p) {
2441 talloc_destroy(ctx);
2442 DEBUG(1, ("ads_server_info: returned ldap server name did not contain a ':' "
2443 "so was deemed invalid\n"));
2444 return ADS_ERROR(LDAP_DECODING_ERROR);
2447 SAFE_FREE(ads->config.ldap_server_name);
2449 ads->config.ldap_server_name = SMB_STRDUP(p+1);
2450 p = strchr(ads->config.ldap_server_name, '$');
2451 if (!p || p[1] != '@') {
2452 talloc_destroy(ctx);
2453 DEBUG(1, ("ads_server_info: returned ldap server name (%s) does not contain '$@'"
2454 " so was deemed invalid\n", ads->config.ldap_server_name));
2455 SAFE_FREE(ads->config.ldap_server_name);
2456 return ADS_ERROR(LDAP_DECODING_ERROR);
2459 *p = 0;
2461 SAFE_FREE(ads->config.realm);
2462 SAFE_FREE(ads->config.bind_path);
2464 ads->config.realm = SMB_STRDUP(p+2);
2465 ads->config.bind_path = ads_build_dn(ads->config.realm);
2467 DEBUG(3,("got ldap server name %s@%s, using bind path: %s\n",
2468 ads->config.ldap_server_name, ads->config.realm,
2469 ads->config.bind_path));
2471 ads->config.current_time = ads_parse_time(timestr);
2473 if (ads->config.current_time != 0) {
2474 ads->auth.time_offset = ads->config.current_time - time(NULL);
2475 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2478 talloc_destroy(ctx);
2480 return ADS_SUCCESS;
2484 * find the domain sid for our domain
2485 * @param ads connection to ads server
2486 * @param sid Pointer to domain sid
2487 * @return status of search
2489 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2491 const char *attrs[] = {"objectSid", NULL};
2492 void *res;
2493 ADS_STATUS rc;
2495 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2496 attrs, &res);
2497 if (!ADS_ERR_OK(rc)) return rc;
2498 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2499 ads_msgfree(ads, res);
2500 return ADS_ERROR_SYSTEM(ENOENT);
2502 ads_msgfree(ads, res);
2504 return ADS_SUCCESS;
2507 /* this is rather complex - we need to find the allternate (netbios) name
2508 for the domain, but there isn't a simple query to do this. Instead
2509 we look for the principle names on the DCs account and find one that has
2510 the right form, then extract the netbios name of the domain from that
2512 NOTE! better method is this:
2514 bin/net -Uadministrator%XXXXX ads search '(&(objectclass=crossref)(dnsroot=VNET3.HOME.SAMBA.ORG))' nETBIOSName
2516 but you need to force the bind path to match the configurationNamingContext from the rootDSE
2519 ADS_STATUS ads_workgroup_name(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **workgroup)
2521 char *expr;
2522 ADS_STATUS rc;
2523 char **principles;
2524 char *prefix;
2525 int prefix_length;
2526 int i;
2527 void *res;
2528 const char *attrs[] = {"servicePrincipalName", NULL};
2529 int num_principals;
2531 (*workgroup) = NULL;
2533 asprintf(&expr, "(&(objectclass=computer)(dnshostname=%s.%s))",
2534 ads->config.ldap_server_name, ads->config.realm);
2535 if (expr == NULL) {
2536 ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2539 rc = ads_search(ads, &res, expr, attrs);
2540 free(expr);
2542 if (!ADS_ERR_OK(rc)) {
2543 return rc;
2546 principles = ads_pull_strings(ads, mem_ctx, res,
2547 "servicePrincipalName", &num_principals);
2549 ads_msgfree(ads, res);
2551 if (!principles) {
2552 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2555 asprintf(&prefix, "HOST/%s.%s/",
2556 ads->config.ldap_server_name,
2557 ads->config.realm);
2559 prefix_length = strlen(prefix);
2561 for (i=0;principles[i]; i++) {
2562 if (strnequal(principles[i], prefix, prefix_length) &&
2563 !strequal(ads->config.realm, principles[i]+prefix_length) &&
2564 !strchr(principles[i]+prefix_length, '.')) {
2565 /* found an alternate (short) name for the domain. */
2566 DEBUG(3,("Found alternate name '%s' for realm '%s'\n",
2567 principles[i]+prefix_length,
2568 ads->config.realm));
2569 (*workgroup) = talloc_strdup(mem_ctx, principles[i]+prefix_length);
2570 break;
2573 free(prefix);
2575 if (!*workgroup) {
2576 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2579 return ADS_SUCCESS;
2582 #endif