r1383: sync from 3.0 tree
[Samba.git] / source / libads / ldap.c
blob78ea9f1497d63c933385fa95ffe5bd666ea4850b
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;
71 try a connection to a given ldap server, returning True and setting the servers IP
72 in the ads struct if successful
74 TODO : add a negative connection cache in here leveraged off of the one
75 found in the rpc code. --jerry
77 static BOOL ads_try_connect(ADS_STRUCT *ads, const char *server, unsigned port)
79 char *srv;
81 if (!server || !*server) {
82 return False;
85 DEBUG(5,("ads_try_connect: trying ldap server '%s' port %u\n", server, port));
87 /* this copes with inet_ntoa brokenness */
88 srv = strdup(server);
90 ads->ld = ldap_open_with_timeout(srv, port, lp_ldap_timeout());
91 if (!ads->ld) {
92 free(srv);
93 return False;
95 ads->ldap_port = port;
96 ads->ldap_ip = *interpret_addr2(srv);
97 free(srv);
99 return True;
103 try a connection to a given ldap server, based on URL, returning True if successful
105 static BOOL ads_try_connect_uri(ADS_STRUCT *ads)
107 #if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
108 DEBUG(5,("ads_try_connect: trying ldap server at URI '%s'\n",
109 ads->server.ldap_uri));
112 if (ldap_initialize((LDAP**)&(ads->ld), ads->server.ldap_uri) == LDAP_SUCCESS) {
113 return True;
115 DEBUG(0, ("ldap_initialize: %s\n", strerror(errno)));
117 #else
119 DEBUG(1, ("no URL support in LDAP libs!\n"));
120 #endif
122 return False;
125 /**********************************************************************
126 Try to find an AD dc using our internal name resolution routines
127 Try the realm first and then then workgroup name if netbios is not
128 disabled
129 **********************************************************************/
131 static BOOL ads_find_dc(ADS_STRUCT *ads)
133 const char *c_realm;
134 int count, i=0;
135 struct ip_service *ip_list;
136 pstring realm;
137 BOOL got_realm = False;
138 BOOL use_own_domain = False;
140 /* if the realm and workgroup are both empty, assume they are ours */
142 /* realm */
143 c_realm = ads->server.realm;
145 if ( !c_realm || !*c_realm ) {
146 /* special case where no realm and no workgroup means our own */
147 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
148 use_own_domain = True;
149 c_realm = lp_realm();
153 if (c_realm && *c_realm)
154 got_realm = True;
156 again:
157 /* we need to try once with the realm name and fallback to the
158 netbios domain name if we fail (if netbios has not been disabled */
160 if ( !got_realm && !lp_disable_netbios() ) {
161 c_realm = ads->server.workgroup;
162 if (!c_realm || !*c_realm) {
163 if ( use_own_domain )
164 c_realm = lp_workgroup();
167 if ( !c_realm || !*c_realm ) {
168 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
169 return False;
173 pstrcpy( realm, c_realm );
175 DEBUG(6,("ads_find_dc: looking for %s '%s'\n",
176 (got_realm ? "realm" : "domain"), realm));
178 if ( !get_sorted_dc_list(realm, &ip_list, &count, got_realm) ) {
179 /* fall back to netbios if we can */
180 if ( got_realm && !lp_disable_netbios() ) {
181 got_realm = False;
182 goto again;
185 return False;
188 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
189 for ( i=0; i<count; i++ ) {
190 /* since this is an ads conection request, default to LDAP_PORT is not set */
191 int port = (ip_list[i].port!=PORT_NONE) ? ip_list[i].port : LDAP_PORT;
192 fstring server;
194 fstrcpy( server, inet_ntoa(ip_list[i].ip) );
196 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
197 continue;
199 if ( ads_try_connect(ads, server, port) ) {
200 SAFE_FREE(ip_list);
201 return True;
204 /* keep track of failures */
205 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
208 SAFE_FREE(ip_list);
210 return False;
215 * Connect to the LDAP server
216 * @param ads Pointer to an existing ADS_STRUCT
217 * @return status of connection
219 ADS_STATUS ads_connect(ADS_STRUCT *ads)
221 int version = LDAP_VERSION3;
222 ADS_STATUS status;
224 ads->last_attempt = time(NULL);
225 ads->ld = NULL;
227 /* try with a URL based server */
229 if (ads->server.ldap_uri &&
230 ads_try_connect_uri(ads)) {
231 goto got_connection;
234 /* try with a user specified server */
235 if (ads->server.ldap_server &&
236 ads_try_connect(ads, ads->server.ldap_server, LDAP_PORT)) {
237 goto got_connection;
240 if (ads_find_dc(ads)) {
241 goto got_connection;
244 return ADS_ERROR_SYSTEM(errno?errno:ENOENT);
246 got_connection:
247 DEBUG(3,("Connected to LDAP server %s\n", inet_ntoa(ads->ldap_ip)));
249 status = ads_server_info(ads);
250 if (!ADS_ERR_OK(status)) {
251 DEBUG(1,("Failed to get ldap server info\n"));
252 return status;
255 ldap_set_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
257 if (!ads->auth.user_name) {
258 /* have to use the userPrincipalName value here and
259 not servicePrincipalName; found by Guenther Deschner @ Sernet */
261 asprintf(&ads->auth.user_name, "host/%s", global_myname() );
264 if (!ads->auth.realm) {
265 ads->auth.realm = strdup(ads->config.realm);
268 if (!ads->auth.kdc_server) {
269 ads->auth.kdc_server = strdup(inet_ntoa(ads->ldap_ip));
272 #if KRB5_DNS_HACK
273 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
274 to MIT kerberos to work (tridge) */
276 char *env;
277 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
278 setenv(env, ads->auth.kdc_server, 1);
279 free(env);
281 #endif
283 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
284 return ADS_SUCCESS;
287 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
288 return ADS_ERROR(ldap_simple_bind_s( ads->ld, NULL, NULL));
291 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
292 return ADS_ERROR(ldap_simple_bind_s( ads->ld, ads->auth.user_name, ads->auth.password));
295 return ads_sasl_bind(ads);
299 Duplicate a struct berval into talloc'ed memory
301 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
303 struct berval *value;
305 if (!in_val) return NULL;
307 value = talloc_zero(ctx, sizeof(struct berval));
308 if (value == NULL)
309 return NULL;
310 if (in_val->bv_len == 0) return value;
312 value->bv_len = in_val->bv_len;
313 value->bv_val = talloc_memdup(ctx, in_val->bv_val, in_val->bv_len);
314 return value;
318 Make a values list out of an array of (struct berval *)
320 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
321 const struct berval **in_vals)
323 struct berval **values;
324 int i;
326 if (!in_vals) return NULL;
327 for (i=0; in_vals[i]; i++); /* count values */
328 values = (struct berval **) talloc_zero(ctx,
329 (i+1)*sizeof(struct berval *));
330 if (!values) return NULL;
332 for (i=0; in_vals[i]; i++) {
333 values[i] = dup_berval(ctx, in_vals[i]);
335 return values;
339 UTF8-encode a values list out of an array of (char *)
341 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
343 char **values;
344 int i;
346 if (!in_vals) return NULL;
347 for (i=0; in_vals[i]; i++); /* count values */
348 values = (char ** ) talloc_zero(ctx, (i+1)*sizeof(char *));
349 if (!values) return NULL;
351 for (i=0; in_vals[i]; i++) {
352 push_utf8_talloc(ctx, &values[i], in_vals[i]);
354 return values;
358 Pull a (char *) array out of a UTF8-encoded values list
360 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
362 char **values;
363 int i;
365 if (!in_vals) return NULL;
366 for (i=0; in_vals[i]; i++); /* count values */
367 values = (char **) talloc_zero(ctx, (i+1)*sizeof(char *));
368 if (!values) return NULL;
370 for (i=0; in_vals[i]; i++) {
371 pull_utf8_talloc(ctx, &values[i], in_vals[i]);
373 return values;
377 * Do a search with paged results. cookie must be null on the first
378 * call, and then returned on each subsequent call. It will be null
379 * again when the entire search is complete
380 * @param ads connection to ads server
381 * @param bind_path Base dn for the search
382 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
383 * @param expr Search expression - specified in local charset
384 * @param attrs Attributes to retrieve - specified in utf8 or ascii
385 * @param res ** which will contain results - free res* with ads_msgfree()
386 * @param count Number of entries retrieved on this page
387 * @param cookie The paged results cookie to be returned on subsequent calls
388 * @return status of search
390 ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
391 int scope, const char *expr,
392 const char **attrs, void **res,
393 int *count, void **cookie)
395 int rc, i, version;
396 char *utf8_expr, *utf8_path, **search_attrs;
397 LDAPControl PagedResults, NoReferrals, *controls[3], **rcontrols;
398 BerElement *cookie_be = NULL;
399 struct berval *cookie_bv= NULL;
400 TALLOC_CTX *ctx;
402 *res = NULL;
404 if (!(ctx = talloc_init("ads_do_paged_search")))
405 return ADS_ERROR(LDAP_NO_MEMORY);
407 /* 0 means the conversion worked but the result was empty
408 so we only fail if it's -1. In any case, it always
409 at least nulls out the dest */
410 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
411 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
412 rc = LDAP_NO_MEMORY;
413 goto done;
416 if (!attrs || !(*attrs))
417 search_attrs = NULL;
418 else {
419 /* This would be the utf8-encoded version...*/
420 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
421 if (!(str_list_copy(&search_attrs, attrs))) {
422 rc = LDAP_NO_MEMORY;
423 goto done;
428 /* Paged results only available on ldap v3 or later */
429 ldap_get_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
430 if (version < LDAP_VERSION3) {
431 rc = LDAP_NOT_SUPPORTED;
432 goto done;
435 cookie_be = ber_alloc_t(LBER_USE_DER);
436 if (cookie && *cookie) {
437 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
438 ber_bvfree(*cookie); /* don't need it from last time */
439 *cookie = NULL;
440 } else {
441 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
443 ber_flatten(cookie_be, &cookie_bv);
444 PagedResults.ldctl_oid = ADS_PAGE_CTL_OID;
445 PagedResults.ldctl_iscritical = (char) 1;
446 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
447 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
449 NoReferrals.ldctl_oid = ADS_NO_REFERRALS_OID;
450 NoReferrals.ldctl_iscritical = (char) 0;
451 NoReferrals.ldctl_value.bv_len = 0;
452 NoReferrals.ldctl_value.bv_val = "";
455 controls[0] = &NoReferrals;
456 controls[1] = &PagedResults;
457 controls[2] = NULL;
459 *res = NULL;
461 /* we need to disable referrals as the openldap libs don't
462 handle them and paged results at the same time. Using them
463 together results in the result record containing the server
464 page control being removed from the result list (tridge/jmcd)
466 leaving this in despite the control that says don't generate
467 referrals, in case the server doesn't support it (jmcd)
469 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
471 rc = ldap_search_ext_s(ads->ld, utf8_path, scope, utf8_expr,
472 search_attrs, 0, controls,
473 NULL, NULL, LDAP_NO_LIMIT, (LDAPMessage **)res);
475 ber_free(cookie_be, 1);
476 ber_bvfree(cookie_bv);
478 if (rc) {
479 DEBUG(3,("ldap_search_ext_s(%s) -> %s\n", expr, ldap_err2string(rc)));
480 goto done;
483 rc = ldap_parse_result(ads->ld, *res, NULL, NULL, NULL,
484 NULL, &rcontrols, 0);
486 if (!rcontrols) {
487 goto done;
490 for (i=0; rcontrols[i]; i++) {
491 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
492 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
493 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
494 &cookie_bv);
495 /* the berval is the cookie, but must be freed when
496 it is all done */
497 if (cookie_bv->bv_len) /* still more to do */
498 *cookie=ber_bvdup(cookie_bv);
499 else
500 *cookie=NULL;
501 ber_bvfree(cookie_bv);
502 ber_free(cookie_be, 1);
503 break;
506 ldap_controls_free(rcontrols);
508 done:
509 talloc_destroy(ctx);
510 /* if/when we decide to utf8-encode attrs, take out this next line */
511 str_list_free(&search_attrs);
513 return ADS_ERROR(rc);
518 * Get all results for a search. This uses ads_do_paged_search() to return
519 * all entries in a large search.
520 * @param ads connection to ads server
521 * @param bind_path Base dn for the search
522 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
523 * @param expr Search expression
524 * @param attrs Attributes to retrieve
525 * @param res ** which will contain results - free res* with ads_msgfree()
526 * @return status of search
528 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
529 int scope, const char *expr,
530 const char **attrs, void **res)
532 void *cookie = NULL;
533 int count = 0;
534 ADS_STATUS status;
536 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, res,
537 &count, &cookie);
539 if (!ADS_ERR_OK(status)) return status;
541 while (cookie) {
542 void *res2 = NULL;
543 ADS_STATUS status2;
544 LDAPMessage *msg, *next;
546 status2 = ads_do_paged_search(ads, bind_path, scope, expr,
547 attrs, &res2, &count, &cookie);
549 if (!ADS_ERR_OK(status2)) break;
551 /* this relies on the way that ldap_add_result_entry() works internally. I hope
552 that this works on all ldap libs, but I have only tested with openldap */
553 for (msg = ads_first_entry(ads, res2); msg; msg = next) {
554 next = ads_next_entry(ads, msg);
555 ldap_add_result_entry((LDAPMessage **)res, msg);
557 /* note that we do not free res2, as the memory is now
558 part of the main returned list */
561 return status;
565 * Run a function on all results for a search. Uses ads_do_paged_search() and
566 * runs the function as each page is returned, using ads_process_results()
567 * @param ads connection to ads server
568 * @param bind_path Base dn for the search
569 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
570 * @param expr Search expression - specified in local charset
571 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
572 * @param fn Function which takes attr name, values list, and data_area
573 * @param data_area Pointer which is passed to function on each call
574 * @return status of search
576 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
577 int scope, const char *expr, const char **attrs,
578 BOOL(*fn)(char *, void **, void *),
579 void *data_area)
581 void *cookie = NULL;
582 int count = 0;
583 ADS_STATUS status;
584 void *res;
586 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
587 &count, &cookie);
589 if (!ADS_ERR_OK(status)) return status;
591 ads_process_results(ads, res, fn, data_area);
592 ads_msgfree(ads, res);
594 while (cookie) {
595 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
596 &res, &count, &cookie);
598 if (!ADS_ERR_OK(status)) break;
600 ads_process_results(ads, res, fn, data_area);
601 ads_msgfree(ads, res);
604 return status;
608 * Do a search with a timeout.
609 * @param ads connection to ads server
610 * @param bind_path Base dn for the search
611 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
612 * @param expr Search expression
613 * @param attrs Attributes to retrieve
614 * @param res ** which will contain results - free res* with ads_msgfree()
615 * @return status of search
617 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
618 const char *expr,
619 const char **attrs, void **res)
621 struct timeval timeout;
622 int rc;
623 char *utf8_expr, *utf8_path, **search_attrs = NULL;
624 TALLOC_CTX *ctx;
626 if (!(ctx = talloc_init("ads_do_search"))) {
627 DEBUG(1,("ads_do_search: talloc_init() failed!"));
628 return ADS_ERROR(LDAP_NO_MEMORY);
631 /* 0 means the conversion worked but the result was empty
632 so we only fail if it's negative. In any case, it always
633 at least nulls out the dest */
634 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
635 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
636 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
637 rc = LDAP_NO_MEMORY;
638 goto done;
641 if (!attrs || !(*attrs))
642 search_attrs = NULL;
643 else {
644 /* This would be the utf8-encoded version...*/
645 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
646 if (!(str_list_copy(&search_attrs, attrs)))
648 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
649 rc = LDAP_NO_MEMORY;
650 goto done;
654 timeout.tv_sec = ADS_SEARCH_TIMEOUT;
655 timeout.tv_usec = 0;
656 *res = NULL;
658 /* see the note in ads_do_paged_search - we *must* disable referrals */
659 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
661 rc = ldap_search_ext_s(ads->ld, utf8_path, scope, utf8_expr,
662 search_attrs, 0, NULL, NULL,
663 &timeout, LDAP_NO_LIMIT, (LDAPMessage **)res);
665 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
666 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
667 rc = 0;
670 done:
671 talloc_destroy(ctx);
672 /* if/when we decide to utf8-encode attrs, take out this next line */
673 str_list_free(&search_attrs);
674 return ADS_ERROR(rc);
677 * Do a general ADS search
678 * @param ads connection to ads server
679 * @param res ** which will contain results - free res* with ads_msgfree()
680 * @param expr Search expression
681 * @param attrs Attributes to retrieve
682 * @return status of search
684 ADS_STATUS ads_search(ADS_STRUCT *ads, void **res,
685 const char *expr,
686 const char **attrs)
688 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
689 expr, attrs, res);
693 * Do a search on a specific DistinguishedName
694 * @param ads connection to ads server
695 * @param res ** which will contain results - free res* with ads_msgfree()
696 * @param dn DistinguishName to search
697 * @param attrs Attributes to retrieve
698 * @return status of search
700 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, void **res,
701 const char *dn,
702 const char **attrs)
704 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)", attrs, res);
708 * Free up memory from a ads_search
709 * @param ads connection to ads server
710 * @param msg Search results to free
712 void ads_msgfree(ADS_STRUCT *ads, void *msg)
714 if (!msg) return;
715 ldap_msgfree(msg);
719 * Free up memory from various ads requests
720 * @param ads connection to ads server
721 * @param mem Area to free
723 void ads_memfree(ADS_STRUCT *ads, void *mem)
725 SAFE_FREE(mem);
729 * Get a dn from search results
730 * @param ads connection to ads server
731 * @param msg Search result
732 * @return dn string
734 char *ads_get_dn(ADS_STRUCT *ads, void *msg)
736 char *utf8_dn, *unix_dn;
738 utf8_dn = ldap_get_dn(ads->ld, msg);
740 if (!utf8_dn) {
741 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
742 return NULL;
745 if (pull_utf8_allocate(&unix_dn, utf8_dn) == (size_t)-1) {
746 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
747 utf8_dn ));
748 return NULL;
750 ldap_memfree(utf8_dn);
751 return unix_dn;
755 * Find a machine account given a hostname
756 * @param ads connection to ads server
757 * @param res ** which will contain results - free res* with ads_msgfree()
758 * @param host Hostname to search for
759 * @return status of search
761 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, void **res, const char *machine)
763 ADS_STATUS status;
764 char *expr;
765 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
767 /* the easiest way to find a machine account anywhere in the tree
768 is to look for hostname$ */
769 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
770 DEBUG(1, ("asprintf failed!\n"));
771 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
774 status = ads_search(ads, res, expr, attrs);
775 SAFE_FREE(expr);
776 return status;
780 * Initialize a list of mods to be used in a modify request
781 * @param ctx An initialized TALLOC_CTX
782 * @return allocated ADS_MODLIST
784 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
786 #define ADS_MODLIST_ALLOC_SIZE 10
787 LDAPMod **mods;
789 if ((mods = (LDAPMod **) talloc_zero(ctx, sizeof(LDAPMod *) *
790 (ADS_MODLIST_ALLOC_SIZE + 1))))
791 /* -1 is safety to make sure we don't go over the end.
792 need to reset it to NULL before doing ldap modify */
793 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
795 return mods;
800 add an attribute to the list, with values list already constructed
802 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
803 int mod_op, const char *name,
804 const void **invals)
806 int curmod;
807 LDAPMod **modlist = (LDAPMod **) *mods;
808 struct berval **ber_values = NULL;
809 char **char_values = NULL;
811 if (!invals) {
812 mod_op = LDAP_MOD_DELETE;
813 } else {
814 if (mod_op & LDAP_MOD_BVALUES)
815 ber_values = ads_dup_values(ctx,
816 (const struct berval **)invals);
817 else
818 char_values = ads_push_strvals(ctx,
819 (const char **) invals);
822 /* find the first empty slot */
823 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
824 curmod++);
825 if (modlist[curmod] == (LDAPMod *) -1) {
826 if (!(modlist = talloc_realloc(ctx, modlist,
827 (curmod+ADS_MODLIST_ALLOC_SIZE+1)*sizeof(LDAPMod *))))
828 return ADS_ERROR(LDAP_NO_MEMORY);
829 memset(&modlist[curmod], 0,
830 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
831 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
832 *mods = modlist;
835 if (!(modlist[curmod] = talloc_zero(ctx, sizeof(LDAPMod))))
836 return ADS_ERROR(LDAP_NO_MEMORY);
837 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
838 if (mod_op & LDAP_MOD_BVALUES) {
839 modlist[curmod]->mod_bvalues = ber_values;
840 } else if (mod_op & LDAP_MOD_DELETE) {
841 modlist[curmod]->mod_values = NULL;
842 } else {
843 modlist[curmod]->mod_values = char_values;
846 modlist[curmod]->mod_op = mod_op;
847 return ADS_ERROR(LDAP_SUCCESS);
851 * Add a single string value to a mod list
852 * @param ctx An initialized TALLOC_CTX
853 * @param mods An initialized ADS_MODLIST
854 * @param name The attribute name to add
855 * @param val The value to add - NULL means DELETE
856 * @return ADS STATUS indicating success of add
858 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
859 const char *name, const char *val)
861 const char *values[2];
863 values[0] = val;
864 values[1] = NULL;
866 if (!val)
867 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
868 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name,
869 (const void **) values);
873 * Add an array of string values to a mod list
874 * @param ctx An initialized TALLOC_CTX
875 * @param mods An initialized ADS_MODLIST
876 * @param name The attribute name to add
877 * @param vals The array of string values to add - NULL means DELETE
878 * @return ADS STATUS indicating success of add
880 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
881 const char *name, const char **vals)
883 if (!vals)
884 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
885 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
886 name, (const void **) vals);
890 * Add a single ber-encoded 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 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
898 const char *name, const struct berval *val)
900 const struct berval *values[2];
902 values[0] = val;
903 values[1] = NULL;
904 if (!val)
905 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
906 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
907 name, (const void **) values);
911 * Perform an ldap modify
912 * @param ads connection to ads server
913 * @param mod_dn DistinguishedName to modify
914 * @param mods list of modifications to perform
915 * @return status of modify
917 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
919 int ret,i;
920 char *utf8_dn = NULL;
922 this control is needed to modify that contains a currently
923 non-existent attribute (but allowable for the object) to run
925 LDAPControl PermitModify = {
926 ADS_PERMIT_MODIFY_OID,
927 {0, NULL},
928 (char) 1};
929 LDAPControl *controls[2];
931 controls[0] = &PermitModify;
932 controls[1] = NULL;
934 if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) {
935 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
938 /* find the end of the list, marked by NULL or -1 */
939 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
940 /* make sure the end of the list is NULL */
941 mods[i] = NULL;
942 ret = ldap_modify_ext_s(ads->ld, utf8_dn,
943 (LDAPMod **) mods, controls, NULL);
944 SAFE_FREE(utf8_dn);
945 return ADS_ERROR(ret);
949 * Perform an ldap add
950 * @param ads connection to ads server
951 * @param new_dn DistinguishedName to add
952 * @param mods list of attributes and values for DN
953 * @return status of add
955 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
957 int ret, i;
958 char *utf8_dn = NULL;
960 if (push_utf8_allocate(&utf8_dn, new_dn) == -1) {
961 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
962 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
965 /* find the end of the list, marked by NULL or -1 */
966 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
967 /* make sure the end of the list is NULL */
968 mods[i] = NULL;
970 ret = ldap_add_s(ads->ld, utf8_dn, mods);
971 SAFE_FREE(utf8_dn);
972 return ADS_ERROR(ret);
976 * Delete a DistinguishedName
977 * @param ads connection to ads server
978 * @param new_dn DistinguishedName to delete
979 * @return status of delete
981 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
983 int ret;
984 char *utf8_dn = NULL;
985 if (push_utf8_allocate(&utf8_dn, del_dn) == -1) {
986 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
987 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
990 ret = ldap_delete_s(ads->ld, utf8_dn);
991 return ADS_ERROR(ret);
995 * Build an org unit string
996 * if org unit is Computers or blank then assume a container, otherwise
997 * assume a \ separated list of organisational units
998 * @param org_unit Organizational unit
999 * @return org unit string - caller must free
1001 char *ads_ou_string(const char *org_unit)
1003 if (!org_unit || !*org_unit || strequal(org_unit, "Computers")) {
1004 return strdup("cn=Computers");
1007 return ads_build_path(org_unit, "\\/", "ou=", 1);
1011 * Adds (appends) an item to an attribute array, rather then
1012 * replacing the whole list
1013 * @param ctx An initialized TALLOC_CTX
1014 * @param mods An initialized ADS_MODLIST
1015 * @param name name of the ldap attribute to append to
1016 * @param vals an array of values to add
1017 * @return status of addition
1020 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1021 const char *name, const char **vals)
1023 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name, (const void **) vals);
1027 * Determines the computer account's current KVNO via an LDAP lookup
1028 * @param ads An initialized ADS_STRUCT
1029 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1030 * @return the kvno for the computer account, or -1 in case of a failure.
1033 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *machine_name)
1035 LDAPMessage *res = NULL;
1036 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1037 char *filter;
1038 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1039 char *dn_string = NULL;
1040 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1042 DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name));
1043 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1044 return kvno;
1046 ret = ads_search(ads, (void**) &res, filter, attrs);
1047 SAFE_FREE(filter);
1048 if (!ADS_ERR_OK(ret) && ads_count_replies(ads, res)) {
1049 DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name));
1050 ads_msgfree(ads, res);
1051 return kvno;
1054 dn_string = ads_get_dn(ads, res);
1055 if (!dn_string) {
1056 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1057 ads_msgfree(ads, res);
1058 return kvno;
1060 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1061 ads_memfree(ads, dn_string);
1063 /* ---------------------------------------------------------
1064 * 0 is returned as a default KVNO from this point on...
1065 * This is done because Windows 2000 does not support key
1066 * version numbers. Chances are that a failure in the next
1067 * step is simply due to Windows 2000 being used for a
1068 * domain controller. */
1069 kvno = 0;
1071 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1072 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1073 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1074 ads_msgfree(ads, res);
1075 return kvno;
1078 /* Success */
1079 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1080 ads_msgfree(ads, res);
1081 return kvno;
1085 * This clears out all registered spn's for a given hostname
1086 * @param ads An initilaized ADS_STRUCT
1087 * @param machine_name the NetBIOS name of the computer.
1088 * @return 0 upon success, non-zero otherwise.
1091 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1093 TALLOC_CTX *ctx;
1094 LDAPMessage *res = NULL;
1095 ADS_MODLIST mods;
1096 const char *servicePrincipalName[1] = {NULL};
1097 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1098 char *dn_string = NULL;
1100 ret = ads_find_machine_acct(ads, (void **)&res, machine_name);
1101 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1102 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1103 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1104 ads_msgfree(ads, res);
1105 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1108 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1109 ctx = talloc_init("ads_clear_service_principal_names");
1110 if (!ctx) {
1111 ads_msgfree(ads, res);
1112 return ADS_ERROR(LDAP_NO_MEMORY);
1115 if (!(mods = ads_init_mods(ctx))) {
1116 talloc_destroy(ctx);
1117 ads_msgfree(ads, res);
1118 return ADS_ERROR(LDAP_NO_MEMORY);
1120 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1121 if (!ADS_ERR_OK(ret)) {
1122 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1123 ads_msgfree(ads, res);
1124 talloc_destroy(ctx);
1125 return ret;
1127 dn_string = ads_get_dn(ads, res);
1128 if (!dn_string) {
1129 talloc_destroy(ctx);
1130 ads_msgfree(ads, res);
1131 return ADS_ERROR(LDAP_NO_MEMORY);
1133 ret = ads_gen_mod(ads, dn_string, mods);
1134 ads_memfree(ads,dn_string);
1135 if (!ADS_ERR_OK(ret)) {
1136 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1137 machine_name));
1138 ads_msgfree(ads, res);
1139 talloc_destroy(ctx);
1140 return ret;
1143 ads_msgfree(ads, res);
1144 talloc_destroy(ctx);
1145 return ret;
1149 * This adds a service principal name to an existing computer account
1150 * (found by hostname) in AD.
1151 * @param ads An initialized ADS_STRUCT
1152 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1153 * @param spn A string of the service principal to add, i.e. 'host'
1154 * @return 0 upon sucess, or non-zero if a failure occurs
1157 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name, const char *spn)
1159 ADS_STATUS ret;
1160 TALLOC_CTX *ctx;
1161 LDAPMessage *res = NULL;
1162 char *host_spn, *host_upn, *psp1, *psp2;
1163 ADS_MODLIST mods;
1164 fstring my_fqdn;
1165 char *dn_string = NULL;
1166 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1168 ret = ads_find_machine_acct(ads, (void **)&res, machine_name);
1169 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1170 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1171 machine_name));
1172 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1173 spn, machine_name, ads->config.realm));
1174 ads_msgfree(ads, res);
1175 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1178 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1179 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1180 ads_msgfree(ads, res);
1181 return ADS_ERROR(LDAP_NO_MEMORY);
1184 name_to_fqdn(my_fqdn, machine_name);
1185 if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", my_fqdn))) {
1186 talloc_destroy(ctx);
1187 ads_msgfree(ads, res);
1188 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1190 if (!(host_upn = talloc_asprintf(ctx, "%s@%s", host_spn, ads->config.realm))) {
1191 talloc_destroy(ctx);
1192 ads_msgfree(ads, res);
1193 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1196 /* Add the extra principal */
1197 psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name);
1198 strupper_m(psp1);
1199 strlower_m(&psp1[strlen(spn)]);
1200 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp1, machine_name));
1201 servicePrincipalName[0] = psp1;
1202 psp2 = talloc_asprintf(ctx, "%s/%s.%s", spn, machine_name, ads->config.realm);
1203 strupper_m(psp2);
1204 strlower_m(&psp2[strlen(spn)]);
1205 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp2, machine_name));
1206 servicePrincipalName[1] = psp2;
1208 if (!(mods = ads_init_mods(ctx))) {
1209 talloc_destroy(ctx);
1210 ads_msgfree(ads, res);
1211 return ADS_ERROR(LDAP_NO_MEMORY);
1213 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1214 if (!ADS_ERR_OK(ret)) {
1215 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1216 talloc_destroy(ctx);
1217 ads_msgfree(ads, res);
1218 return ret;
1220 dn_string = ads_get_dn(ads, res);
1221 if (!dn_string) {
1222 talloc_destroy(ctx);
1223 ads_msgfree(ads, res);
1224 return ADS_ERROR(LDAP_NO_MEMORY);
1226 ret = ads_gen_mod(ads, dn_string, mods);
1227 ads_memfree(ads,dn_string);
1228 if (!ADS_ERR_OK(ret)) {
1229 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1230 talloc_destroy(ctx);
1231 ads_msgfree(ads, res);
1232 return ret;
1235 talloc_destroy(ctx);
1236 ads_msgfree(ads, res);
1237 return ret;
1241 * adds a machine account to the ADS server
1242 * @param ads An intialized ADS_STRUCT
1243 * @param machine_name - the NetBIOS machine name of this account.
1244 * @param account_type A number indicating the type of account to create
1245 * @param org_unit The LDAP path in which to place this account
1246 * @return 0 upon success, or non-zero otherwise
1249 static ADS_STATUS ads_add_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1250 uint32 account_type,
1251 const char *org_unit)
1253 ADS_STATUS ret, status;
1254 char *host_spn, *host_upn, *new_dn, *samAccountName, *controlstr;
1255 TALLOC_CTX *ctx;
1256 ADS_MODLIST mods;
1257 const char *objectClass[] = {"top", "person", "organizationalPerson",
1258 "user", "computer", NULL};
1259 const char *servicePrincipalName[5] = {NULL, NULL, NULL, NULL, NULL};
1260 char *psp, *psp2;
1261 unsigned acct_control;
1262 unsigned exists=0;
1263 fstring my_fqdn;
1264 LDAPMessage *res = NULL;
1266 if (!(ctx = talloc_init("ads_add_machine_acct")))
1267 return ADS_ERROR(LDAP_NO_MEMORY);
1269 ret = ADS_ERROR(LDAP_NO_MEMORY);
1271 name_to_fqdn(my_fqdn, machine_name);
1273 status = ads_find_machine_acct(ads, (void **)&res, machine_name);
1274 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1275 char *dn_string = ads_get_dn(ads, res);
1276 if (!dn_string) {
1277 DEBUG(1, ("ads_add_machine_acct: ads_get_dn returned NULL (malloc failure?)\n"));
1278 goto done;
1280 new_dn = talloc_strdup(ctx, dn_string);
1281 ads_memfree(ads,dn_string);
1282 DEBUG(0, ("ads_add_machine_acct: Host account for %s already exists - modifying old account\n",
1283 machine_name));
1284 exists=1;
1285 } else {
1286 char *ou_str = ads_ou_string(org_unit);
1287 if (!ou_str) {
1288 DEBUG(1, ("ads_add_machine_acct: ads_ou_string returned NULL (malloc failure?)\n"));
1289 goto done;
1291 new_dn = talloc_asprintf(ctx, "cn=%s,%s,%s", machine_name, ou_str,
1292 ads->config.bind_path);
1294 SAFE_FREE(ou_str);
1297 if (!new_dn) {
1298 goto done;
1301 if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", machine_name)))
1302 goto done;
1303 if (!(host_upn = talloc_asprintf(ctx, "%s@%s", host_spn, ads->config.realm)))
1304 goto done;
1305 servicePrincipalName[0] = talloc_asprintf(ctx, "HOST/%s", machine_name);
1306 psp = talloc_asprintf(ctx, "HOST/%s.%s",
1307 machine_name,
1308 ads->config.realm);
1309 strlower_m(&psp[5]);
1310 servicePrincipalName[1] = psp;
1311 servicePrincipalName[2] = talloc_asprintf(ctx, "CIFS/%s", machine_name);
1312 psp2 = talloc_asprintf(ctx, "CIFS/%s.%s",
1313 machine_name,
1314 ads->config.realm);
1315 strlower_m(&psp2[5]);
1316 servicePrincipalName[3] = psp2;
1318 if (!(samAccountName = talloc_asprintf(ctx, "%s$", machine_name))) {
1319 goto done;
1322 acct_control = account_type | UF_DONT_EXPIRE_PASSWD;
1323 #ifndef ENCTYPE_ARCFOUR_HMAC
1324 acct_control |= UF_USE_DES_KEY_ONLY;
1325 #endif
1327 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
1328 goto done;
1331 if (!(mods = ads_init_mods(ctx))) {
1332 goto done;
1335 if (!exists) {
1336 ads_mod_str(ctx, &mods, "cn", machine_name);
1337 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1338 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1339 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1341 ads_mod_str(ctx, &mods, "dNSHostName", my_fqdn);
1342 ads_mod_str(ctx, &mods, "userPrincipalName", host_upn);
1343 ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1344 ads_mod_str(ctx, &mods, "operatingSystem", "Samba");
1345 ads_mod_str(ctx, &mods, "operatingSystemVersion", SAMBA_VERSION_STRING);
1347 if (!exists) {
1348 ret = ads_gen_add(ads, new_dn, mods);
1349 } else {
1350 ret = ads_gen_mod(ads, new_dn, mods);
1353 if (!ADS_ERR_OK(ret)) {
1354 goto done;
1357 /* Do not fail if we can't set security descriptor
1358 * it shouldn't be mandatory and probably we just
1359 * don't have enough rights to do it.
1361 if (!exists) {
1362 status = ads_set_machine_sd(ads, machine_name, new_dn);
1364 if (!ADS_ERR_OK(status)) {
1365 DEBUG(0, ("Warning: ads_set_machine_sd: %s\n",
1366 ads_errstr(status)));
1369 done:
1370 ads_msgfree(ads, res);
1371 talloc_destroy(ctx);
1372 return ret;
1376 dump a binary result from ldap
1378 static void dump_binary(const char *field, struct berval **values)
1380 int i, j;
1381 for (i=0; values[i]; i++) {
1382 printf("%s: ", field);
1383 for (j=0; j<values[i]->bv_len; j++) {
1384 printf("%02X", (unsigned char)values[i]->bv_val[j]);
1386 printf("\n");
1390 static void dump_guid(const char *field, struct berval **values)
1392 int i;
1393 UUID_FLAT guid;
1394 for (i=0; values[i]; i++) {
1395 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1396 printf("%s: %s\n", field,
1397 smb_uuid_string_static(smb_uuid_unpack_static(guid)));
1402 dump a sid result from ldap
1404 static void dump_sid(const char *field, struct berval **values)
1406 int i;
1407 for (i=0; values[i]; i++) {
1408 DOM_SID sid;
1409 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1410 printf("%s: %s\n", field, sid_string_static(&sid));
1415 dump ntSecurityDescriptor
1417 static void dump_sd(const char *filed, struct berval **values)
1419 prs_struct ps;
1421 SEC_DESC *psd = 0;
1422 TALLOC_CTX *ctx = 0;
1424 if (!(ctx = talloc_init("sec_io_desc")))
1425 return;
1427 /* prepare data */
1428 prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
1429 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
1430 prs_set_offset(&ps,0);
1432 /* parse secdesc */
1433 if (!sec_io_desc("sd", &psd, &ps, 1)) {
1434 prs_mem_free(&ps);
1435 talloc_destroy(ctx);
1436 return;
1438 if (psd) ads_disp_sd(psd);
1440 prs_mem_free(&ps);
1441 talloc_destroy(ctx);
1445 dump a string result from ldap
1447 static void dump_string(const char *field, char **values)
1449 int i;
1450 for (i=0; values[i]; i++) {
1451 printf("%s: %s\n", field, values[i]);
1456 dump a field from LDAP on stdout
1457 used for debugging
1460 static BOOL ads_dump_field(char *field, void **values, void *data_area)
1462 const struct {
1463 const char *name;
1464 BOOL string;
1465 void (*handler)(const char *, struct berval **);
1466 } handlers[] = {
1467 {"objectGUID", False, dump_guid},
1468 {"nTSecurityDescriptor", False, dump_sd},
1469 {"dnsRecord", False, dump_binary},
1470 {"objectSid", False, dump_sid},
1471 {"tokenGroups", False, dump_sid},
1472 {NULL, True, NULL}
1474 int i;
1476 if (!field) { /* must be end of an entry */
1477 printf("\n");
1478 return False;
1481 for (i=0; handlers[i].name; i++) {
1482 if (StrCaseCmp(handlers[i].name, field) == 0) {
1483 if (!values) /* first time, indicate string or not */
1484 return handlers[i].string;
1485 handlers[i].handler(field, (struct berval **) values);
1486 break;
1489 if (!handlers[i].name) {
1490 if (!values) /* first time, indicate string conversion */
1491 return True;
1492 dump_string(field, (char **)values);
1494 return False;
1498 * Dump a result from LDAP on stdout
1499 * used for debugging
1500 * @param ads connection to ads server
1501 * @param res Results to dump
1504 void ads_dump(ADS_STRUCT *ads, void *res)
1506 ads_process_results(ads, res, ads_dump_field, NULL);
1510 * Walk through results, calling a function for each entry found.
1511 * The function receives a field name, a berval * array of values,
1512 * and a data area passed through from the start. The function is
1513 * called once with null for field and values at the end of each
1514 * entry.
1515 * @param ads connection to ads server
1516 * @param res Results to process
1517 * @param fn Function for processing each result
1518 * @param data_area user-defined area to pass to function
1520 void ads_process_results(ADS_STRUCT *ads, void *res,
1521 BOOL(*fn)(char *, void **, void *),
1522 void *data_area)
1524 void *msg;
1525 TALLOC_CTX *ctx;
1527 if (!(ctx = talloc_init("ads_process_results")))
1528 return;
1530 for (msg = ads_first_entry(ads, res); msg;
1531 msg = ads_next_entry(ads, msg)) {
1532 char *utf8_field;
1533 BerElement *b;
1535 for (utf8_field=ldap_first_attribute(ads->ld,
1536 (LDAPMessage *)msg,&b);
1537 utf8_field;
1538 utf8_field=ldap_next_attribute(ads->ld,
1539 (LDAPMessage *)msg,b)) {
1540 struct berval **ber_vals;
1541 char **str_vals, **utf8_vals;
1542 char *field;
1543 BOOL string;
1545 pull_utf8_talloc(ctx, &field, utf8_field);
1546 string = fn(field, NULL, data_area);
1548 if (string) {
1549 utf8_vals = ldap_get_values(ads->ld,
1550 (LDAPMessage *)msg, field);
1551 str_vals = ads_pull_strvals(ctx,
1552 (const char **) utf8_vals);
1553 fn(field, (void **) str_vals, data_area);
1554 ldap_value_free(utf8_vals);
1555 } else {
1556 ber_vals = ldap_get_values_len(ads->ld,
1557 (LDAPMessage *)msg, field);
1558 fn(field, (void **) ber_vals, data_area);
1560 ldap_value_free_len(ber_vals);
1562 ldap_memfree(utf8_field);
1564 ber_free(b, 0);
1565 talloc_destroy_pool(ctx);
1566 fn(NULL, NULL, data_area); /* completed an entry */
1569 talloc_destroy(ctx);
1573 * count how many replies are in a LDAPMessage
1574 * @param ads connection to ads server
1575 * @param res Results to count
1576 * @return number of replies
1578 int ads_count_replies(ADS_STRUCT *ads, void *res)
1580 return ldap_count_entries(ads->ld, (LDAPMessage *)res);
1584 * Join a machine to a realm
1585 * Creates the machine account and sets the machine password
1586 * @param ads connection to ads server
1587 * @param machine name of host to add
1588 * @param org_unit Organizational unit to place machine in
1589 * @return status of join
1591 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
1592 uint32 account_type, const char *org_unit)
1594 ADS_STATUS status;
1595 LDAPMessage *res = NULL;
1596 char *machine;
1598 /* machine name must be lowercase */
1599 machine = strdup(machine_name);
1600 strlower_m(machine);
1603 status = ads_find_machine_acct(ads, (void **)&res, machine);
1604 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1605 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
1606 status = ads_leave_realm(ads, machine);
1607 if (!ADS_ERR_OK(status)) {
1608 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
1609 machine, ads->config.realm));
1610 return status;
1615 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
1616 if (!ADS_ERR_OK(status)) {
1617 DEBUG(0, ("ads_add_machine_acct (%s): %s\n", machine, ads_errstr(status)));
1618 SAFE_FREE(machine);
1619 return status;
1622 status = ads_find_machine_acct(ads, (void **)&res, machine);
1623 if (!ADS_ERR_OK(status)) {
1624 DEBUG(0, ("Host account test failed for machine %s\n", machine));
1625 SAFE_FREE(machine);
1626 return status;
1629 SAFE_FREE(machine);
1630 ads_msgfree(ads, res);
1632 return status;
1636 * Delete a machine from the realm
1637 * @param ads connection to ads server
1638 * @param hostname Machine to remove
1639 * @return status of delete
1641 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
1643 ADS_STATUS status;
1644 void *res, *msg;
1645 char *hostnameDN, *host;
1646 int rc;
1648 /* hostname must be lowercase */
1649 host = strdup(hostname);
1650 strlower_m(host);
1652 status = ads_find_machine_acct(ads, &res, host);
1653 if (!ADS_ERR_OK(status)) {
1654 DEBUG(0, ("Host account for %s does not exist.\n", host));
1655 return status;
1658 msg = ads_first_entry(ads, res);
1659 if (!msg) {
1660 return ADS_ERROR_SYSTEM(ENOENT);
1663 hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
1664 rc = ldap_delete_s(ads->ld, hostnameDN);
1665 ads_memfree(ads, hostnameDN);
1666 if (rc != LDAP_SUCCESS) {
1667 return ADS_ERROR(rc);
1670 status = ads_find_machine_acct(ads, &res, host);
1671 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1672 DEBUG(0, ("Failed to remove host account.\n"));
1673 return status;
1676 free(host);
1678 return status;
1682 * add machine account to existing security descriptor
1683 * @param ads connection to ads server
1684 * @param hostname machine to add
1685 * @param dn DN of security descriptor
1686 * @return status
1688 ADS_STATUS ads_set_machine_sd(ADS_STRUCT *ads, const char *hostname, char *dn)
1690 const char *attrs[] = {"nTSecurityDescriptor", "objectSid", 0};
1691 char *expr = 0;
1692 size_t sd_size = 0;
1693 struct berval bval = {0, NULL};
1694 prs_struct ps_wire;
1695 char *escaped_hostname = escape_ldap_string_alloc(hostname);
1697 LDAPMessage *res = 0;
1698 LDAPMessage *msg = 0;
1699 ADS_MODLIST mods = 0;
1701 NTSTATUS status;
1702 ADS_STATUS ret;
1703 DOM_SID sid;
1704 SEC_DESC *psd = NULL;
1705 TALLOC_CTX *ctx = NULL;
1707 /* Avoid segmentation fault in prs_mem_free if
1708 * we have to bail out before prs_init */
1709 ps_wire.is_dynamic = False;
1711 if (!ads) return ADS_ERROR(LDAP_SERVER_DOWN);
1713 ret = ADS_ERROR(LDAP_SUCCESS);
1715 if (!escaped_hostname) {
1716 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1719 if (asprintf(&expr, "(samAccountName=%s$)", escaped_hostname) == -1) {
1720 DEBUG(1, ("ads_set_machine_sd: asprintf failed!\n"));
1721 SAFE_FREE(escaped_hostname);
1722 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1725 SAFE_FREE(escaped_hostname);
1727 ret = ads_search(ads, (void *) &res, expr, attrs);
1729 if (!ADS_ERR_OK(ret)) return ret;
1731 if ( !(msg = ads_first_entry(ads, res) )) {
1732 ret = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1733 goto ads_set_sd_error;
1736 if (!ads_pull_sid(ads, msg, attrs[1], &sid)) {
1737 ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
1738 goto ads_set_sd_error;
1741 if (!(ctx = talloc_init("sec_io_desc"))) {
1742 ret = ADS_ERROR(LDAP_NO_MEMORY);
1743 goto ads_set_sd_error;
1746 if (!ads_pull_sd(ads, ctx, msg, attrs[0], &psd)) {
1747 ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
1748 goto ads_set_sd_error;
1751 status = sec_desc_add_sid(ctx, &psd, &sid, SEC_RIGHTS_FULL_CTRL, &sd_size);
1753 if (!NT_STATUS_IS_OK(status)) {
1754 ret = ADS_ERROR_NT(status);
1755 goto ads_set_sd_error;
1758 if (!prs_init(&ps_wire, sd_size, ctx, MARSHALL)) {
1759 ret = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1762 if (!sec_io_desc("sd_wire", &psd, &ps_wire, 1)) {
1763 ret = ADS_ERROR(LDAP_NO_MEMORY);
1764 goto ads_set_sd_error;
1767 #if 0
1768 file_save("/tmp/sec_desc.new", ps_wire.data_p, sd_size);
1769 #endif
1770 if (!(mods = ads_init_mods(ctx))) return ADS_ERROR(LDAP_NO_MEMORY);
1772 bval.bv_len = prs_offset(&ps_wire);
1773 bval.bv_val = talloc(ctx, bval.bv_len);
1774 if (!bval.bv_val) {
1775 ret = ADS_ERROR(LDAP_NO_MEMORY);
1776 goto ads_set_sd_error;
1779 prs_set_offset(&ps_wire, 0);
1781 if (!prs_copy_data_out(bval.bv_val, &ps_wire, bval.bv_len)) {
1782 ret = ADS_ERROR(LDAP_NO_MEMORY);
1783 goto ads_set_sd_error;
1786 ret = ads_mod_ber(ctx, &mods, attrs[0], &bval);
1787 if (ADS_ERR_OK(ret)) {
1788 ret = ads_gen_mod(ads, dn, mods);
1791 ads_set_sd_error:
1792 ads_msgfree(ads, res);
1793 prs_mem_free(&ps_wire);
1794 talloc_destroy(ctx);
1795 return ret;
1799 * pull the first entry from a ADS result
1800 * @param ads connection to ads server
1801 * @param res Results of search
1802 * @return first entry from result
1804 void *ads_first_entry(ADS_STRUCT *ads, void *res)
1806 return (void *)ldap_first_entry(ads->ld, (LDAPMessage *)res);
1810 * pull the next entry from a ADS result
1811 * @param ads connection to ads server
1812 * @param res Results of search
1813 * @return next entry from result
1815 void *ads_next_entry(ADS_STRUCT *ads, void *res)
1817 return (void *)ldap_next_entry(ads->ld, (LDAPMessage *)res);
1821 * pull a single string from a ADS result
1822 * @param ads connection to ads server
1823 * @param mem_ctx TALLOC_CTX to use for allocating result string
1824 * @param msg Results of search
1825 * @param field Attribute to retrieve
1826 * @return Result string in talloc context
1828 char *ads_pull_string(ADS_STRUCT *ads,
1829 TALLOC_CTX *mem_ctx, void *msg, const char *field)
1831 char **values;
1832 char *ret = NULL;
1833 char *ux_string;
1834 size_t rc;
1836 values = ldap_get_values(ads->ld, msg, field);
1837 if (!values)
1838 return NULL;
1840 if (values[0]) {
1841 rc = pull_utf8_talloc(mem_ctx, &ux_string,
1842 values[0]);
1843 if (rc != (size_t)-1)
1844 ret = ux_string;
1847 ldap_value_free(values);
1848 return ret;
1852 * pull an array of strings from a ADS result
1853 * @param ads connection to ads server
1854 * @param mem_ctx TALLOC_CTX to use for allocating result string
1855 * @param msg Results of search
1856 * @param field Attribute to retrieve
1857 * @return Result strings in talloc context
1859 char **ads_pull_strings(ADS_STRUCT *ads,
1860 TALLOC_CTX *mem_ctx, void *msg, const char *field,
1861 size_t *num_values)
1863 char **values;
1864 char **ret = NULL;
1865 int i;
1867 values = ldap_get_values(ads->ld, msg, field);
1868 if (!values)
1869 return NULL;
1871 *num_values = ldap_count_values(values);
1873 ret = talloc(mem_ctx, sizeof(char *) * (*num_values+1));
1874 if (!ret) {
1875 ldap_value_free(values);
1876 return NULL;
1879 for (i=0;i<*num_values;i++) {
1880 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
1881 ldap_value_free(values);
1882 return NULL;
1885 ret[i] = NULL;
1887 ldap_value_free(values);
1888 return ret;
1892 * pull an array of strings from a ADS result
1893 * (handle large multivalue attributes with range retrieval)
1894 * @param ads connection to ads server
1895 * @param mem_ctx TALLOC_CTX to use for allocating result string
1896 * @param msg Results of search
1897 * @param field Attribute to retrieve
1898 * @param current_strings strings returned by a previous call to this function
1899 * @param next_attribute The next query should ask for this attribute
1900 * @param num_values How many values did we get this time?
1901 * @param more_values Are there more values to get?
1902 * @return Result strings in talloc context
1904 char **ads_pull_strings_range(ADS_STRUCT *ads,
1905 TALLOC_CTX *mem_ctx,
1906 void *msg, const char *field,
1907 char **current_strings,
1908 const char **next_attribute,
1909 size_t *num_strings,
1910 BOOL *more_strings)
1912 char *attr;
1913 char *expected_range_attrib, *range_attr;
1914 BerElement *ptr = NULL;
1915 char **strings;
1916 char **new_strings;
1917 size_t num_new_strings;
1918 unsigned long int range_start;
1919 unsigned long int range_end;
1921 /* we might have been given the whole lot anyway */
1922 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
1923 *more_strings = False;
1924 return strings;
1927 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
1929 /* look for Range result */
1930 for (attr = ldap_first_attribute(ads->ld, (LDAPMessage *)msg, &ptr);
1931 attr;
1932 attr = ldap_next_attribute(ads->ld, (LDAPMessage *)msg, ptr)) {
1933 /* we ignore the fact that this is utf8, as all attributes are ascii... */
1934 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
1935 range_attr = attr;
1936 break;
1938 ldap_memfree(attr);
1940 if (!attr) {
1941 ber_free(ptr, 0);
1942 /* nothing here - this field is just empty */
1943 *more_strings = False;
1944 return NULL;
1947 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
1948 &range_start, &range_end) == 2) {
1949 *more_strings = True;
1950 } else {
1951 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
1952 &range_start) == 1) {
1953 *more_strings = False;
1954 } else {
1955 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
1956 range_attr));
1957 ldap_memfree(range_attr);
1958 *more_strings = False;
1959 return NULL;
1963 if ((*num_strings) != range_start) {
1964 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
1965 " - aborting range retreival\n",
1966 range_attr, *num_strings + 1, range_start));
1967 ldap_memfree(range_attr);
1968 *more_strings = False;
1969 return NULL;
1972 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
1974 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
1975 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
1976 "strings in this bunch, but we only got %lu - aborting range retreival\n",
1977 range_attr, (unsigned long int)range_end - range_start + 1,
1978 (unsigned long int)num_new_strings));
1979 ldap_memfree(range_attr);
1980 *more_strings = False;
1981 return NULL;
1984 strings = talloc_realloc(mem_ctx, current_strings,
1985 sizeof(*current_strings) *
1986 (*num_strings + num_new_strings));
1988 if (strings == NULL) {
1989 ldap_memfree(range_attr);
1990 *more_strings = False;
1991 return NULL;
1994 memcpy(&strings[*num_strings], new_strings,
1995 sizeof(*new_strings) * num_new_strings);
1997 (*num_strings) += num_new_strings;
1999 if (*more_strings) {
2000 *next_attribute = talloc_asprintf(mem_ctx,
2001 "%s;range=%d-*",
2002 field,
2003 *num_strings);
2005 if (!*next_attribute) {
2006 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2007 ldap_memfree(range_attr);
2008 *more_strings = False;
2009 return NULL;
2013 ldap_memfree(range_attr);
2015 return strings;
2019 * pull a single uint32 from a ADS result
2020 * @param ads connection to ads server
2021 * @param msg Results of search
2022 * @param field Attribute to retrieve
2023 * @param v Pointer to int to store result
2024 * @return boolean inidicating success
2026 BOOL ads_pull_uint32(ADS_STRUCT *ads,
2027 void *msg, const char *field, uint32 *v)
2029 char **values;
2031 values = ldap_get_values(ads->ld, msg, field);
2032 if (!values)
2033 return False;
2034 if (!values[0]) {
2035 ldap_value_free(values);
2036 return False;
2039 *v = atoi(values[0]);
2040 ldap_value_free(values);
2041 return True;
2045 * pull a single objectGUID from an ADS result
2046 * @param ads connection to ADS server
2047 * @param msg results of search
2048 * @param guid 37-byte area to receive text guid
2049 * @return boolean indicating success
2051 BOOL ads_pull_guid(ADS_STRUCT *ads,
2052 void *msg, struct uuid *guid)
2054 char **values;
2055 UUID_FLAT flat_guid;
2057 values = ldap_get_values(ads->ld, msg, "objectGUID");
2058 if (!values)
2059 return False;
2061 if (values[0]) {
2062 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2063 smb_uuid_unpack(flat_guid, guid);
2064 ldap_value_free(values);
2065 return True;
2067 ldap_value_free(values);
2068 return False;
2074 * pull a single DOM_SID from a ADS result
2075 * @param ads connection to ads server
2076 * @param msg Results of search
2077 * @param field Attribute to retrieve
2078 * @param sid Pointer to sid to store result
2079 * @return boolean inidicating success
2081 BOOL ads_pull_sid(ADS_STRUCT *ads,
2082 void *msg, const char *field, DOM_SID *sid)
2084 struct berval **values;
2085 BOOL ret = False;
2087 values = ldap_get_values_len(ads->ld, msg, field);
2089 if (!values)
2090 return False;
2092 if (values[0])
2093 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2095 ldap_value_free_len(values);
2096 return ret;
2100 * pull an array of DOM_SIDs from a ADS result
2101 * @param ads connection to ads server
2102 * @param mem_ctx TALLOC_CTX for allocating sid array
2103 * @param msg Results of search
2104 * @param field Attribute to retrieve
2105 * @param sids pointer to sid array to allocate
2106 * @return the count of SIDs pulled
2108 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2109 void *msg, const char *field, DOM_SID **sids)
2111 struct berval **values;
2112 BOOL ret;
2113 int count, i;
2115 values = ldap_get_values_len(ads->ld, msg, field);
2117 if (!values)
2118 return 0;
2120 for (i=0; values[i]; i++)
2121 /* nop */ ;
2123 (*sids) = talloc(mem_ctx, sizeof(DOM_SID) * i);
2124 if (!(*sids)) {
2125 ldap_value_free_len(values);
2126 return 0;
2129 count = 0;
2130 for (i=0; values[i]; i++) {
2131 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2132 if (ret) {
2133 fstring sid;
2134 DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid, &(*sids)[count])));
2135 count++;
2139 ldap_value_free_len(values);
2140 return count;
2144 * pull a SEC_DESC from a ADS result
2145 * @param ads connection to ads server
2146 * @param mem_ctx TALLOC_CTX for allocating sid array
2147 * @param msg Results of search
2148 * @param field Attribute to retrieve
2149 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2150 * @return boolean inidicating success
2152 BOOL ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2153 void *msg, const char *field, SEC_DESC **sd)
2155 struct berval **values;
2156 prs_struct ps;
2157 BOOL ret = False;
2159 values = ldap_get_values_len(ads->ld, msg, field);
2161 if (!values) return False;
2163 if (values[0]) {
2164 prs_init(&ps, values[0]->bv_len, mem_ctx, UNMARSHALL);
2165 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
2166 prs_set_offset(&ps,0);
2168 ret = sec_io_desc("sd", sd, &ps, 1);
2171 ldap_value_free_len(values);
2172 return ret;
2176 * in order to support usernames longer than 21 characters we need to
2177 * use both the sAMAccountName and the userPrincipalName attributes
2178 * It seems that not all users have the userPrincipalName attribute set
2180 * @param ads connection to ads server
2181 * @param mem_ctx TALLOC_CTX for allocating sid array
2182 * @param msg Results of search
2183 * @return the username
2185 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, void *msg)
2187 char *ret, *p;
2189 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2190 if (ret && (p = strchr(ret, '@'))) {
2191 *p = 0;
2192 return ret;
2194 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2199 * find the update serial number - this is the core of the ldap cache
2200 * @param ads connection to ads server
2201 * @param ads connection to ADS server
2202 * @param usn Pointer to retrieved update serial number
2203 * @return status of search
2205 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2207 const char *attrs[] = {"highestCommittedUSN", NULL};
2208 ADS_STATUS status;
2209 void *res;
2211 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2212 if (!ADS_ERR_OK(status))
2213 return status;
2215 if (ads_count_replies(ads, res) != 1) {
2216 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2219 ads_pull_uint32(ads, res, "highestCommittedUSN", usn);
2220 ads_msgfree(ads, res);
2221 return ADS_SUCCESS;
2224 /* parse a ADS timestring - typical string is
2225 '20020917091222.0Z0' which means 09:12.22 17th September
2226 2002, timezone 0 */
2227 static time_t ads_parse_time(const char *str)
2229 struct tm tm;
2231 ZERO_STRUCT(tm);
2233 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2234 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2235 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2236 return 0;
2238 tm.tm_year -= 1900;
2239 tm.tm_mon -= 1;
2241 return timegm(&tm);
2246 * Find the servers name and realm - this can be done before authentication
2247 * The ldapServiceName field on w2k looks like this:
2248 * vnet3.home.samba.org:win2000-vnet3$@VNET3.HOME.SAMBA.ORG
2249 * @param ads connection to ads server
2250 * @return status of search
2252 ADS_STATUS ads_server_info(ADS_STRUCT *ads)
2254 const char *attrs[] = {"ldapServiceName", "currentTime", NULL};
2255 ADS_STATUS status;
2256 void *res;
2257 char *value;
2258 char *p;
2259 char *timestr;
2260 TALLOC_CTX *ctx;
2262 if (!(ctx = talloc_init("ads_server_info"))) {
2263 return ADS_ERROR(LDAP_NO_MEMORY);
2266 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2267 if (!ADS_ERR_OK(status)) {
2268 talloc_destroy(ctx);
2269 return status;
2272 value = ads_pull_string(ads, ctx, res, "ldapServiceName");
2273 if (!value) {
2274 ads_msgfree(ads, res);
2275 talloc_destroy(ctx);
2276 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2279 timestr = ads_pull_string(ads, ctx, res, "currentTime");
2280 if (!timestr) {
2281 ads_msgfree(ads, res);
2282 talloc_destroy(ctx);
2283 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2286 ads_msgfree(ads, res);
2288 p = strchr(value, ':');
2289 if (!p) {
2290 talloc_destroy(ctx);
2291 DEBUG(1, ("ads_server_info: returned ldap server name did not contain a ':' "
2292 "so was deemed invalid\n"));
2293 return ADS_ERROR(LDAP_DECODING_ERROR);
2296 SAFE_FREE(ads->config.ldap_server_name);
2298 ads->config.ldap_server_name = strdup(p+1);
2299 p = strchr(ads->config.ldap_server_name, '$');
2300 if (!p || p[1] != '@') {
2301 talloc_destroy(ctx);
2302 DEBUG(1, ("ads_server_info: returned ldap server name (%s) does not contain '$@'"
2303 " so was deemed invalid\n", ads->config.ldap_server_name));
2304 SAFE_FREE(ads->config.ldap_server_name);
2305 return ADS_ERROR(LDAP_DECODING_ERROR);
2308 *p = 0;
2310 SAFE_FREE(ads->config.realm);
2311 SAFE_FREE(ads->config.bind_path);
2313 ads->config.realm = strdup(p+2);
2314 ads->config.bind_path = ads_build_dn(ads->config.realm);
2316 DEBUG(3,("got ldap server name %s@%s, using bind path: %s\n",
2317 ads->config.ldap_server_name, ads->config.realm,
2318 ads->config.bind_path));
2320 ads->config.current_time = ads_parse_time(timestr);
2322 if (ads->config.current_time != 0) {
2323 ads->auth.time_offset = ads->config.current_time - time(NULL);
2324 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2327 talloc_destroy(ctx);
2329 return ADS_SUCCESS;
2333 * find the domain sid for our domain
2334 * @param ads connection to ads server
2335 * @param sid Pointer to domain sid
2336 * @return status of search
2338 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2340 const char *attrs[] = {"objectSid", NULL};
2341 void *res;
2342 ADS_STATUS rc;
2344 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2345 attrs, &res);
2346 if (!ADS_ERR_OK(rc)) return rc;
2347 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2348 ads_msgfree(ads, res);
2349 return ADS_ERROR_SYSTEM(ENOENT);
2351 ads_msgfree(ads, res);
2353 return ADS_SUCCESS;
2356 /* this is rather complex - we need to find the allternate (netbios) name
2357 for the domain, but there isn't a simple query to do this. Instead
2358 we look for the principle names on the DCs account and find one that has
2359 the right form, then extract the netbios name of the domain from that
2361 NOTE! better method is this:
2363 bin/net -Uadministrator%XXXXX ads search '(&(objectclass=crossref)(dnsroot=VNET3.HOME.SAMBA.ORG))' nETBIOSName
2365 but you need to force the bind path to match the configurationNamingContext from the rootDSE
2368 ADS_STATUS ads_workgroup_name(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **workgroup)
2370 char *expr;
2371 ADS_STATUS rc;
2372 char **principles;
2373 char *prefix;
2374 int prefix_length;
2375 int i;
2376 void *res;
2377 const char *attrs[] = {"servicePrincipalName", NULL};
2378 int num_principals;
2380 (*workgroup) = NULL;
2382 asprintf(&expr, "(&(objectclass=computer)(dnshostname=%s.%s))",
2383 ads->config.ldap_server_name, ads->config.realm);
2384 rc = ads_search(ads, &res, expr, attrs);
2385 free(expr);
2387 if (!ADS_ERR_OK(rc)) {
2388 return rc;
2391 principles = ads_pull_strings(ads, mem_ctx, res,
2392 "servicePrincipalName", &num_principals);
2394 ads_msgfree(ads, res);
2396 if (!principles) {
2397 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2400 asprintf(&prefix, "HOST/%s.%s/",
2401 ads->config.ldap_server_name,
2402 ads->config.realm);
2404 prefix_length = strlen(prefix);
2406 for (i=0;principles[i]; i++) {
2407 if (strnequal(principles[i], prefix, prefix_length) &&
2408 !strequal(ads->config.realm, principles[i]+prefix_length) &&
2409 !strchr(principles[i]+prefix_length, '.')) {
2410 /* found an alternate (short) name for the domain. */
2411 DEBUG(3,("Found alternate name '%s' for realm '%s'\n",
2412 principles[i]+prefix_length,
2413 ads->config.realm));
2414 (*workgroup) = talloc_strdup(mem_ctx, principles[i]+prefix_length);
2415 break;
2418 free(prefix);
2420 if (!*workgroup) {
2421 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2424 return ADS_SUCCESS;
2427 #endif