r4231: commiting changes to 3.0.10
[Samba.git] / source / libads / ldap.c
blob541472ffd27f2ce3f32af9f68446f38b831fa1c5
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 = SMB_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 = SMB_STRDUP(ads->config.realm);
268 if (!ads->auth.kdc_server) {
269 ads->auth.kdc_server = SMB_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_P(ctx, 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++)
328 ; /* count values */
329 values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
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++)
348 ; /* count values */
349 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
350 if (!values) return NULL;
352 for (i=0; in_vals[i]; i++) {
353 push_utf8_talloc(ctx, &values[i], in_vals[i]);
355 return values;
359 Pull a (char *) array out of a UTF8-encoded values list
361 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
363 char **values;
364 int i;
366 if (!in_vals) return NULL;
367 for (i=0; in_vals[i]; i++)
368 ; /* count values */
369 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
370 if (!values) return NULL;
372 for (i=0; in_vals[i]; i++) {
373 pull_utf8_talloc(ctx, &values[i], in_vals[i]);
375 return values;
379 * Do a search with paged results. cookie must be null on the first
380 * call, and then returned on each subsequent call. It will be null
381 * again when the entire search is complete
382 * @param ads connection to ads server
383 * @param bind_path Base dn for the search
384 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
385 * @param expr Search expression - specified in local charset
386 * @param attrs Attributes to retrieve - specified in utf8 or ascii
387 * @param res ** which will contain results - free res* with ads_msgfree()
388 * @param count Number of entries retrieved on this page
389 * @param cookie The paged results cookie to be returned on subsequent calls
390 * @return status of search
392 ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
393 int scope, const char *expr,
394 const char **attrs, void **res,
395 int *count, void **cookie)
397 int rc, i, version;
398 char *utf8_expr, *utf8_path, **search_attrs;
399 LDAPControl PagedResults, NoReferrals, *controls[3], **rcontrols;
400 BerElement *cookie_be = NULL;
401 struct berval *cookie_bv= NULL;
402 TALLOC_CTX *ctx;
404 *res = NULL;
406 if (!(ctx = talloc_init("ads_do_paged_search")))
407 return ADS_ERROR(LDAP_NO_MEMORY);
409 /* 0 means the conversion worked but the result was empty
410 so we only fail if it's -1. In any case, it always
411 at least nulls out the dest */
412 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
413 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
414 rc = LDAP_NO_MEMORY;
415 goto done;
418 if (!attrs || !(*attrs))
419 search_attrs = NULL;
420 else {
421 /* This would be the utf8-encoded version...*/
422 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
423 if (!(str_list_copy(&search_attrs, attrs))) {
424 rc = LDAP_NO_MEMORY;
425 goto done;
430 /* Paged results only available on ldap v3 or later */
431 ldap_get_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
432 if (version < LDAP_VERSION3) {
433 rc = LDAP_NOT_SUPPORTED;
434 goto done;
437 cookie_be = ber_alloc_t(LBER_USE_DER);
438 if (cookie && *cookie) {
439 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
440 ber_bvfree(*cookie); /* don't need it from last time */
441 *cookie = NULL;
442 } else {
443 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
445 ber_flatten(cookie_be, &cookie_bv);
446 PagedResults.ldctl_oid = ADS_PAGE_CTL_OID;
447 PagedResults.ldctl_iscritical = (char) 1;
448 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
449 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
451 NoReferrals.ldctl_oid = ADS_NO_REFERRALS_OID;
452 NoReferrals.ldctl_iscritical = (char) 0;
453 NoReferrals.ldctl_value.bv_len = 0;
454 NoReferrals.ldctl_value.bv_val = "";
457 controls[0] = &NoReferrals;
458 controls[1] = &PagedResults;
459 controls[2] = 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 *res = NULL;
537 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, res,
538 &count, &cookie);
540 if (!ADS_ERR_OK(status)) return status;
542 while (cookie) {
543 void *res2 = NULL;
544 ADS_STATUS status2;
545 LDAPMessage *msg, *next;
547 status2 = ads_do_paged_search(ads, bind_path, scope, expr,
548 attrs, &res2, &count, &cookie);
550 if (!ADS_ERR_OK(status2)) break;
552 /* this relies on the way that ldap_add_result_entry() works internally. I hope
553 that this works on all ldap libs, but I have only tested with openldap */
554 for (msg = ads_first_entry(ads, res2); msg; msg = next) {
555 next = ads_next_entry(ads, msg);
556 ldap_add_result_entry((LDAPMessage **)res, msg);
558 /* note that we do not free res2, as the memory is now
559 part of the main returned list */
562 return status;
566 * Run a function on all results for a search. Uses ads_do_paged_search() and
567 * runs the function as each page is returned, using ads_process_results()
568 * @param ads connection to ads server
569 * @param bind_path Base dn for the search
570 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
571 * @param expr Search expression - specified in local charset
572 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
573 * @param fn Function which takes attr name, values list, and data_area
574 * @param data_area Pointer which is passed to function on each call
575 * @return status of search
577 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
578 int scope, const char *expr, const char **attrs,
579 BOOL(*fn)(char *, void **, void *),
580 void *data_area)
582 void *cookie = NULL;
583 int count = 0;
584 ADS_STATUS status;
585 void *res;
587 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
588 &count, &cookie);
590 if (!ADS_ERR_OK(status)) return status;
592 ads_process_results(ads, res, fn, data_area);
593 ads_msgfree(ads, res);
595 while (cookie) {
596 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
597 &res, &count, &cookie);
599 if (!ADS_ERR_OK(status)) break;
601 ads_process_results(ads, res, fn, data_area);
602 ads_msgfree(ads, res);
605 return status;
609 * Do a search with a timeout.
610 * @param ads connection to ads server
611 * @param bind_path Base dn for the search
612 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
613 * @param expr Search expression
614 * @param attrs Attributes to retrieve
615 * @param res ** which will contain results - free res* with ads_msgfree()
616 * @return status of search
618 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
619 const char *expr,
620 const char **attrs, void **res)
622 struct timeval timeout;
623 int rc;
624 char *utf8_expr, *utf8_path, **search_attrs = NULL;
625 TALLOC_CTX *ctx;
627 *res = NULL;
628 if (!(ctx = talloc_init("ads_do_search"))) {
629 DEBUG(1,("ads_do_search: talloc_init() failed!"));
630 return ADS_ERROR(LDAP_NO_MEMORY);
633 /* 0 means the conversion worked but the result was empty
634 so we only fail if it's negative. In any case, it always
635 at least nulls out the dest */
636 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
637 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
638 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
639 rc = LDAP_NO_MEMORY;
640 goto done;
643 if (!attrs || !(*attrs))
644 search_attrs = NULL;
645 else {
646 /* This would be the utf8-encoded version...*/
647 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
648 if (!(str_list_copy(&search_attrs, attrs)))
650 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
651 rc = LDAP_NO_MEMORY;
652 goto done;
656 timeout.tv_sec = ADS_SEARCH_TIMEOUT;
657 timeout.tv_usec = 0;
659 /* see the note in ads_do_paged_search - we *must* disable referrals */
660 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
662 rc = ldap_search_ext_s(ads->ld, utf8_path, scope, utf8_expr,
663 search_attrs, 0, NULL, NULL,
664 &timeout, LDAP_NO_LIMIT, (LDAPMessage **)res);
666 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
667 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
668 rc = 0;
671 done:
672 talloc_destroy(ctx);
673 /* if/when we decide to utf8-encode attrs, take out this next line */
674 str_list_free(&search_attrs);
675 return ADS_ERROR(rc);
678 * Do a general ADS search
679 * @param ads connection to ads server
680 * @param res ** which will contain results - free res* with ads_msgfree()
681 * @param expr Search expression
682 * @param attrs Attributes to retrieve
683 * @return status of search
685 ADS_STATUS ads_search(ADS_STRUCT *ads, void **res,
686 const char *expr,
687 const char **attrs)
689 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
690 expr, attrs, res);
694 * Do a search on a specific DistinguishedName
695 * @param ads connection to ads server
696 * @param res ** which will contain results - free res* with ads_msgfree()
697 * @param dn DistinguishName to search
698 * @param attrs Attributes to retrieve
699 * @return status of search
701 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, void **res,
702 const char *dn,
703 const char **attrs)
705 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)", attrs, res);
709 * Free up memory from a ads_search
710 * @param ads connection to ads server
711 * @param msg Search results to free
713 void ads_msgfree(ADS_STRUCT *ads, void *msg)
715 if (!msg) return;
716 ldap_msgfree(msg);
720 * Free up memory from various ads requests
721 * @param ads connection to ads server
722 * @param mem Area to free
724 void ads_memfree(ADS_STRUCT *ads, void *mem)
726 SAFE_FREE(mem);
730 * Get a dn from search results
731 * @param ads connection to ads server
732 * @param msg Search result
733 * @return dn string
735 char *ads_get_dn(ADS_STRUCT *ads, void *msg)
737 char *utf8_dn, *unix_dn;
739 utf8_dn = ldap_get_dn(ads->ld, msg);
741 if (!utf8_dn) {
742 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
743 return NULL;
746 if (pull_utf8_allocate(&unix_dn, utf8_dn) == (size_t)-1) {
747 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
748 utf8_dn ));
749 return NULL;
751 ldap_memfree(utf8_dn);
752 return unix_dn;
756 * Find a machine account given a hostname
757 * @param ads connection to ads server
758 * @param res ** which will contain results - free res* with ads_msgfree()
759 * @param host Hostname to search for
760 * @return status of search
762 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, void **res, const char *machine)
764 ADS_STATUS status;
765 char *expr;
766 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
768 *res = NULL;
770 /* the easiest way to find a machine account anywhere in the tree
771 is to look for hostname$ */
772 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
773 DEBUG(1, ("asprintf failed!\n"));
774 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
777 status = ads_search(ads, res, expr, attrs);
778 SAFE_FREE(expr);
779 return status;
783 * Initialize a list of mods to be used in a modify request
784 * @param ctx An initialized TALLOC_CTX
785 * @return allocated ADS_MODLIST
787 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
789 #define ADS_MODLIST_ALLOC_SIZE 10
790 LDAPMod **mods;
792 if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
793 /* -1 is safety to make sure we don't go over the end.
794 need to reset it to NULL before doing ldap modify */
795 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
797 return mods;
802 add an attribute to the list, with values list already constructed
804 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
805 int mod_op, const char *name,
806 const void **invals)
808 int curmod;
809 LDAPMod **modlist = (LDAPMod **) *mods;
810 struct berval **ber_values = NULL;
811 char **char_values = NULL;
813 if (!invals) {
814 mod_op = LDAP_MOD_DELETE;
815 } else {
816 if (mod_op & LDAP_MOD_BVALUES)
817 ber_values = ads_dup_values(ctx,
818 (const struct berval **)invals);
819 else
820 char_values = ads_push_strvals(ctx,
821 (const char **) invals);
824 /* find the first empty slot */
825 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
826 curmod++);
827 if (modlist[curmod] == (LDAPMod *) -1) {
828 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
829 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
830 return ADS_ERROR(LDAP_NO_MEMORY);
831 memset(&modlist[curmod], 0,
832 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
833 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
834 *mods = modlist;
837 if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
838 return ADS_ERROR(LDAP_NO_MEMORY);
839 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
840 if (mod_op & LDAP_MOD_BVALUES) {
841 modlist[curmod]->mod_bvalues = ber_values;
842 } else if (mod_op & LDAP_MOD_DELETE) {
843 modlist[curmod]->mod_values = NULL;
844 } else {
845 modlist[curmod]->mod_values = char_values;
848 modlist[curmod]->mod_op = mod_op;
849 return ADS_ERROR(LDAP_SUCCESS);
853 * Add a single string value to a mod list
854 * @param ctx An initialized TALLOC_CTX
855 * @param mods An initialized ADS_MODLIST
856 * @param name The attribute name to add
857 * @param val The value to add - NULL means DELETE
858 * @return ADS STATUS indicating success of add
860 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
861 const char *name, const char *val)
863 const char *values[2];
865 values[0] = val;
866 values[1] = NULL;
868 if (!val)
869 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
870 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name,
871 (const void **) values);
875 * Add an array of string values to a mod list
876 * @param ctx An initialized TALLOC_CTX
877 * @param mods An initialized ADS_MODLIST
878 * @param name The attribute name to add
879 * @param vals The array of string values to add - NULL means DELETE
880 * @return ADS STATUS indicating success of add
882 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
883 const char *name, const char **vals)
885 if (!vals)
886 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
887 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
888 name, (const void **) vals);
892 * Add a single ber-encoded value to a mod list
893 * @param ctx An initialized TALLOC_CTX
894 * @param mods An initialized ADS_MODLIST
895 * @param name The attribute name to add
896 * @param val The value to add - NULL means DELETE
897 * @return ADS STATUS indicating success of add
899 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
900 const char *name, const struct berval *val)
902 const struct berval *values[2];
904 values[0] = val;
905 values[1] = NULL;
906 if (!val)
907 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
908 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
909 name, (const void **) values);
913 * Perform an ldap modify
914 * @param ads connection to ads server
915 * @param mod_dn DistinguishedName to modify
916 * @param mods list of modifications to perform
917 * @return status of modify
919 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
921 int ret,i;
922 char *utf8_dn = NULL;
924 this control is needed to modify that contains a currently
925 non-existent attribute (but allowable for the object) to run
927 LDAPControl PermitModify = {
928 ADS_PERMIT_MODIFY_OID,
929 {0, NULL},
930 (char) 1};
931 LDAPControl *controls[2];
933 controls[0] = &PermitModify;
934 controls[1] = NULL;
936 if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) {
937 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
940 /* find the end of the list, marked by NULL or -1 */
941 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
942 /* make sure the end of the list is NULL */
943 mods[i] = NULL;
944 ret = ldap_modify_ext_s(ads->ld, utf8_dn,
945 (LDAPMod **) mods, controls, NULL);
946 SAFE_FREE(utf8_dn);
947 return ADS_ERROR(ret);
951 * Perform an ldap add
952 * @param ads connection to ads server
953 * @param new_dn DistinguishedName to add
954 * @param mods list of attributes and values for DN
955 * @return status of add
957 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
959 int ret, i;
960 char *utf8_dn = NULL;
962 if (push_utf8_allocate(&utf8_dn, new_dn) == -1) {
963 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
964 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
967 /* find the end of the list, marked by NULL or -1 */
968 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
969 /* make sure the end of the list is NULL */
970 mods[i] = NULL;
972 ret = ldap_add_s(ads->ld, utf8_dn, mods);
973 SAFE_FREE(utf8_dn);
974 return ADS_ERROR(ret);
978 * Delete a DistinguishedName
979 * @param ads connection to ads server
980 * @param new_dn DistinguishedName to delete
981 * @return status of delete
983 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
985 int ret;
986 char *utf8_dn = NULL;
987 if (push_utf8_allocate(&utf8_dn, del_dn) == -1) {
988 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
989 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
992 ret = ldap_delete_s(ads->ld, utf8_dn);
993 return ADS_ERROR(ret);
997 * Build an org unit string
998 * if org unit is Computers or blank then assume a container, otherwise
999 * assume a \ separated list of organisational units
1000 * @param ads connection to ads server
1001 * @param org_unit Organizational unit
1002 * @return org unit string - caller must free
1004 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1006 char *ret = NULL;
1008 if (!org_unit || !*org_unit) {
1010 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1012 /* samba4 might not yet respond to a wellknownobject-query */
1013 return ret ? ret : SMB_STRDUP("cn=Computers");
1016 if (strequal(org_unit, "Computers")) {
1017 return SMB_STRDUP("cn=Computers");
1020 return ads_build_path(org_unit, "\\/", "ou=", 1);
1024 * Get a org unit string for a well-known GUID
1025 * @param ads connection to ads server
1026 * @param wknguid Well known GUID
1027 * @return org unit string - caller must free
1029 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1031 ADS_STATUS status;
1032 void *res;
1033 char *base, *wkn_dn, *ret, **wkn_dn_exp, **bind_dn_exp;
1034 const char *attrs[] = {"distinguishedName", NULL};
1035 int new_ln, wkn_ln, bind_ln, i;
1037 if (wknguid == NULL) {
1038 return NULL;
1041 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1042 DEBUG(1, ("asprintf failed!\n"));
1043 return NULL;
1046 status = ads_search_dn(ads, &res, base, attrs);
1047 if (!ADS_ERR_OK(status)) {
1048 DEBUG(1,("Failed while searching for: %s\n", base));
1049 return NULL;
1051 free(base);
1053 if (ads_count_replies(ads, res) != 1) {
1054 return NULL;
1057 /* substitute the bind-path from the well-known-guid-search result */
1058 wkn_dn = ads_get_dn(ads, res);
1059 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1060 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1062 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1064 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1067 new_ln = wkn_ln - bind_ln;
1069 ret = wkn_dn_exp[0];
1071 for (i=1; i < new_ln; i++) {
1072 char *s;
1073 asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]);
1074 ret = SMB_STRDUP(s);
1075 free(s);
1078 return ret;
1082 * Adds (appends) an item to an attribute array, rather then
1083 * replacing the whole list
1084 * @param ctx An initialized TALLOC_CTX
1085 * @param mods An initialized ADS_MODLIST
1086 * @param name name of the ldap attribute to append to
1087 * @param vals an array of values to add
1088 * @return status of addition
1091 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1092 const char *name, const char **vals)
1094 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name, (const void **) vals);
1098 * Determines the computer account's current KVNO via an LDAP lookup
1099 * @param ads An initialized ADS_STRUCT
1100 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1101 * @return the kvno for the computer account, or -1 in case of a failure.
1104 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *machine_name)
1106 LDAPMessage *res = NULL;
1107 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1108 char *filter;
1109 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1110 char *dn_string = NULL;
1111 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1113 DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name));
1114 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1115 return kvno;
1117 ret = ads_search(ads, (void**) &res, filter, attrs);
1118 SAFE_FREE(filter);
1119 if (!ADS_ERR_OK(ret) && ads_count_replies(ads, res)) {
1120 DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name));
1121 ads_msgfree(ads, res);
1122 return kvno;
1125 dn_string = ads_get_dn(ads, res);
1126 if (!dn_string) {
1127 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1128 ads_msgfree(ads, res);
1129 return kvno;
1131 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1132 ads_memfree(ads, dn_string);
1134 /* ---------------------------------------------------------
1135 * 0 is returned as a default KVNO from this point on...
1136 * This is done because Windows 2000 does not support key
1137 * version numbers. Chances are that a failure in the next
1138 * step is simply due to Windows 2000 being used for a
1139 * domain controller. */
1140 kvno = 0;
1142 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1143 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1144 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1145 ads_msgfree(ads, res);
1146 return kvno;
1149 /* Success */
1150 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1151 ads_msgfree(ads, res);
1152 return kvno;
1156 * This clears out all registered spn's for a given hostname
1157 * @param ads An initilaized ADS_STRUCT
1158 * @param machine_name the NetBIOS name of the computer.
1159 * @return 0 upon success, non-zero otherwise.
1162 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1164 TALLOC_CTX *ctx;
1165 LDAPMessage *res = NULL;
1166 ADS_MODLIST mods;
1167 const char *servicePrincipalName[1] = {NULL};
1168 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1169 char *dn_string = NULL;
1171 ret = ads_find_machine_acct(ads, (void **)&res, machine_name);
1172 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1173 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1174 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1175 ads_msgfree(ads, res);
1176 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1179 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1180 ctx = talloc_init("ads_clear_service_principal_names");
1181 if (!ctx) {
1182 ads_msgfree(ads, res);
1183 return ADS_ERROR(LDAP_NO_MEMORY);
1186 if (!(mods = ads_init_mods(ctx))) {
1187 talloc_destroy(ctx);
1188 ads_msgfree(ads, res);
1189 return ADS_ERROR(LDAP_NO_MEMORY);
1191 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1192 if (!ADS_ERR_OK(ret)) {
1193 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1194 ads_msgfree(ads, res);
1195 talloc_destroy(ctx);
1196 return ret;
1198 dn_string = ads_get_dn(ads, res);
1199 if (!dn_string) {
1200 talloc_destroy(ctx);
1201 ads_msgfree(ads, res);
1202 return ADS_ERROR(LDAP_NO_MEMORY);
1204 ret = ads_gen_mod(ads, dn_string, mods);
1205 ads_memfree(ads,dn_string);
1206 if (!ADS_ERR_OK(ret)) {
1207 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1208 machine_name));
1209 ads_msgfree(ads, res);
1210 talloc_destroy(ctx);
1211 return ret;
1214 ads_msgfree(ads, res);
1215 talloc_destroy(ctx);
1216 return ret;
1220 * This adds a service principal name to an existing computer account
1221 * (found by hostname) in AD.
1222 * @param ads An initialized ADS_STRUCT
1223 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1224 * @param spn A string of the service principal to add, i.e. 'host'
1225 * @return 0 upon sucess, or non-zero if a failure occurs
1228 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name, const char *spn)
1230 ADS_STATUS ret;
1231 TALLOC_CTX *ctx;
1232 LDAPMessage *res = NULL;
1233 char *host_spn, *host_upn, *psp1, *psp2, *psp3;
1234 ADS_MODLIST mods;
1235 fstring my_fqdn;
1236 char *dn_string = NULL;
1237 const char *servicePrincipalName[4] = {NULL, NULL, NULL, NULL};
1239 ret = ads_find_machine_acct(ads, (void **)&res, machine_name);
1240 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1241 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1242 machine_name));
1243 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1244 spn, machine_name, ads->config.realm));
1245 ads_msgfree(ads, res);
1246 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1249 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1250 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1251 ads_msgfree(ads, res);
1252 return ADS_ERROR(LDAP_NO_MEMORY);
1255 name_to_fqdn(my_fqdn, machine_name);
1256 strlower_m(my_fqdn);
1258 if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", my_fqdn))) {
1259 talloc_destroy(ctx);
1260 ads_msgfree(ads, res);
1261 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1263 if (!(host_upn = talloc_asprintf(ctx, "%s@%s", host_spn, ads->config.realm))) {
1264 talloc_destroy(ctx);
1265 ads_msgfree(ads, res);
1266 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1269 /* Add the extra principal */
1270 psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name);
1271 strupper_m(psp1);
1272 strlower_m(&psp1[strlen(spn)]);
1273 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp1, machine_name));
1274 servicePrincipalName[0] = psp1;
1275 psp2 = talloc_asprintf(ctx, "%s/%s.%s", spn, machine_name, ads->config.realm);
1276 strupper_m(psp2);
1277 strlower_m(&psp2[strlen(spn)]);
1278 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp2, machine_name));
1279 servicePrincipalName[1] = psp2;
1281 /* Add another principal in case the realm != the DNS domain, so that
1282 * the KDC doesn't send "server principal unknown" errors to clients
1283 * which use the DNS name in determining service principal names. */
1284 psp3 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn);
1285 strupper_m(psp3);
1286 strlower_m(&psp3[strlen(spn)]);
1287 if (strcmp(psp2, psp3) != 0) {
1288 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp3, machine_name));
1289 servicePrincipalName[2] = psp3;
1292 if (!(mods = ads_init_mods(ctx))) {
1293 talloc_destroy(ctx);
1294 ads_msgfree(ads, res);
1295 return ADS_ERROR(LDAP_NO_MEMORY);
1297 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1298 if (!ADS_ERR_OK(ret)) {
1299 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1300 talloc_destroy(ctx);
1301 ads_msgfree(ads, res);
1302 return ret;
1304 dn_string = ads_get_dn(ads, res);
1305 if (!dn_string) {
1306 talloc_destroy(ctx);
1307 ads_msgfree(ads, res);
1308 return ADS_ERROR(LDAP_NO_MEMORY);
1310 ret = ads_gen_mod(ads, dn_string, mods);
1311 ads_memfree(ads,dn_string);
1312 if (!ADS_ERR_OK(ret)) {
1313 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1314 talloc_destroy(ctx);
1315 ads_msgfree(ads, res);
1316 return ret;
1319 talloc_destroy(ctx);
1320 ads_msgfree(ads, res);
1321 return ret;
1325 * adds a machine account to the ADS server
1326 * @param ads An intialized ADS_STRUCT
1327 * @param machine_name - the NetBIOS machine name of this account.
1328 * @param account_type A number indicating the type of account to create
1329 * @param org_unit The LDAP path in which to place this account
1330 * @return 0 upon success, or non-zero otherwise
1333 static ADS_STATUS ads_add_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1334 uint32 account_type,
1335 const char *org_unit)
1337 ADS_STATUS ret, status;
1338 char *host_spn, *host_upn, *new_dn, *samAccountName, *controlstr;
1339 TALLOC_CTX *ctx;
1340 ADS_MODLIST mods;
1341 const char *objectClass[] = {"top", "person", "organizationalPerson",
1342 "user", "computer", NULL};
1343 const char *servicePrincipalName[7] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
1344 char *psp, *psp2, *psp3, *psp4;
1345 unsigned acct_control;
1346 unsigned exists=0;
1347 fstring my_fqdn;
1348 LDAPMessage *res = NULL;
1349 int i, next_spn;
1351 if (!(ctx = talloc_init("ads_add_machine_acct")))
1352 return ADS_ERROR(LDAP_NO_MEMORY);
1354 ret = ADS_ERROR(LDAP_NO_MEMORY);
1356 name_to_fqdn(my_fqdn, machine_name);
1358 status = ads_find_machine_acct(ads, (void **)&res, machine_name);
1359 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1360 char *dn_string = ads_get_dn(ads, res);
1361 if (!dn_string) {
1362 DEBUG(1, ("ads_add_machine_acct: ads_get_dn returned NULL (malloc failure?)\n"));
1363 goto done;
1365 new_dn = talloc_strdup(ctx, dn_string);
1366 ads_memfree(ads,dn_string);
1367 DEBUG(0, ("ads_add_machine_acct: Host account for %s already exists - modifying old account\n",
1368 machine_name));
1369 exists=1;
1370 } else {
1371 char *ou_str = ads_ou_string(ads,org_unit);
1372 if (!ou_str) {
1373 DEBUG(1, ("ads_add_machine_acct: ads_ou_string returned NULL (malloc failure?)\n"));
1374 goto done;
1376 new_dn = talloc_asprintf(ctx, "cn=%s,%s,%s", machine_name, ou_str,
1377 ads->config.bind_path);
1379 SAFE_FREE(ou_str);
1382 if (!new_dn) {
1383 goto done;
1386 if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", machine_name)))
1387 goto done;
1388 if (!(host_upn = talloc_asprintf(ctx, "%s@%s", host_spn, ads->config.realm)))
1389 goto done;
1390 servicePrincipalName[0] = talloc_asprintf(ctx, "HOST/%s", machine_name);
1391 psp = talloc_asprintf(ctx, "HOST/%s.%s",
1392 machine_name,
1393 ads->config.realm);
1394 strlower_m(&psp[5]);
1395 servicePrincipalName[1] = psp;
1396 servicePrincipalName[2] = talloc_asprintf(ctx, "CIFS/%s", machine_name);
1397 psp2 = talloc_asprintf(ctx, "CIFS/%s.%s",
1398 machine_name,
1399 ads->config.realm);
1400 strlower_m(&psp2[5]);
1401 servicePrincipalName[3] = psp2;
1403 /* Ensure servicePrincipalName[4] and [5] are unique. */
1404 strlower_m(my_fqdn);
1405 psp3 = talloc_asprintf(ctx, "CIFS/%s", my_fqdn);
1406 strlower_m(&psp3[5]);
1408 next_spn = 4;
1409 for (i = 0; i < next_spn; i++) {
1410 if (strequal(servicePrincipalName[i], psp3))
1411 break;
1413 if (i == next_spn) {
1414 servicePrincipalName[next_spn++] = psp3;
1417 psp4 = talloc_asprintf(ctx, "HOST/%s", my_fqdn);
1418 strlower_m(&psp4[5]);
1419 for (i = 0; i < next_spn; i++) {
1420 if (strequal(servicePrincipalName[i], psp3))
1421 break;
1423 if (i == next_spn) {
1424 servicePrincipalName[next_spn++] = psp4;
1427 if (!(samAccountName = talloc_asprintf(ctx, "%s$", machine_name))) {
1428 goto done;
1431 acct_control = account_type | UF_DONT_EXPIRE_PASSWD;
1432 #ifndef ENCTYPE_ARCFOUR_HMAC
1433 acct_control |= UF_USE_DES_KEY_ONLY;
1434 #endif
1436 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
1437 goto done;
1440 if (!(mods = ads_init_mods(ctx))) {
1441 goto done;
1444 if (!exists) {
1445 ads_mod_str(ctx, &mods, "cn", machine_name);
1446 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1447 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1448 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1450 ads_mod_str(ctx, &mods, "dNSHostName", my_fqdn);
1451 ads_mod_str(ctx, &mods, "userPrincipalName", host_upn);
1452 ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1453 ads_mod_str(ctx, &mods, "operatingSystem", "Samba");
1454 ads_mod_str(ctx, &mods, "operatingSystemVersion", SAMBA_VERSION_STRING);
1456 if (!exists) {
1457 ret = ads_gen_add(ads, new_dn, mods);
1458 } else {
1459 ret = ads_gen_mod(ads, new_dn, mods);
1462 if (!ADS_ERR_OK(ret)) {
1463 goto done;
1466 /* Do not fail if we can't set security descriptor
1467 * it shouldn't be mandatory and probably we just
1468 * don't have enough rights to do it.
1470 if (!exists) {
1471 status = ads_set_machine_sd(ads, machine_name, new_dn);
1473 if (!ADS_ERR_OK(status)) {
1474 DEBUG(0, ("Warning: ads_set_machine_sd: %s\n",
1475 ads_errstr(status)));
1478 done:
1479 ads_msgfree(ads, res);
1480 talloc_destroy(ctx);
1481 return ret;
1485 dump a binary result from ldap
1487 static void dump_binary(const char *field, struct berval **values)
1489 int i, j;
1490 for (i=0; values[i]; i++) {
1491 printf("%s: ", field);
1492 for (j=0; j<values[i]->bv_len; j++) {
1493 printf("%02X", (unsigned char)values[i]->bv_val[j]);
1495 printf("\n");
1499 static void dump_guid(const char *field, struct berval **values)
1501 int i;
1502 UUID_FLAT guid;
1503 for (i=0; values[i]; i++) {
1504 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1505 printf("%s: %s\n", field,
1506 smb_uuid_string_static(smb_uuid_unpack_static(guid)));
1511 dump a sid result from ldap
1513 static void dump_sid(const char *field, struct berval **values)
1515 int i;
1516 for (i=0; values[i]; i++) {
1517 DOM_SID sid;
1518 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1519 printf("%s: %s\n", field, sid_string_static(&sid));
1524 dump ntSecurityDescriptor
1526 static void dump_sd(const char *filed, struct berval **values)
1528 prs_struct ps;
1530 SEC_DESC *psd = 0;
1531 TALLOC_CTX *ctx = 0;
1533 if (!(ctx = talloc_init("sec_io_desc")))
1534 return;
1536 /* prepare data */
1537 prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
1538 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
1539 prs_set_offset(&ps,0);
1541 /* parse secdesc */
1542 if (!sec_io_desc("sd", &psd, &ps, 1)) {
1543 prs_mem_free(&ps);
1544 talloc_destroy(ctx);
1545 return;
1547 if (psd) ads_disp_sd(psd);
1549 prs_mem_free(&ps);
1550 talloc_destroy(ctx);
1554 dump a string result from ldap
1556 static void dump_string(const char *field, char **values)
1558 int i;
1559 for (i=0; values[i]; i++) {
1560 printf("%s: %s\n", field, values[i]);
1565 dump a field from LDAP on stdout
1566 used for debugging
1569 static BOOL ads_dump_field(char *field, void **values, void *data_area)
1571 const struct {
1572 const char *name;
1573 BOOL string;
1574 void (*handler)(const char *, struct berval **);
1575 } handlers[] = {
1576 {"objectGUID", False, dump_guid},
1577 {"nTSecurityDescriptor", False, dump_sd},
1578 {"dnsRecord", False, dump_binary},
1579 {"objectSid", False, dump_sid},
1580 {"tokenGroups", False, dump_sid},
1581 {NULL, True, NULL}
1583 int i;
1585 if (!field) { /* must be end of an entry */
1586 printf("\n");
1587 return False;
1590 for (i=0; handlers[i].name; i++) {
1591 if (StrCaseCmp(handlers[i].name, field) == 0) {
1592 if (!values) /* first time, indicate string or not */
1593 return handlers[i].string;
1594 handlers[i].handler(field, (struct berval **) values);
1595 break;
1598 if (!handlers[i].name) {
1599 if (!values) /* first time, indicate string conversion */
1600 return True;
1601 dump_string(field, (char **)values);
1603 return False;
1607 * Dump a result from LDAP on stdout
1608 * used for debugging
1609 * @param ads connection to ads server
1610 * @param res Results to dump
1613 void ads_dump(ADS_STRUCT *ads, void *res)
1615 ads_process_results(ads, res, ads_dump_field, NULL);
1619 * Walk through results, calling a function for each entry found.
1620 * The function receives a field name, a berval * array of values,
1621 * and a data area passed through from the start. The function is
1622 * called once with null for field and values at the end of each
1623 * entry.
1624 * @param ads connection to ads server
1625 * @param res Results to process
1626 * @param fn Function for processing each result
1627 * @param data_area user-defined area to pass to function
1629 void ads_process_results(ADS_STRUCT *ads, void *res,
1630 BOOL(*fn)(char *, void **, void *),
1631 void *data_area)
1633 void *msg;
1634 TALLOC_CTX *ctx;
1636 if (!(ctx = talloc_init("ads_process_results")))
1637 return;
1639 for (msg = ads_first_entry(ads, res); msg;
1640 msg = ads_next_entry(ads, msg)) {
1641 char *utf8_field;
1642 BerElement *b;
1644 for (utf8_field=ldap_first_attribute(ads->ld,
1645 (LDAPMessage *)msg,&b);
1646 utf8_field;
1647 utf8_field=ldap_next_attribute(ads->ld,
1648 (LDAPMessage *)msg,b)) {
1649 struct berval **ber_vals;
1650 char **str_vals, **utf8_vals;
1651 char *field;
1652 BOOL string;
1654 pull_utf8_talloc(ctx, &field, utf8_field);
1655 string = fn(field, NULL, data_area);
1657 if (string) {
1658 utf8_vals = ldap_get_values(ads->ld,
1659 (LDAPMessage *)msg, field);
1660 str_vals = ads_pull_strvals(ctx,
1661 (const char **) utf8_vals);
1662 fn(field, (void **) str_vals, data_area);
1663 ldap_value_free(utf8_vals);
1664 } else {
1665 ber_vals = ldap_get_values_len(ads->ld,
1666 (LDAPMessage *)msg, field);
1667 fn(field, (void **) ber_vals, data_area);
1669 ldap_value_free_len(ber_vals);
1671 ldap_memfree(utf8_field);
1673 ber_free(b, 0);
1674 talloc_destroy_pool(ctx);
1675 fn(NULL, NULL, data_area); /* completed an entry */
1678 talloc_destroy(ctx);
1682 * count how many replies are in a LDAPMessage
1683 * @param ads connection to ads server
1684 * @param res Results to count
1685 * @return number of replies
1687 int ads_count_replies(ADS_STRUCT *ads, void *res)
1689 return ldap_count_entries(ads->ld, (LDAPMessage *)res);
1693 * Join a machine to a realm
1694 * Creates the machine account and sets the machine password
1695 * @param ads connection to ads server
1696 * @param machine name of host to add
1697 * @param org_unit Organizational unit to place machine in
1698 * @return status of join
1700 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
1701 uint32 account_type, const char *org_unit)
1703 ADS_STATUS status;
1704 LDAPMessage *res = NULL;
1705 char *machine;
1707 /* machine name must be lowercase */
1708 machine = SMB_STRDUP(machine_name);
1709 strlower_m(machine);
1712 status = ads_find_machine_acct(ads, (void **)&res, machine);
1713 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1714 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
1715 status = ads_leave_realm(ads, machine);
1716 if (!ADS_ERR_OK(status)) {
1717 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
1718 machine, ads->config.realm));
1719 return status;
1724 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
1725 if (!ADS_ERR_OK(status)) {
1726 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
1727 SAFE_FREE(machine);
1728 return status;
1731 status = ads_find_machine_acct(ads, (void **)&res, machine);
1732 if (!ADS_ERR_OK(status)) {
1733 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
1734 SAFE_FREE(machine);
1735 return status;
1738 SAFE_FREE(machine);
1739 ads_msgfree(ads, res);
1741 return status;
1745 * Delete a machine from the realm
1746 * @param ads connection to ads server
1747 * @param hostname Machine to remove
1748 * @return status of delete
1750 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
1752 ADS_STATUS status;
1753 void *res, *msg;
1754 char *hostnameDN, *host;
1755 int rc;
1757 /* hostname must be lowercase */
1758 host = SMB_STRDUP(hostname);
1759 strlower_m(host);
1761 status = ads_find_machine_acct(ads, &res, host);
1762 if (!ADS_ERR_OK(status)) {
1763 DEBUG(0, ("Host account for %s does not exist.\n", host));
1764 return status;
1767 msg = ads_first_entry(ads, res);
1768 if (!msg) {
1769 return ADS_ERROR_SYSTEM(ENOENT);
1772 hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
1773 rc = ldap_delete_s(ads->ld, hostnameDN);
1774 ads_memfree(ads, hostnameDN);
1775 if (rc != LDAP_SUCCESS) {
1776 return ADS_ERROR(rc);
1779 status = ads_find_machine_acct(ads, &res, host);
1780 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1781 DEBUG(0, ("Failed to remove host account.\n"));
1782 return status;
1785 free(host);
1787 return status;
1791 * add machine account to existing security descriptor
1792 * @param ads connection to ads server
1793 * @param hostname machine to add
1794 * @param dn DN of security descriptor
1795 * @return status
1797 ADS_STATUS ads_set_machine_sd(ADS_STRUCT *ads, const char *hostname, char *dn)
1799 const char *attrs[] = {"nTSecurityDescriptor", "objectSid", 0};
1800 char *expr = 0;
1801 size_t sd_size = 0;
1802 struct berval bval = {0, NULL};
1803 prs_struct ps_wire;
1804 char *escaped_hostname = escape_ldap_string_alloc(hostname);
1806 LDAPMessage *res = 0;
1807 LDAPMessage *msg = 0;
1808 ADS_MODLIST mods = 0;
1810 NTSTATUS status;
1811 ADS_STATUS ret;
1812 DOM_SID sid;
1813 SEC_DESC *psd = NULL;
1814 TALLOC_CTX *ctx = NULL;
1816 /* Avoid segmentation fault in prs_mem_free if
1817 * we have to bail out before prs_init */
1818 ps_wire.is_dynamic = False;
1820 if (!ads) return ADS_ERROR(LDAP_SERVER_DOWN);
1822 ret = ADS_ERROR(LDAP_SUCCESS);
1824 if (!escaped_hostname) {
1825 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1828 if (asprintf(&expr, "(samAccountName=%s$)", escaped_hostname) == -1) {
1829 DEBUG(1, ("ads_set_machine_sd: asprintf failed!\n"));
1830 SAFE_FREE(escaped_hostname);
1831 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1834 SAFE_FREE(escaped_hostname);
1836 ret = ads_search(ads, (void *) &res, expr, attrs);
1838 if (!ADS_ERR_OK(ret)) return ret;
1840 if ( !(msg = ads_first_entry(ads, res) )) {
1841 ret = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1842 goto ads_set_sd_error;
1845 if (!ads_pull_sid(ads, msg, attrs[1], &sid)) {
1846 ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
1847 goto ads_set_sd_error;
1850 if (!(ctx = talloc_init("sec_io_desc"))) {
1851 ret = ADS_ERROR(LDAP_NO_MEMORY);
1852 goto ads_set_sd_error;
1855 if (!ads_pull_sd(ads, ctx, msg, attrs[0], &psd)) {
1856 ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
1857 goto ads_set_sd_error;
1860 status = sec_desc_add_sid(ctx, &psd, &sid, SEC_RIGHTS_FULL_CTRL, &sd_size);
1862 if (!NT_STATUS_IS_OK(status)) {
1863 ret = ADS_ERROR_NT(status);
1864 goto ads_set_sd_error;
1867 if (!prs_init(&ps_wire, sd_size, ctx, MARSHALL)) {
1868 ret = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1871 if (!sec_io_desc("sd_wire", &psd, &ps_wire, 1)) {
1872 ret = ADS_ERROR(LDAP_NO_MEMORY);
1873 goto ads_set_sd_error;
1876 #if 0
1877 file_save("/tmp/sec_desc.new", ps_wire.data_p, sd_size);
1878 #endif
1879 if (!(mods = ads_init_mods(ctx))) return ADS_ERROR(LDAP_NO_MEMORY);
1881 bval.bv_len = prs_offset(&ps_wire);
1882 bval.bv_val = TALLOC(ctx, bval.bv_len);
1883 if (!bval.bv_val) {
1884 ret = ADS_ERROR(LDAP_NO_MEMORY);
1885 goto ads_set_sd_error;
1888 prs_set_offset(&ps_wire, 0);
1890 if (!prs_copy_data_out(bval.bv_val, &ps_wire, bval.bv_len)) {
1891 ret = ADS_ERROR(LDAP_NO_MEMORY);
1892 goto ads_set_sd_error;
1895 ret = ads_mod_ber(ctx, &mods, attrs[0], &bval);
1896 if (ADS_ERR_OK(ret)) {
1897 ret = ads_gen_mod(ads, dn, mods);
1900 ads_set_sd_error:
1901 ads_msgfree(ads, res);
1902 prs_mem_free(&ps_wire);
1903 talloc_destroy(ctx);
1904 return ret;
1908 * pull the first entry from a ADS result
1909 * @param ads connection to ads server
1910 * @param res Results of search
1911 * @return first entry from result
1913 void *ads_first_entry(ADS_STRUCT *ads, void *res)
1915 return (void *)ldap_first_entry(ads->ld, (LDAPMessage *)res);
1919 * pull the next entry from a ADS result
1920 * @param ads connection to ads server
1921 * @param res Results of search
1922 * @return next entry from result
1924 void *ads_next_entry(ADS_STRUCT *ads, void *res)
1926 return (void *)ldap_next_entry(ads->ld, (LDAPMessage *)res);
1930 * pull a single string from a ADS result
1931 * @param ads connection to ads server
1932 * @param mem_ctx TALLOC_CTX to use for allocating result string
1933 * @param msg Results of search
1934 * @param field Attribute to retrieve
1935 * @return Result string in talloc context
1937 char *ads_pull_string(ADS_STRUCT *ads,
1938 TALLOC_CTX *mem_ctx, void *msg, const char *field)
1940 char **values;
1941 char *ret = NULL;
1942 char *ux_string;
1943 size_t rc;
1945 values = ldap_get_values(ads->ld, msg, field);
1946 if (!values)
1947 return NULL;
1949 if (values[0]) {
1950 rc = pull_utf8_talloc(mem_ctx, &ux_string,
1951 values[0]);
1952 if (rc != (size_t)-1)
1953 ret = ux_string;
1956 ldap_value_free(values);
1957 return ret;
1961 * pull an array of strings from a ADS result
1962 * @param ads connection to ads server
1963 * @param mem_ctx TALLOC_CTX to use for allocating result string
1964 * @param msg Results of search
1965 * @param field Attribute to retrieve
1966 * @return Result strings in talloc context
1968 char **ads_pull_strings(ADS_STRUCT *ads,
1969 TALLOC_CTX *mem_ctx, void *msg, const char *field,
1970 size_t *num_values)
1972 char **values;
1973 char **ret = NULL;
1974 int i;
1976 values = ldap_get_values(ads->ld, msg, field);
1977 if (!values)
1978 return NULL;
1980 *num_values = ldap_count_values(values);
1982 ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
1983 if (!ret) {
1984 ldap_value_free(values);
1985 return NULL;
1988 for (i=0;i<*num_values;i++) {
1989 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
1990 ldap_value_free(values);
1991 return NULL;
1994 ret[i] = NULL;
1996 ldap_value_free(values);
1997 return ret;
2001 * pull an array of strings from a ADS result
2002 * (handle large multivalue attributes with range retrieval)
2003 * @param ads connection to ads server
2004 * @param mem_ctx TALLOC_CTX to use for allocating result string
2005 * @param msg Results of search
2006 * @param field Attribute to retrieve
2007 * @param current_strings strings returned by a previous call to this function
2008 * @param next_attribute The next query should ask for this attribute
2009 * @param num_values How many values did we get this time?
2010 * @param more_values Are there more values to get?
2011 * @return Result strings in talloc context
2013 char **ads_pull_strings_range(ADS_STRUCT *ads,
2014 TALLOC_CTX *mem_ctx,
2015 void *msg, const char *field,
2016 char **current_strings,
2017 const char **next_attribute,
2018 size_t *num_strings,
2019 BOOL *more_strings)
2021 char *attr;
2022 char *expected_range_attrib, *range_attr;
2023 BerElement *ptr = NULL;
2024 char **strings;
2025 char **new_strings;
2026 size_t num_new_strings;
2027 unsigned long int range_start;
2028 unsigned long int range_end;
2030 /* we might have been given the whole lot anyway */
2031 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2032 *more_strings = False;
2033 return strings;
2036 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2038 /* look for Range result */
2039 for (attr = ldap_first_attribute(ads->ld, (LDAPMessage *)msg, &ptr);
2040 attr;
2041 attr = ldap_next_attribute(ads->ld, (LDAPMessage *)msg, ptr)) {
2042 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2043 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2044 range_attr = attr;
2045 break;
2047 ldap_memfree(attr);
2049 if (!attr) {
2050 ber_free(ptr, 0);
2051 /* nothing here - this field is just empty */
2052 *more_strings = False;
2053 return NULL;
2056 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2057 &range_start, &range_end) == 2) {
2058 *more_strings = True;
2059 } else {
2060 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2061 &range_start) == 1) {
2062 *more_strings = False;
2063 } else {
2064 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2065 range_attr));
2066 ldap_memfree(range_attr);
2067 *more_strings = False;
2068 return NULL;
2072 if ((*num_strings) != range_start) {
2073 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2074 " - aborting range retreival\n",
2075 range_attr, *num_strings + 1, range_start));
2076 ldap_memfree(range_attr);
2077 *more_strings = False;
2078 return NULL;
2081 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2083 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2084 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2085 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2086 range_attr, (unsigned long int)range_end - range_start + 1,
2087 (unsigned long int)num_new_strings));
2088 ldap_memfree(range_attr);
2089 *more_strings = False;
2090 return NULL;
2093 strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2094 *num_strings + num_new_strings);
2096 if (strings == NULL) {
2097 ldap_memfree(range_attr);
2098 *more_strings = False;
2099 return NULL;
2102 memcpy(&strings[*num_strings], new_strings,
2103 sizeof(*new_strings) * num_new_strings);
2105 (*num_strings) += num_new_strings;
2107 if (*more_strings) {
2108 *next_attribute = talloc_asprintf(mem_ctx,
2109 "%s;range=%d-*",
2110 field,
2111 *num_strings);
2113 if (!*next_attribute) {
2114 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2115 ldap_memfree(range_attr);
2116 *more_strings = False;
2117 return NULL;
2121 ldap_memfree(range_attr);
2123 return strings;
2127 * pull a single uint32 from a ADS result
2128 * @param ads connection to ads server
2129 * @param msg Results of search
2130 * @param field Attribute to retrieve
2131 * @param v Pointer to int to store result
2132 * @return boolean inidicating success
2134 BOOL ads_pull_uint32(ADS_STRUCT *ads,
2135 void *msg, const char *field, uint32 *v)
2137 char **values;
2139 values = ldap_get_values(ads->ld, msg, field);
2140 if (!values)
2141 return False;
2142 if (!values[0]) {
2143 ldap_value_free(values);
2144 return False;
2147 *v = atoi(values[0]);
2148 ldap_value_free(values);
2149 return True;
2153 * pull a single objectGUID from an ADS result
2154 * @param ads connection to ADS server
2155 * @param msg results of search
2156 * @param guid 37-byte area to receive text guid
2157 * @return boolean indicating success
2159 BOOL ads_pull_guid(ADS_STRUCT *ads,
2160 void *msg, struct uuid *guid)
2162 char **values;
2163 UUID_FLAT flat_guid;
2165 values = ldap_get_values(ads->ld, msg, "objectGUID");
2166 if (!values)
2167 return False;
2169 if (values[0]) {
2170 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2171 smb_uuid_unpack(flat_guid, guid);
2172 ldap_value_free(values);
2173 return True;
2175 ldap_value_free(values);
2176 return False;
2182 * pull a single DOM_SID from a ADS result
2183 * @param ads connection to ads server
2184 * @param msg Results of search
2185 * @param field Attribute to retrieve
2186 * @param sid Pointer to sid to store result
2187 * @return boolean inidicating success
2189 BOOL ads_pull_sid(ADS_STRUCT *ads,
2190 void *msg, const char *field, DOM_SID *sid)
2192 struct berval **values;
2193 BOOL ret = False;
2195 values = ldap_get_values_len(ads->ld, msg, field);
2197 if (!values)
2198 return False;
2200 if (values[0])
2201 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2203 ldap_value_free_len(values);
2204 return ret;
2208 * pull an array of DOM_SIDs from a ADS result
2209 * @param ads connection to ads server
2210 * @param mem_ctx TALLOC_CTX for allocating sid array
2211 * @param msg Results of search
2212 * @param field Attribute to retrieve
2213 * @param sids pointer to sid array to allocate
2214 * @return the count of SIDs pulled
2216 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2217 void *msg, const char *field, DOM_SID **sids)
2219 struct berval **values;
2220 BOOL ret;
2221 int count, i;
2223 values = ldap_get_values_len(ads->ld, msg, field);
2225 if (!values)
2226 return 0;
2228 for (i=0; values[i]; i++)
2229 /* nop */ ;
2231 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2232 if (!(*sids)) {
2233 ldap_value_free_len(values);
2234 return 0;
2237 count = 0;
2238 for (i=0; values[i]; i++) {
2239 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2240 if (ret) {
2241 fstring sid;
2242 DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid, &(*sids)[count])));
2243 count++;
2247 ldap_value_free_len(values);
2248 return count;
2252 * pull a SEC_DESC from a ADS result
2253 * @param ads connection to ads server
2254 * @param mem_ctx TALLOC_CTX for allocating sid array
2255 * @param msg Results of search
2256 * @param field Attribute to retrieve
2257 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2258 * @return boolean inidicating success
2260 BOOL ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2261 void *msg, const char *field, SEC_DESC **sd)
2263 struct berval **values;
2264 prs_struct ps;
2265 BOOL ret = False;
2267 values = ldap_get_values_len(ads->ld, msg, field);
2269 if (!values) return False;
2271 if (values[0]) {
2272 prs_init(&ps, values[0]->bv_len, mem_ctx, UNMARSHALL);
2273 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
2274 prs_set_offset(&ps,0);
2276 ret = sec_io_desc("sd", sd, &ps, 1);
2279 ldap_value_free_len(values);
2280 return ret;
2284 * in order to support usernames longer than 21 characters we need to
2285 * use both the sAMAccountName and the userPrincipalName attributes
2286 * It seems that not all users have the userPrincipalName attribute set
2288 * @param ads connection to ads server
2289 * @param mem_ctx TALLOC_CTX for allocating sid array
2290 * @param msg Results of search
2291 * @return the username
2293 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, void *msg)
2295 #if 0 /* JERRY */
2296 char *ret, *p;
2298 /* lookup_name() only works on the sAMAccountName to
2299 returning the username portion of userPrincipalName
2300 breaks winbindd_getpwnam() */
2302 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2303 if (ret && (p = strchr_m(ret, '@'))) {
2304 *p = 0;
2305 return ret;
2307 #endif
2308 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2313 * find the update serial number - this is the core of the ldap cache
2314 * @param ads connection to ads server
2315 * @param ads connection to ADS server
2316 * @param usn Pointer to retrieved update serial number
2317 * @return status of search
2319 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2321 const char *attrs[] = {"highestCommittedUSN", NULL};
2322 ADS_STATUS status;
2323 void *res;
2325 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2326 if (!ADS_ERR_OK(status))
2327 return status;
2329 if (ads_count_replies(ads, res) != 1) {
2330 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2333 ads_pull_uint32(ads, res, "highestCommittedUSN", usn);
2334 ads_msgfree(ads, res);
2335 return ADS_SUCCESS;
2338 /* parse a ADS timestring - typical string is
2339 '20020917091222.0Z0' which means 09:12.22 17th September
2340 2002, timezone 0 */
2341 static time_t ads_parse_time(const char *str)
2343 struct tm tm;
2345 ZERO_STRUCT(tm);
2347 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2348 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2349 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2350 return 0;
2352 tm.tm_year -= 1900;
2353 tm.tm_mon -= 1;
2355 return timegm(&tm);
2360 * Find the servers name and realm - this can be done before authentication
2361 * The ldapServiceName field on w2k looks like this:
2362 * vnet3.home.samba.org:win2000-vnet3$@VNET3.HOME.SAMBA.ORG
2363 * @param ads connection to ads server
2364 * @return status of search
2366 ADS_STATUS ads_server_info(ADS_STRUCT *ads)
2368 const char *attrs[] = {"ldapServiceName", "currentTime", NULL};
2369 ADS_STATUS status;
2370 void *res;
2371 char *value;
2372 char *p;
2373 char *timestr;
2374 TALLOC_CTX *ctx;
2376 if (!(ctx = talloc_init("ads_server_info"))) {
2377 return ADS_ERROR(LDAP_NO_MEMORY);
2380 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2381 if (!ADS_ERR_OK(status)) {
2382 talloc_destroy(ctx);
2383 return status;
2386 value = ads_pull_string(ads, ctx, res, "ldapServiceName");
2387 if (!value) {
2388 ads_msgfree(ads, res);
2389 talloc_destroy(ctx);
2390 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2393 timestr = ads_pull_string(ads, ctx, res, "currentTime");
2394 if (!timestr) {
2395 ads_msgfree(ads, res);
2396 talloc_destroy(ctx);
2397 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2400 ads_msgfree(ads, res);
2402 p = strchr(value, ':');
2403 if (!p) {
2404 talloc_destroy(ctx);
2405 DEBUG(1, ("ads_server_info: returned ldap server name did not contain a ':' "
2406 "so was deemed invalid\n"));
2407 return ADS_ERROR(LDAP_DECODING_ERROR);
2410 SAFE_FREE(ads->config.ldap_server_name);
2412 ads->config.ldap_server_name = SMB_STRDUP(p+1);
2413 p = strchr(ads->config.ldap_server_name, '$');
2414 if (!p || p[1] != '@') {
2415 talloc_destroy(ctx);
2416 DEBUG(1, ("ads_server_info: returned ldap server name (%s) does not contain '$@'"
2417 " so was deemed invalid\n", ads->config.ldap_server_name));
2418 SAFE_FREE(ads->config.ldap_server_name);
2419 return ADS_ERROR(LDAP_DECODING_ERROR);
2422 *p = 0;
2424 SAFE_FREE(ads->config.realm);
2425 SAFE_FREE(ads->config.bind_path);
2427 ads->config.realm = SMB_STRDUP(p+2);
2428 ads->config.bind_path = ads_build_dn(ads->config.realm);
2430 DEBUG(3,("got ldap server name %s@%s, using bind path: %s\n",
2431 ads->config.ldap_server_name, ads->config.realm,
2432 ads->config.bind_path));
2434 ads->config.current_time = ads_parse_time(timestr);
2436 if (ads->config.current_time != 0) {
2437 ads->auth.time_offset = ads->config.current_time - time(NULL);
2438 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2441 talloc_destroy(ctx);
2443 return ADS_SUCCESS;
2447 * find the domain sid for our domain
2448 * @param ads connection to ads server
2449 * @param sid Pointer to domain sid
2450 * @return status of search
2452 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2454 const char *attrs[] = {"objectSid", NULL};
2455 void *res;
2456 ADS_STATUS rc;
2458 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2459 attrs, &res);
2460 if (!ADS_ERR_OK(rc)) return rc;
2461 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2462 ads_msgfree(ads, res);
2463 return ADS_ERROR_SYSTEM(ENOENT);
2465 ads_msgfree(ads, res);
2467 return ADS_SUCCESS;
2470 /* this is rather complex - we need to find the allternate (netbios) name
2471 for the domain, but there isn't a simple query to do this. Instead
2472 we look for the principle names on the DCs account and find one that has
2473 the right form, then extract the netbios name of the domain from that
2475 NOTE! better method is this:
2477 bin/net -Uadministrator%XXXXX ads search '(&(objectclass=crossref)(dnsroot=VNET3.HOME.SAMBA.ORG))' nETBIOSName
2479 but you need to force the bind path to match the configurationNamingContext from the rootDSE
2482 ADS_STATUS ads_workgroup_name(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **workgroup)
2484 char *expr;
2485 ADS_STATUS rc;
2486 char **principles;
2487 char *prefix;
2488 int prefix_length;
2489 int i;
2490 void *res;
2491 const char *attrs[] = {"servicePrincipalName", NULL};
2492 int num_principals;
2494 (*workgroup) = NULL;
2496 asprintf(&expr, "(&(objectclass=computer)(dnshostname=%s.%s))",
2497 ads->config.ldap_server_name, ads->config.realm);
2498 if (expr == NULL) {
2499 ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2502 rc = ads_search(ads, &res, expr, attrs);
2503 free(expr);
2505 if (!ADS_ERR_OK(rc)) {
2506 return rc;
2509 principles = ads_pull_strings(ads, mem_ctx, res,
2510 "servicePrincipalName", &num_principals);
2512 ads_msgfree(ads, res);
2514 if (!principles) {
2515 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2518 asprintf(&prefix, "HOST/%s.%s/",
2519 ads->config.ldap_server_name,
2520 ads->config.realm);
2522 prefix_length = strlen(prefix);
2524 for (i=0;principles[i]; i++) {
2525 if (strnequal(principles[i], prefix, prefix_length) &&
2526 !strequal(ads->config.realm, principles[i]+prefix_length) &&
2527 !strchr(principles[i]+prefix_length, '.')) {
2528 /* found an alternate (short) name for the domain. */
2529 DEBUG(3,("Found alternate name '%s' for realm '%s'\n",
2530 principles[i]+prefix_length,
2531 ads->config.realm));
2532 (*workgroup) = talloc_strdup(mem_ctx, principles[i]+prefix_length);
2533 break;
2536 free(prefix);
2538 if (!*workgroup) {
2539 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2542 return ADS_SUCCESS;
2545 #endif