connect to the right realm or domain for trusted AD domains
[Samba.git] / source / libads / ldap.c
blobcf6f9375f8821f775a460e993cfe80897e9d20f7
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 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 **/
42 try a connection to a given ldap server, returning True and setting the servers IP
43 in the ads struct if successful
45 TODO : add a negative connection cache in here leveraged off of the one
46 found in the rpc code. --jerry
48 static BOOL ads_try_connect(ADS_STRUCT *ads, const char *server, unsigned port)
50 char *srv;
52 if (!server || !*server) {
53 return False;
56 DEBUG(5,("ads_try_connect: trying ldap server '%s' port %u\n", server, port));
58 /* this copes with inet_ntoa brokenness */
59 srv = strdup(server);
61 ads->ld = ldap_open(srv, port);
62 if (!ads->ld) {
63 free(srv);
64 return False;
66 ads->ldap_port = port;
67 ads->ldap_ip = *interpret_addr2(srv);
68 free(srv);
70 return True;
74 try a connection to a given ldap server, based on URL, returning True if successful
76 static BOOL ads_try_connect_uri(ADS_STRUCT *ads)
78 #if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
79 DEBUG(5,("ads_try_connect: trying ldap server at URI '%s'\n",
80 ads->server.ldap_uri));
83 if (ldap_initialize((LDAP**)&(ads->ld), ads->server.ldap_uri) == LDAP_SUCCESS) {
84 return True;
86 DEBUG(0, ("ldap_initialize: %s\n", strerror(errno)));
88 #else
90 DEBUG(1, ("no URL support in LDAP libs!\n"));
91 #endif
93 return False;
96 /**********************************************************************
97 Try to find an AD dc using our internal name resolution routines
98 Try the realm first and then then workgroup name if netbios is not
99 disabled
100 **********************************************************************/
102 static BOOL ads_find_dc(ADS_STRUCT *ads)
104 const char *c_realm;
105 int count, i=0;
106 struct ip_service *ip_list;
107 pstring realm;
108 BOOL got_realm = False;
110 /* realm */
111 c_realm = ads->server.realm;
112 if (c_realm && *c_realm)
113 got_realm = True;
115 again:
116 /* we need to try once with the realm name and fallback to the
117 netbios domain name if we fail (if netbios has not been disabled */
119 if ( !got_realm && !lp_disable_netbios() ) {
120 c_realm = ads->server.workgroup;
121 if (!c_realm || !*c_realm) {
122 DEBUG(0,("ads_find_dc: no realm or workgroup! Was the structure initialized?\n"));
123 return False;
127 pstrcpy( realm, c_realm );
129 DEBUG(6,("ads_find_dc: looking for %s '%s'\n",
130 (got_realm ? "realm" : "domain"), realm));
132 if ( !get_sorted_dc_list(realm, &ip_list, &count, got_realm) ) {
133 /* fall back to netbios if we can */
134 if ( got_realm && !lp_disable_netbios() ) {
135 got_realm = False;
136 goto again;
139 return False;
142 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
143 for ( i=0; i<count; i++ ) {
144 /* since this is an ads conection request, default to LDAP_PORT is not set */
145 int port = (ip_list[i].port!=PORT_NONE) ? ip_list[i].port : LDAP_PORT;
146 fstring server;
148 fstrcpy( server, inet_ntoa(ip_list[i].ip) );
150 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
151 continue;
153 if ( ads_try_connect(ads, server, port) ) {
154 SAFE_FREE(ip_list);
155 return True;
158 /* keep track of failures */
159 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
162 SAFE_FREE(ip_list);
164 return False;
169 * Connect to the LDAP server
170 * @param ads Pointer to an existing ADS_STRUCT
171 * @return status of connection
173 ADS_STATUS ads_connect(ADS_STRUCT *ads)
175 int version = LDAP_VERSION3;
176 ADS_STATUS status;
178 ads->last_attempt = time(NULL);
179 ads->ld = NULL;
181 /* try with a URL based server */
183 if (ads->server.ldap_uri &&
184 ads_try_connect_uri(ads)) {
185 goto got_connection;
188 /* try with a user specified server */
189 if (ads->server.ldap_server &&
190 ads_try_connect(ads, ads->server.ldap_server, LDAP_PORT)) {
191 goto got_connection;
194 if (ads_find_dc(ads)) {
195 goto got_connection;
198 return ADS_ERROR_SYSTEM(errno?errno:ENOENT);
200 got_connection:
201 DEBUG(3,("Connected to LDAP server %s\n", inet_ntoa(ads->ldap_ip)));
203 status = ads_server_info(ads);
204 if (!ADS_ERR_OK(status)) {
205 DEBUG(1,("Failed to get ldap server info\n"));
206 return status;
209 ldap_set_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
211 if (!ads->auth.user_name) {
212 /* by default use the machine account */
213 fstring myname;
214 fstrcpy(myname, global_myname());
215 strlower_m(myname);
216 asprintf(&ads->auth.user_name, "HOST/%s", myname);
219 if (!ads->auth.realm) {
220 ads->auth.realm = strdup(ads->config.realm);
223 if (!ads->auth.kdc_server) {
224 ads->auth.kdc_server = strdup(inet_ntoa(ads->ldap_ip));
227 #if KRB5_DNS_HACK
228 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
229 to MIT kerberos to work (tridge) */
231 char *env;
232 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
233 setenv(env, ads->auth.kdc_server, 1);
234 free(env);
236 #endif
238 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
239 return ADS_SUCCESS;
242 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
243 return ADS_ERROR(ldap_simple_bind_s( ads->ld, NULL, NULL));
246 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
247 return ADS_ERROR(ldap_simple_bind_s( ads->ld, ads->auth.user_name, ads->auth.password));
250 return ads_sasl_bind(ads);
254 Duplicate a struct berval into talloc'ed memory
256 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
258 struct berval *value;
260 if (!in_val) return NULL;
262 value = talloc_zero(ctx, sizeof(struct berval));
263 if (value == NULL)
264 return NULL;
265 if (in_val->bv_len == 0) return value;
267 value->bv_len = in_val->bv_len;
268 value->bv_val = talloc_memdup(ctx, in_val->bv_val, in_val->bv_len);
269 return value;
273 Make a values list out of an array of (struct berval *)
275 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
276 const struct berval **in_vals)
278 struct berval **values;
279 int i;
281 if (!in_vals) return NULL;
282 for (i=0; in_vals[i]; i++); /* count values */
283 values = (struct berval **) talloc_zero(ctx,
284 (i+1)*sizeof(struct berval *));
285 if (!values) return NULL;
287 for (i=0; in_vals[i]; i++) {
288 values[i] = dup_berval(ctx, in_vals[i]);
290 return values;
294 UTF8-encode a values list out of an array of (char *)
296 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
298 char **values;
299 int i;
301 if (!in_vals) return NULL;
302 for (i=0; in_vals[i]; i++); /* count values */
303 values = (char ** ) talloc_zero(ctx, (i+1)*sizeof(char *));
304 if (!values) return NULL;
306 for (i=0; in_vals[i]; i++) {
307 push_utf8_talloc(ctx, &values[i], in_vals[i]);
309 return values;
313 Pull a (char *) array out of a UTF8-encoded values list
315 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
317 char **values;
318 int i;
320 if (!in_vals) return NULL;
321 for (i=0; in_vals[i]; i++); /* count values */
322 values = (char **) talloc_zero(ctx, (i+1)*sizeof(char *));
323 if (!values) return NULL;
325 for (i=0; in_vals[i]; i++) {
326 pull_utf8_talloc(ctx, &values[i], in_vals[i]);
328 return values;
332 * Do a search with paged results. cookie must be null on the first
333 * call, and then returned on each subsequent call. It will be null
334 * again when the entire search is complete
335 * @param ads connection to ads server
336 * @param bind_path Base dn for the search
337 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
338 * @param expr Search expression - specified in local charset
339 * @param attrs Attributes to retrieve - specified in utf8 or ascii
340 * @param res ** which will contain results - free res* with ads_msgfree()
341 * @param count Number of entries retrieved on this page
342 * @param cookie The paged results cookie to be returned on subsequent calls
343 * @return status of search
345 ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
346 int scope, const char *expr,
347 const char **attrs, void **res,
348 int *count, void **cookie)
350 int rc, i, version;
351 char *utf8_expr, *utf8_path, **search_attrs;
352 LDAPControl PagedResults, NoReferrals, *controls[3], **rcontrols;
353 BerElement *cookie_be = NULL;
354 struct berval *cookie_bv= NULL;
355 TALLOC_CTX *ctx;
357 *res = NULL;
359 if (!(ctx = talloc_init("ads_do_paged_search")))
360 return ADS_ERROR(LDAP_NO_MEMORY);
362 /* 0 means the conversion worked but the result was empty
363 so we only fail if it's -1. In any case, it always
364 at least nulls out the dest */
365 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
366 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
367 rc = LDAP_NO_MEMORY;
368 goto done;
371 if (!attrs || !(*attrs))
372 search_attrs = NULL;
373 else {
374 /* This would be the utf8-encoded version...*/
375 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
376 if (!(str_list_copy(&search_attrs, attrs))) {
377 rc = LDAP_NO_MEMORY;
378 goto done;
383 /* Paged results only available on ldap v3 or later */
384 ldap_get_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
385 if (version < LDAP_VERSION3) {
386 rc = LDAP_NOT_SUPPORTED;
387 goto done;
390 cookie_be = ber_alloc_t(LBER_USE_DER);
391 if (cookie && *cookie) {
392 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
393 ber_bvfree(*cookie); /* don't need it from last time */
394 *cookie = NULL;
395 } else {
396 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
398 ber_flatten(cookie_be, &cookie_bv);
399 PagedResults.ldctl_oid = ADS_PAGE_CTL_OID;
400 PagedResults.ldctl_iscritical = (char) 1;
401 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
402 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
404 NoReferrals.ldctl_oid = ADS_NO_REFERRALS_OID;
405 NoReferrals.ldctl_iscritical = (char) 0;
406 NoReferrals.ldctl_value.bv_len = 0;
407 NoReferrals.ldctl_value.bv_val = "";
410 controls[0] = &NoReferrals;
411 controls[1] = &PagedResults;
412 controls[2] = NULL;
414 *res = NULL;
416 /* we need to disable referrals as the openldap libs don't
417 handle them and paged results at the same time. Using them
418 together results in the result record containing the server
419 page control being removed from the result list (tridge/jmcd)
421 leaving this in despite the control that says don't generate
422 referrals, in case the server doesn't support it (jmcd)
424 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
426 rc = ldap_search_ext_s(ads->ld, utf8_path, scope, utf8_expr,
427 search_attrs, 0, controls,
428 NULL, NULL, LDAP_NO_LIMIT, (LDAPMessage **)res);
430 ber_free(cookie_be, 1);
431 ber_bvfree(cookie_bv);
433 if (rc) {
434 DEBUG(3,("ldap_search_ext_s(%s) -> %s\n", expr, ldap_err2string(rc)));
435 goto done;
438 rc = ldap_parse_result(ads->ld, *res, NULL, NULL, NULL,
439 NULL, &rcontrols, 0);
441 if (!rcontrols) {
442 goto done;
445 for (i=0; rcontrols[i]; i++) {
446 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
447 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
448 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
449 &cookie_bv);
450 /* the berval is the cookie, but must be freed when
451 it is all done */
452 if (cookie_bv->bv_len) /* still more to do */
453 *cookie=ber_bvdup(cookie_bv);
454 else
455 *cookie=NULL;
456 ber_bvfree(cookie_bv);
457 ber_free(cookie_be, 1);
458 break;
461 ldap_controls_free(rcontrols);
463 done:
464 talloc_destroy(ctx);
465 /* if/when we decide to utf8-encode attrs, take out this next line */
466 str_list_free(&search_attrs);
468 return ADS_ERROR(rc);
473 * Get all results for a search. This uses ads_do_paged_search() to return
474 * all entries in a large search.
475 * @param ads connection to ads server
476 * @param bind_path Base dn for the search
477 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
478 * @param expr Search expression
479 * @param attrs Attributes to retrieve
480 * @param res ** which will contain results - free res* with ads_msgfree()
481 * @return status of search
483 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
484 int scope, const char *expr,
485 const char **attrs, void **res)
487 void *cookie = NULL;
488 int count = 0;
489 ADS_STATUS status;
491 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, res,
492 &count, &cookie);
494 if (!ADS_ERR_OK(status)) return status;
496 while (cookie) {
497 void *res2 = NULL;
498 ADS_STATUS status2;
499 LDAPMessage *msg, *next;
501 status2 = ads_do_paged_search(ads, bind_path, scope, expr,
502 attrs, &res2, &count, &cookie);
504 if (!ADS_ERR_OK(status2)) break;
506 /* this relies on the way that ldap_add_result_entry() works internally. I hope
507 that this works on all ldap libs, but I have only tested with openldap */
508 for (msg = ads_first_entry(ads, res2); msg; msg = next) {
509 next = ads_next_entry(ads, msg);
510 ldap_add_result_entry((LDAPMessage **)res, msg);
512 /* note that we do not free res2, as the memory is now
513 part of the main returned list */
516 return status;
520 * Run a function on all results for a search. Uses ads_do_paged_search() and
521 * runs the function as each page is returned, using ads_process_results()
522 * @param ads connection to ads server
523 * @param bind_path Base dn for the search
524 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
525 * @param expr Search expression - specified in local charset
526 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
527 * @param fn Function which takes attr name, values list, and data_area
528 * @param data_area Pointer which is passed to function on each call
529 * @return status of search
531 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
532 int scope, const char *expr, const char **attrs,
533 BOOL(*fn)(char *, void **, void *),
534 void *data_area)
536 void *cookie = NULL;
537 int count = 0;
538 ADS_STATUS status;
539 void *res;
541 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
542 &count, &cookie);
544 if (!ADS_ERR_OK(status)) return status;
546 ads_process_results(ads, res, fn, data_area);
547 ads_msgfree(ads, res);
549 while (cookie) {
550 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
551 &res, &count, &cookie);
553 if (!ADS_ERR_OK(status)) break;
555 ads_process_results(ads, res, fn, data_area);
556 ads_msgfree(ads, res);
559 return status;
563 * Do a search with a timeout.
564 * @param ads connection to ads server
565 * @param bind_path Base dn for the search
566 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
567 * @param expr Search expression
568 * @param attrs Attributes to retrieve
569 * @param res ** which will contain results - free res* with ads_msgfree()
570 * @return status of search
572 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
573 const char *expr,
574 const char **attrs, void **res)
576 struct timeval timeout;
577 int rc;
578 char *utf8_expr, *utf8_path, **search_attrs = NULL;
579 TALLOC_CTX *ctx;
581 if (!(ctx = talloc_init("ads_do_search"))) {
582 DEBUG(1,("ads_do_search: talloc_init() failed!"));
583 return ADS_ERROR(LDAP_NO_MEMORY);
586 /* 0 means the conversion worked but the result was empty
587 so we only fail if it's negative. In any case, it always
588 at least nulls out the dest */
589 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
590 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
591 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
592 rc = LDAP_NO_MEMORY;
593 goto done;
596 if (!attrs || !(*attrs))
597 search_attrs = NULL;
598 else {
599 /* This would be the utf8-encoded version...*/
600 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
601 if (!(str_list_copy(&search_attrs, attrs)))
603 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
604 rc = LDAP_NO_MEMORY;
605 goto done;
609 timeout.tv_sec = ADS_SEARCH_TIMEOUT;
610 timeout.tv_usec = 0;
611 *res = NULL;
613 /* see the note in ads_do_paged_search - we *must* disable referrals */
614 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
616 rc = ldap_search_ext_s(ads->ld, utf8_path, scope, utf8_expr,
617 search_attrs, 0, NULL, NULL,
618 &timeout, LDAP_NO_LIMIT, (LDAPMessage **)res);
620 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
621 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
622 rc = 0;
625 done:
626 talloc_destroy(ctx);
627 /* if/when we decide to utf8-encode attrs, take out this next line */
628 str_list_free(&search_attrs);
629 return ADS_ERROR(rc);
632 * Do a general ADS search
633 * @param ads connection to ads server
634 * @param res ** which will contain results - free res* with ads_msgfree()
635 * @param expr Search expression
636 * @param attrs Attributes to retrieve
637 * @return status of search
639 ADS_STATUS ads_search(ADS_STRUCT *ads, void **res,
640 const char *expr,
641 const char **attrs)
643 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
644 expr, attrs, res);
648 * Do a search on a specific DistinguishedName
649 * @param ads connection to ads server
650 * @param res ** which will contain results - free res* with ads_msgfree()
651 * @param dn DistinguishName to search
652 * @param attrs Attributes to retrieve
653 * @return status of search
655 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, void **res,
656 const char *dn,
657 const char **attrs)
659 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)", attrs, res);
663 * Free up memory from a ads_search
664 * @param ads connection to ads server
665 * @param msg Search results to free
667 void ads_msgfree(ADS_STRUCT *ads, void *msg)
669 if (!msg) return;
670 ldap_msgfree(msg);
674 * Free up memory from various ads requests
675 * @param ads connection to ads server
676 * @param mem Area to free
678 void ads_memfree(ADS_STRUCT *ads, void *mem)
680 SAFE_FREE(mem);
684 * Get a dn from search results
685 * @param ads connection to ads server
686 * @param msg Search result
687 * @return dn string
689 char *ads_get_dn(ADS_STRUCT *ads, void *msg)
691 char *utf8_dn, *unix_dn;
693 utf8_dn = ldap_get_dn(ads->ld, msg);
695 pull_utf8_allocate((void **) &unix_dn, utf8_dn);
696 ldap_memfree(utf8_dn);
697 return unix_dn;
701 * Find a machine account given a hostname
702 * @param ads connection to ads server
703 * @param res ** which will contain results - free res* with ads_msgfree()
704 * @param host Hostname to search for
705 * @return status of search
707 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, void **res, const char *host)
709 ADS_STATUS status;
710 char *expr;
711 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
713 /* the easiest way to find a machine account anywhere in the tree
714 is to look for hostname$ */
715 if (asprintf(&expr, "(samAccountName=%s$)", host) == -1) {
716 DEBUG(1, ("asprintf failed!\n"));
717 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
720 status = ads_search(ads, res, expr, attrs);
721 free(expr);
722 return status;
726 * Initialize a list of mods to be used in a modify request
727 * @param ctx An initialized TALLOC_CTX
728 * @return allocated ADS_MODLIST
730 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
732 #define ADS_MODLIST_ALLOC_SIZE 10
733 LDAPMod **mods;
735 if ((mods = (LDAPMod **) talloc_zero(ctx, sizeof(LDAPMod *) *
736 (ADS_MODLIST_ALLOC_SIZE + 1))))
737 /* -1 is safety to make sure we don't go over the end.
738 need to reset it to NULL before doing ldap modify */
739 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
741 return mods;
746 add an attribute to the list, with values list already constructed
748 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
749 int mod_op, const char *name,
750 const void **invals)
752 int curmod;
753 LDAPMod **modlist = (LDAPMod **) *mods;
754 struct berval **ber_values = NULL;
755 char **char_values = NULL;
757 if (!invals) {
758 mod_op = LDAP_MOD_DELETE;
759 } else {
760 if (mod_op & LDAP_MOD_BVALUES)
761 ber_values = ads_dup_values(ctx,
762 (const struct berval **)invals);
763 else
764 char_values = ads_push_strvals(ctx,
765 (const char **) invals);
768 /* find the first empty slot */
769 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
770 curmod++);
771 if (modlist[curmod] == (LDAPMod *) -1) {
772 if (!(modlist = talloc_realloc(ctx, modlist,
773 (curmod+ADS_MODLIST_ALLOC_SIZE+1)*sizeof(LDAPMod *))))
774 return ADS_ERROR(LDAP_NO_MEMORY);
775 memset(&modlist[curmod], 0,
776 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
777 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
778 *mods = modlist;
781 if (!(modlist[curmod] = talloc_zero(ctx, sizeof(LDAPMod))))
782 return ADS_ERROR(LDAP_NO_MEMORY);
783 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
784 if (mod_op & LDAP_MOD_BVALUES) {
785 modlist[curmod]->mod_bvalues = ber_values;
786 } else if (mod_op & LDAP_MOD_DELETE) {
787 modlist[curmod]->mod_values = NULL;
788 } else {
789 modlist[curmod]->mod_values = char_values;
792 modlist[curmod]->mod_op = mod_op;
793 return ADS_ERROR(LDAP_SUCCESS);
797 * Add a single string value to a mod list
798 * @param ctx An initialized TALLOC_CTX
799 * @param mods An initialized ADS_MODLIST
800 * @param name The attribute name to add
801 * @param val The value to add - NULL means DELETE
802 * @return ADS STATUS indicating success of add
804 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
805 const char *name, const char *val)
807 const char *values[2];
809 values[0] = val;
810 values[1] = NULL;
812 if (!val)
813 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
814 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name,
815 (const void **) values);
819 * Add an array of string values to a mod list
820 * @param ctx An initialized TALLOC_CTX
821 * @param mods An initialized ADS_MODLIST
822 * @param name The attribute name to add
823 * @param vals The array of string values to add - NULL means DELETE
824 * @return ADS STATUS indicating success of add
826 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
827 const char *name, const char **vals)
829 if (!vals)
830 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
831 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
832 name, (const void **) vals);
836 * Add a single ber-encoded value to a mod list
837 * @param ctx An initialized TALLOC_CTX
838 * @param mods An initialized ADS_MODLIST
839 * @param name The attribute name to add
840 * @param val The value to add - NULL means DELETE
841 * @return ADS STATUS indicating success of add
843 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
844 const char *name, const struct berval *val)
846 const struct berval *values[2];
848 values[0] = val;
849 values[1] = NULL;
850 if (!val)
851 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
852 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
853 name, (const void **) values);
857 * Perform an ldap modify
858 * @param ads connection to ads server
859 * @param mod_dn DistinguishedName to modify
860 * @param mods list of modifications to perform
861 * @return status of modify
863 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
865 int ret,i;
866 char *utf8_dn = NULL;
868 this control is needed to modify that contains a currently
869 non-existent attribute (but allowable for the object) to run
871 LDAPControl PermitModify = {
872 ADS_PERMIT_MODIFY_OID,
873 {0, NULL},
874 (char) 1};
875 LDAPControl *controls[2];
877 controls[0] = &PermitModify;
878 controls[1] = NULL;
880 if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) {
881 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
884 /* find the end of the list, marked by NULL or -1 */
885 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
886 /* make sure the end of the list is NULL */
887 mods[i] = NULL;
888 ret = ldap_modify_ext_s(ads->ld, utf8_dn,
889 (LDAPMod **) mods, controls, NULL);
890 SAFE_FREE(utf8_dn);
891 return ADS_ERROR(ret);
895 * Perform an ldap add
896 * @param ads connection to ads server
897 * @param new_dn DistinguishedName to add
898 * @param mods list of attributes and values for DN
899 * @return status of add
901 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
903 int ret, i;
904 char *utf8_dn = NULL;
906 if (push_utf8_allocate(&utf8_dn, new_dn) == -1) {
907 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
908 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
911 /* find the end of the list, marked by NULL or -1 */
912 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
913 /* make sure the end of the list is NULL */
914 mods[i] = NULL;
916 ret = ldap_add_s(ads->ld, utf8_dn, mods);
917 SAFE_FREE(utf8_dn);
918 return ADS_ERROR(ret);
922 * Delete a DistinguishedName
923 * @param ads connection to ads server
924 * @param new_dn DistinguishedName to delete
925 * @return status of delete
927 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
929 int ret;
930 char *utf8_dn = NULL;
931 if (push_utf8_allocate(&utf8_dn, del_dn) == -1) {
932 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
933 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
936 ret = ldap_delete_s(ads->ld, utf8_dn);
937 return ADS_ERROR(ret);
941 * Build an org unit string
942 * if org unit is Computers or blank then assume a container, otherwise
943 * assume a \ separated list of organisational units
944 * @param org_unit Organizational unit
945 * @return org unit string - caller must free
947 char *ads_ou_string(const char *org_unit)
949 if (!org_unit || !*org_unit || strcasecmp(org_unit, "Computers") == 0) {
950 return strdup("cn=Computers");
953 return ads_build_path(org_unit, "\\/", "ou=", 1);
959 add a machine account to the ADS server
961 static ADS_STATUS ads_add_machine_acct(ADS_STRUCT *ads, const char *hostname,
962 uint32 account_type,
963 const char *org_unit)
965 ADS_STATUS ret, status;
966 char *host_spn, *host_upn, *new_dn, *samAccountName, *controlstr;
967 char *ou_str;
968 TALLOC_CTX *ctx;
969 ADS_MODLIST mods;
970 const char *objectClass[] = {"top", "person", "organizationalPerson",
971 "user", "computer", NULL};
972 const char *servicePrincipalName[5] = {NULL, NULL, NULL, NULL, NULL};
973 char *psp, *psp2;
974 unsigned acct_control;
976 if (!(ctx = talloc_init("machine_account")))
977 return ADS_ERROR(LDAP_NO_MEMORY);
979 ret = ADS_ERROR(LDAP_NO_MEMORY);
981 if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", hostname)))
982 goto done;
983 if (!(host_upn = talloc_asprintf(ctx, "%s@%s", host_spn, ads->config.realm)))
984 goto done;
985 ou_str = ads_ou_string(org_unit);
986 if (!ou_str) {
987 DEBUG(1, ("ads_ou_string returned NULL (malloc failure?)\n"));
988 goto done;
990 new_dn = talloc_asprintf(ctx, "cn=%s,%s,%s", hostname, ou_str,
991 ads->config.bind_path);
992 servicePrincipalName[0] = talloc_asprintf(ctx, "HOST/%s", hostname);
993 psp = talloc_asprintf(ctx, "HOST/%s.%s",
994 hostname,
995 ads->config.realm);
996 strlower_m(&psp[5]);
997 servicePrincipalName[1] = psp;
998 servicePrincipalName[2] = talloc_asprintf(ctx, "CIFS/%s", hostname);
999 psp2 = talloc_asprintf(ctx, "CIFS/%s.%s",
1000 hostname,
1001 ads->config.realm);
1002 strlower_m(&psp2[5]);
1003 servicePrincipalName[3] = psp2;
1005 free(ou_str);
1006 if (!new_dn)
1007 goto done;
1009 if (!(samAccountName = talloc_asprintf(ctx, "%s$", hostname)))
1010 goto done;
1012 acct_control = account_type | UF_DONT_EXPIRE_PASSWD;
1013 #ifndef ENCTYPE_ARCFOUR_HMAC
1014 acct_control |= UF_USE_DES_KEY_ONLY;
1015 #endif
1017 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control)))
1018 goto done;
1020 if (!(mods = ads_init_mods(ctx)))
1021 goto done;
1023 ads_mod_str(ctx, &mods, "cn", hostname);
1024 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1025 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1026 ads_mod_str(ctx, &mods, "userPrincipalName", host_upn);
1027 ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1028 ads_mod_str(ctx, &mods, "dNSHostName", hostname);
1029 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1030 ads_mod_str(ctx, &mods, "operatingSystem", "Samba");
1031 ads_mod_str(ctx, &mods, "operatingSystemVersion", VERSION);
1033 ret = ads_gen_add(ads, new_dn, mods);
1035 if (!ADS_ERR_OK(ret))
1036 goto done;
1038 /* Do not fail if we can't set security descriptor
1039 * it shouldn't be mandatory and probably we just
1040 * don't have enough rights to do it.
1042 status = ads_set_machine_sd(ads, hostname, new_dn);
1044 if (!ADS_ERR_OK(status)) {
1045 DEBUG(0, ("Warning: ads_set_machine_sd: %s\n",
1046 ads_errstr(status)));
1048 done:
1049 talloc_destroy(ctx);
1050 return ret;
1054 dump a binary result from ldap
1056 static void dump_binary(const char *field, struct berval **values)
1058 int i, j;
1059 for (i=0; values[i]; i++) {
1060 printf("%s: ", field);
1061 for (j=0; j<values[i]->bv_len; j++) {
1062 printf("%02X", (unsigned char)values[i]->bv_val[j]);
1064 printf("\n");
1068 struct uuid {
1069 uint32 i1;
1070 uint16 i2;
1071 uint16 i3;
1072 uint8 s[8];
1075 static void dump_guid(const char *field, struct berval **values)
1077 int i;
1078 GUID guid;
1079 for (i=0; values[i]; i++) {
1080 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1081 printf("%s: %s\n", field, smb_uuid_string_static(guid));
1086 dump a sid result from ldap
1088 static void dump_sid(const char *field, struct berval **values)
1090 int i;
1091 for (i=0; values[i]; i++) {
1092 DOM_SID sid;
1093 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1094 printf("%s: %s\n", field, sid_string_static(&sid));
1099 dump ntSecurityDescriptor
1101 static void dump_sd(const char *filed, struct berval **values)
1103 prs_struct ps;
1105 SEC_DESC *psd = 0;
1106 TALLOC_CTX *ctx = 0;
1108 if (!(ctx = talloc_init("sec_io_desc")))
1109 return;
1111 /* prepare data */
1112 prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
1113 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
1114 prs_set_offset(&ps,0);
1116 /* parse secdesc */
1117 if (!sec_io_desc("sd", &psd, &ps, 1)) {
1118 prs_mem_free(&ps);
1119 talloc_destroy(ctx);
1120 return;
1122 if (psd) ads_disp_sd(psd);
1124 prs_mem_free(&ps);
1125 talloc_destroy(ctx);
1129 dump a string result from ldap
1131 static void dump_string(const char *field, char **values)
1133 int i;
1134 for (i=0; values[i]; i++) {
1135 printf("%s: %s\n", field, values[i]);
1140 dump a field from LDAP on stdout
1141 used for debugging
1144 static BOOL ads_dump_field(char *field, void **values, void *data_area)
1146 const struct {
1147 const char *name;
1148 BOOL string;
1149 void (*handler)(const char *, struct berval **);
1150 } handlers[] = {
1151 {"objectGUID", False, dump_guid},
1152 {"nTSecurityDescriptor", False, dump_sd},
1153 {"dnsRecord", False, dump_binary},
1154 {"objectSid", False, dump_sid},
1155 {"tokenGroups", False, dump_sid},
1156 {NULL, True, NULL}
1158 int i;
1160 if (!field) { /* must be end of an entry */
1161 printf("\n");
1162 return False;
1165 for (i=0; handlers[i].name; i++) {
1166 if (StrCaseCmp(handlers[i].name, field) == 0) {
1167 if (!values) /* first time, indicate string or not */
1168 return handlers[i].string;
1169 handlers[i].handler(field, (struct berval **) values);
1170 break;
1173 if (!handlers[i].name) {
1174 if (!values) /* first time, indicate string conversion */
1175 return True;
1176 dump_string(field, (char **)values);
1178 return False;
1182 * Dump a result from LDAP on stdout
1183 * used for debugging
1184 * @param ads connection to ads server
1185 * @param res Results to dump
1188 void ads_dump(ADS_STRUCT *ads, void *res)
1190 ads_process_results(ads, res, ads_dump_field, NULL);
1194 * Walk through results, calling a function for each entry found.
1195 * The function receives a field name, a berval * array of values,
1196 * and a data area passed through from the start. The function is
1197 * called once with null for field and values at the end of each
1198 * entry.
1199 * @param ads connection to ads server
1200 * @param res Results to process
1201 * @param fn Function for processing each result
1202 * @param data_area user-defined area to pass to function
1204 void ads_process_results(ADS_STRUCT *ads, void *res,
1205 BOOL(*fn)(char *, void **, void *),
1206 void *data_area)
1208 void *msg;
1209 TALLOC_CTX *ctx;
1211 if (!(ctx = talloc_init("ads_process_results")))
1212 return;
1214 for (msg = ads_first_entry(ads, res); msg;
1215 msg = ads_next_entry(ads, msg)) {
1216 char *utf8_field;
1217 BerElement *b;
1219 for (utf8_field=ldap_first_attribute(ads->ld,
1220 (LDAPMessage *)msg,&b);
1221 utf8_field;
1222 utf8_field=ldap_next_attribute(ads->ld,
1223 (LDAPMessage *)msg,b)) {
1224 struct berval **ber_vals;
1225 char **str_vals, **utf8_vals;
1226 char *field;
1227 BOOL string;
1229 pull_utf8_talloc(ctx, &field, utf8_field);
1230 string = fn(field, NULL, data_area);
1232 if (string) {
1233 utf8_vals = ldap_get_values(ads->ld,
1234 (LDAPMessage *)msg, field);
1235 str_vals = ads_pull_strvals(ctx,
1236 (const char **) utf8_vals);
1237 fn(field, (void **) str_vals, data_area);
1238 ldap_value_free(utf8_vals);
1239 } else {
1240 ber_vals = ldap_get_values_len(ads->ld,
1241 (LDAPMessage *)msg, field);
1242 fn(field, (void **) ber_vals, data_area);
1244 ldap_value_free_len(ber_vals);
1246 ldap_memfree(utf8_field);
1248 ber_free(b, 0);
1249 talloc_destroy_pool(ctx);
1250 fn(NULL, NULL, data_area); /* completed an entry */
1253 talloc_destroy(ctx);
1257 * count how many replies are in a LDAPMessage
1258 * @param ads connection to ads server
1259 * @param res Results to count
1260 * @return number of replies
1262 int ads_count_replies(ADS_STRUCT *ads, void *res)
1264 return ldap_count_entries(ads->ld, (LDAPMessage *)res);
1268 * Join a machine to a realm
1269 * Creates the machine account and sets the machine password
1270 * @param ads connection to ads server
1271 * @param hostname name of host to add
1272 * @param org_unit Organizational unit to place machine in
1273 * @return status of join
1275 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *hostname,
1276 uint32 account_type, const char *org_unit)
1278 ADS_STATUS status;
1279 LDAPMessage *res;
1280 char *host;
1282 /* hostname must be lowercase */
1283 host = strdup(hostname);
1284 strlower_m(host);
1286 status = ads_find_machine_acct(ads, (void **)&res, host);
1287 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1288 DEBUG(0, ("Host account for %s already exists - deleting old account\n", host));
1289 status = ads_leave_realm(ads, host);
1290 if (!ADS_ERR_OK(status)) {
1291 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
1292 host, ads->config.realm));
1293 return status;
1297 status = ads_add_machine_acct(ads, host, account_type, org_unit);
1298 if (!ADS_ERR_OK(status)) {
1299 DEBUG(0, ("ads_add_machine_acct: %s\n", ads_errstr(status)));
1300 return status;
1303 status = ads_find_machine_acct(ads, (void **)&res, host);
1304 if (!ADS_ERR_OK(status)) {
1305 DEBUG(0, ("Host account test failed\n"));
1306 return status;
1309 free(host);
1311 return status;
1315 * Delete a machine from the realm
1316 * @param ads connection to ads server
1317 * @param hostname Machine to remove
1318 * @return status of delete
1320 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
1322 ADS_STATUS status;
1323 void *res, *msg;
1324 char *hostnameDN, *host;
1325 int rc;
1327 /* hostname must be lowercase */
1328 host = strdup(hostname);
1329 strlower_m(host);
1331 status = ads_find_machine_acct(ads, &res, host);
1332 if (!ADS_ERR_OK(status)) {
1333 DEBUG(0, ("Host account for %s does not exist.\n", host));
1334 return status;
1337 msg = ads_first_entry(ads, res);
1338 if (!msg) {
1339 return ADS_ERROR_SYSTEM(ENOENT);
1342 hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
1343 rc = ldap_delete_s(ads->ld, hostnameDN);
1344 ads_memfree(ads, hostnameDN);
1345 if (rc != LDAP_SUCCESS) {
1346 return ADS_ERROR(rc);
1349 status = ads_find_machine_acct(ads, &res, host);
1350 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1351 DEBUG(0, ("Failed to remove host account.\n"));
1352 return status;
1355 free(host);
1357 return status;
1361 * add machine account to existing security descriptor
1362 * @param ads connection to ads server
1363 * @param hostname machine to add
1364 * @param dn DN of security descriptor
1365 * @return status
1367 ADS_STATUS ads_set_machine_sd(ADS_STRUCT *ads, const char *hostname, char *dn)
1369 const char *attrs[] = {"nTSecurityDescriptor", "objectSid", 0};
1370 char *expr = 0;
1371 size_t sd_size = 0;
1372 struct berval bval = {0, NULL};
1373 prs_struct ps_wire;
1374 char *escaped_hostname = escape_ldap_string_alloc(hostname);
1376 LDAPMessage *res = 0;
1377 LDAPMessage *msg = 0;
1378 ADS_MODLIST mods = 0;
1380 NTSTATUS status;
1381 ADS_STATUS ret;
1382 DOM_SID sid;
1383 SEC_DESC *psd = NULL;
1384 TALLOC_CTX *ctx = NULL;
1386 /* Avoid segmentation fault in prs_mem_free if
1387 * we have to bail out before prs_init */
1388 ps_wire.is_dynamic = False;
1390 if (!ads) return ADS_ERROR(LDAP_SERVER_DOWN);
1392 ret = ADS_ERROR(LDAP_SUCCESS);
1394 if (!escaped_hostname) {
1395 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1398 if (asprintf(&expr, "(samAccountName=%s$)", escaped_hostname) == -1) {
1399 DEBUG(1, ("ads_set_machine_sd: asprintf failed!\n"));
1400 SAFE_FREE(escaped_hostname);
1401 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1404 SAFE_FREE(escaped_hostname);
1406 ret = ads_search(ads, (void *) &res, expr, attrs);
1408 if (!ADS_ERR_OK(ret)) return ret;
1410 if ( !(msg = ads_first_entry(ads, res) )) {
1411 ret = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1412 goto ads_set_sd_error;
1415 if (!ads_pull_sid(ads, msg, attrs[1], &sid)) {
1416 ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
1417 goto ads_set_sd_error;
1420 if (!(ctx = talloc_init("sec_io_desc"))) {
1421 ret = ADS_ERROR(LDAP_NO_MEMORY);
1422 goto ads_set_sd_error;
1425 if (!ads_pull_sd(ads, ctx, msg, attrs[0], &psd)) {
1426 ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
1427 goto ads_set_sd_error;
1430 status = sec_desc_add_sid(ctx, &psd, &sid, SEC_RIGHTS_FULL_CTRL, &sd_size);
1432 if (!NT_STATUS_IS_OK(status)) {
1433 ret = ADS_ERROR_NT(status);
1434 goto ads_set_sd_error;
1437 if (!prs_init(&ps_wire, sd_size, ctx, MARSHALL)) {
1438 ret = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1441 if (!sec_io_desc("sd_wire", &psd, &ps_wire, 1)) {
1442 ret = ADS_ERROR(LDAP_NO_MEMORY);
1443 goto ads_set_sd_error;
1446 #if 0
1447 file_save("/tmp/sec_desc.new", ps_wire.data_p, sd_size);
1448 #endif
1449 if (!(mods = ads_init_mods(ctx))) return ADS_ERROR(LDAP_NO_MEMORY);
1451 bval.bv_len = prs_offset(&ps_wire);
1452 bval.bv_val = talloc(ctx, bval.bv_len);
1453 if (!bval.bv_val) {
1454 ret = ADS_ERROR(LDAP_NO_MEMORY);
1455 goto ads_set_sd_error;
1458 prs_set_offset(&ps_wire, 0);
1460 if (!prs_copy_data_out(bval.bv_val, &ps_wire, bval.bv_len)) {
1461 ret = ADS_ERROR(LDAP_NO_MEMORY);
1462 goto ads_set_sd_error;
1465 ret = ads_mod_ber(ctx, &mods, attrs[0], &bval);
1466 if (ADS_ERR_OK(ret)) {
1467 ret = ads_gen_mod(ads, dn, mods);
1470 ads_set_sd_error:
1471 ads_msgfree(ads, res);
1472 prs_mem_free(&ps_wire);
1473 talloc_destroy(ctx);
1474 return ret;
1478 * pull the first entry from a ADS result
1479 * @param ads connection to ads server
1480 * @param res Results of search
1481 * @return first entry from result
1483 void *ads_first_entry(ADS_STRUCT *ads, void *res)
1485 return (void *)ldap_first_entry(ads->ld, (LDAPMessage *)res);
1489 * pull the next entry from a ADS result
1490 * @param ads connection to ads server
1491 * @param res Results of search
1492 * @return next entry from result
1494 void *ads_next_entry(ADS_STRUCT *ads, void *res)
1496 return (void *)ldap_next_entry(ads->ld, (LDAPMessage *)res);
1500 * pull a single string from a ADS result
1501 * @param ads connection to ads server
1502 * @param mem_ctx TALLOC_CTX to use for allocating result string
1503 * @param msg Results of search
1504 * @param field Attribute to retrieve
1505 * @return Result string in talloc context
1507 char *ads_pull_string(ADS_STRUCT *ads,
1508 TALLOC_CTX *mem_ctx, void *msg, const char *field)
1510 char **values;
1511 char *ret = NULL;
1512 char *ux_string;
1513 size_t rc;
1515 values = ldap_get_values(ads->ld, msg, field);
1516 if (!values)
1517 return NULL;
1519 if (values[0]) {
1520 rc = pull_utf8_talloc(mem_ctx, &ux_string,
1521 values[0]);
1522 if (rc != (size_t)-1)
1523 ret = ux_string;
1526 ldap_value_free(values);
1527 return ret;
1531 * pull an array of strings from a ADS result
1532 * @param ads connection to ads server
1533 * @param mem_ctx TALLOC_CTX to use for allocating result string
1534 * @param msg Results of search
1535 * @param field Attribute to retrieve
1536 * @return Result strings in talloc context
1538 char **ads_pull_strings(ADS_STRUCT *ads,
1539 TALLOC_CTX *mem_ctx, void *msg, const char *field)
1541 char **values;
1542 char **ret = NULL;
1543 int i, n;
1545 values = ldap_get_values(ads->ld, msg, field);
1546 if (!values)
1547 return NULL;
1549 for (i=0;values[i];i++)
1550 /* noop */ ;
1551 n = i;
1553 ret = talloc(mem_ctx, sizeof(char *) * (n+1));
1554 if (!ret) {
1555 ldap_value_free(values);
1556 return NULL;
1559 for (i=0;i<n;i++) {
1560 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
1561 ldap_value_free(values);
1562 return NULL;
1565 ret[i] = NULL;
1567 ldap_value_free(values);
1568 return ret;
1573 * pull a single uint32 from a ADS result
1574 * @param ads connection to ads server
1575 * @param msg Results of search
1576 * @param field Attribute to retrieve
1577 * @param v Pointer to int to store result
1578 * @return boolean inidicating success
1580 BOOL ads_pull_uint32(ADS_STRUCT *ads,
1581 void *msg, const char *field, uint32 *v)
1583 char **values;
1585 values = ldap_get_values(ads->ld, msg, field);
1586 if (!values)
1587 return False;
1588 if (!values[0]) {
1589 ldap_value_free(values);
1590 return False;
1593 *v = atoi(values[0]);
1594 ldap_value_free(values);
1595 return True;
1599 * pull a single objectGUID from an ADS result
1600 * @param ads connection to ADS server
1601 * @param msg results of search
1602 * @param guid 37-byte area to receive text guid
1603 * @return boolean indicating success
1605 BOOL ads_pull_guid(ADS_STRUCT *ads,
1606 void *msg, GUID *guid)
1608 char **values;
1610 values = ldap_get_values(ads->ld, msg, "objectGUID");
1611 if (!values)
1612 return False;
1614 if (values[0]) {
1615 memcpy(guid, values[0], sizeof(GUID));
1616 ldap_value_free(values);
1617 return True;
1619 ldap_value_free(values);
1620 return False;
1626 * pull a single DOM_SID from a ADS result
1627 * @param ads connection to ads server
1628 * @param msg Results of search
1629 * @param field Attribute to retrieve
1630 * @param sid Pointer to sid to store result
1631 * @return boolean inidicating success
1633 BOOL ads_pull_sid(ADS_STRUCT *ads,
1634 void *msg, const char *field, DOM_SID *sid)
1636 struct berval **values;
1637 BOOL ret = False;
1639 values = ldap_get_values_len(ads->ld, msg, field);
1641 if (!values)
1642 return False;
1644 if (values[0])
1645 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
1647 ldap_value_free_len(values);
1648 return ret;
1652 * pull an array of DOM_SIDs from a ADS result
1653 * @param ads connection to ads server
1654 * @param mem_ctx TALLOC_CTX for allocating sid array
1655 * @param msg Results of search
1656 * @param field Attribute to retrieve
1657 * @param sids pointer to sid array to allocate
1658 * @return the count of SIDs pulled
1660 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
1661 void *msg, const char *field, DOM_SID **sids)
1663 struct berval **values;
1664 BOOL ret;
1665 int count, i;
1667 values = ldap_get_values_len(ads->ld, msg, field);
1669 if (!values)
1670 return 0;
1672 for (i=0; values[i]; i++)
1673 /* nop */ ;
1675 (*sids) = talloc(mem_ctx, sizeof(DOM_SID) * i);
1676 if (!(*sids)) {
1677 ldap_value_free_len(values);
1678 return 0;
1681 count = 0;
1682 for (i=0; values[i]; i++) {
1683 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
1684 if (ret) {
1685 fstring sid;
1686 DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid, &(*sids)[count])));
1687 count++;
1691 ldap_value_free_len(values);
1692 return count;
1696 * pull a SEC_DESC from a ADS result
1697 * @param ads connection to ads server
1698 * @param mem_ctx TALLOC_CTX for allocating sid array
1699 * @param msg Results of search
1700 * @param field Attribute to retrieve
1701 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
1702 * @return boolean inidicating success
1704 BOOL ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
1705 void *msg, const char *field, SEC_DESC **sd)
1707 struct berval **values;
1708 prs_struct ps;
1709 BOOL ret = False;
1711 values = ldap_get_values_len(ads->ld, msg, field);
1713 if (!values) return False;
1715 if (values[0]) {
1716 prs_init(&ps, values[0]->bv_len, mem_ctx, UNMARSHALL);
1717 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
1718 prs_set_offset(&ps,0);
1720 ret = sec_io_desc("sd", sd, &ps, 1);
1723 ldap_value_free_len(values);
1724 return ret;
1728 * in order to support usernames longer than 21 characters we need to
1729 * use both the sAMAccountName and the userPrincipalName attributes
1730 * It seems that not all users have the userPrincipalName attribute set
1732 * @param ads connection to ads server
1733 * @param mem_ctx TALLOC_CTX for allocating sid array
1734 * @param msg Results of search
1735 * @return the username
1737 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, void *msg)
1739 char *ret, *p;
1741 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
1742 if (ret && (p = strchr(ret, '@'))) {
1743 *p = 0;
1744 return ret;
1746 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
1751 * find the update serial number - this is the core of the ldap cache
1752 * @param ads connection to ads server
1753 * @param ads connection to ADS server
1754 * @param usn Pointer to retrieved update serial number
1755 * @return status of search
1757 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
1759 const char *attrs[] = {"highestCommittedUSN", NULL};
1760 ADS_STATUS status;
1761 void *res;
1763 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
1764 if (!ADS_ERR_OK(status)) return status;
1766 if (ads_count_replies(ads, res) != 1) {
1767 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1770 ads_pull_uint32(ads, res, "highestCommittedUSN", usn);
1771 ads_msgfree(ads, res);
1772 return ADS_SUCCESS;
1775 /* parse a ADS timestring - typical string is
1776 '20020917091222.0Z0' which means 09:12.22 17th September
1777 2002, timezone 0 */
1778 static time_t ads_parse_time(const char *str)
1780 struct tm tm;
1782 ZERO_STRUCT(tm);
1784 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
1785 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
1786 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
1787 return 0;
1789 tm.tm_year -= 1900;
1790 tm.tm_mon -= 1;
1792 return timegm(&tm);
1797 * Find the servers name and realm - this can be done before authentication
1798 * The ldapServiceName field on w2k looks like this:
1799 * vnet3.home.samba.org:win2000-vnet3$@VNET3.HOME.SAMBA.ORG
1800 * @param ads connection to ads server
1801 * @return status of search
1803 ADS_STATUS ads_server_info(ADS_STRUCT *ads)
1805 const char *attrs[] = {"ldapServiceName", "currentTime", NULL};
1806 ADS_STATUS status;
1807 void *res;
1808 char *value;
1809 char *p;
1810 char *timestr;
1811 TALLOC_CTX *ctx;
1813 if (!(ctx = talloc_init("ads_server_info"))) {
1814 return ADS_ERROR(LDAP_NO_MEMORY);
1817 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
1818 if (!ADS_ERR_OK(status)) return status;
1820 value = ads_pull_string(ads, ctx, res, "ldapServiceName");
1821 if (!value) {
1822 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1825 timestr = ads_pull_string(ads, ctx, res, "currentTime");
1826 if (!timestr) {
1827 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1830 ldap_msgfree(res);
1832 p = strchr(value, ':');
1833 if (!p) {
1834 talloc_destroy(ctx);
1835 DEBUG(1, ("ads_server_info: returned ldap server name did not contain a ':' so was deemed invalid\n"));
1836 return ADS_ERROR(LDAP_DECODING_ERROR);
1839 SAFE_FREE(ads->config.ldap_server_name);
1841 ads->config.ldap_server_name = strdup(p+1);
1842 p = strchr(ads->config.ldap_server_name, '$');
1843 if (!p || p[1] != '@') {
1844 talloc_destroy(ctx);
1845 DEBUG(1, ("ads_server_info: returned ldap server name (%s) does not contain '$@' so was deemed invalid\n", ads->config.ldap_server_name));
1846 SAFE_FREE(ads->config.ldap_server_name);
1847 return ADS_ERROR(LDAP_DECODING_ERROR);
1850 *p = 0;
1852 SAFE_FREE(ads->config.realm);
1853 SAFE_FREE(ads->config.bind_path);
1855 ads->config.realm = strdup(p+2);
1856 ads->config.bind_path = ads_build_dn(ads->config.realm);
1858 DEBUG(3,("got ldap server name %s@%s, using bind path: %s\n",
1859 ads->config.ldap_server_name, ads->config.realm,
1860 ads->config.bind_path));
1862 ads->config.current_time = ads_parse_time(timestr);
1864 if (ads->config.current_time != 0) {
1865 ads->auth.time_offset = ads->config.current_time - time(NULL);
1866 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
1869 talloc_destroy(ctx);
1871 return ADS_SUCCESS;
1876 * find the list of trusted domains
1877 * @param ads connection to ads server
1878 * @param mem_ctx TALLOC_CTX for allocating results
1879 * @param num_trusts pointer to number of trusts
1880 * @param names pointer to trusted domain name list
1881 * @param sids pointer to list of sids of trusted domains
1882 * @return the count of SIDs pulled
1884 ADS_STATUS ads_trusted_domains(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
1885 int *num_trusts,
1886 char ***names,
1887 char ***alt_names,
1888 DOM_SID **sids)
1890 const char *attrs[] = {"name", "flatname", "securityIdentifier",
1891 "trustDirection", NULL};
1892 ADS_STATUS status;
1893 void *res, *msg;
1894 int count, i;
1896 *num_trusts = 0;
1898 status = ads_search(ads, &res, "(objectcategory=trustedDomain)", attrs);
1899 if (!ADS_ERR_OK(status)) return status;
1901 count = ads_count_replies(ads, res);
1902 if (count == 0) {
1903 ads_msgfree(ads, res);
1904 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1907 (*names) = talloc(mem_ctx, sizeof(char *) * count);
1908 (*alt_names) = talloc(mem_ctx, sizeof(char *) * count);
1909 (*sids) = talloc(mem_ctx, sizeof(DOM_SID) * count);
1910 if (! *names || ! *sids) return ADS_ERROR(LDAP_NO_MEMORY);
1912 for (i=0, msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
1913 uint32 direction;
1915 /* direction is a 2 bit bitfield, 1 means they trust us
1916 but we don't trust them, so we should not list them
1917 as users from that domain can't login */
1918 if (ads_pull_uint32(ads, msg, "trustDirection", &direction) &&
1919 direction == 1) {
1920 continue;
1923 (*names)[i] = ads_pull_string(ads, mem_ctx, msg, "name");
1924 (*alt_names)[i] = ads_pull_string(ads, mem_ctx, msg, "flatname");
1926 if ((*alt_names)[i] && (*alt_names)[i][0]) {
1927 /* we prefer the flatname as the primary name
1928 for consistency with RPC */
1929 char *name = (*alt_names)[i];
1930 (*alt_names)[i] = (*names)[i];
1931 (*names)[i] = name;
1933 if (ads_pull_sid(ads, msg, "securityIdentifier", &(*sids)[i])) {
1934 i++;
1938 ads_msgfree(ads, res);
1940 *num_trusts = i;
1942 return ADS_SUCCESS;
1946 * find the domain sid for our domain
1947 * @param ads connection to ads server
1948 * @param sid Pointer to domain sid
1949 * @return status of search
1951 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
1953 const char *attrs[] = {"objectSid", NULL};
1954 void *res;
1955 ADS_STATUS rc;
1957 rc = ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
1958 attrs, &res);
1959 if (!ADS_ERR_OK(rc)) return rc;
1960 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
1961 return ADS_ERROR_SYSTEM(ENOENT);
1963 ads_msgfree(ads, res);
1965 return ADS_SUCCESS;
1968 /* this is rather complex - we need to find the allternate (netbios) name
1969 for the domain, but there isn't a simple query to do this. Instead
1970 we look for the principle names on the DCs account and find one that has
1971 the right form, then extract the netbios name of the domain from that
1973 NOTE! better method is this:
1975 bin/net -Uadministrator%XXXXX ads search '(&(objectclass=crossref)(dnsroot=VNET3.HOME.SAMBA.ORG))' nETBIOSName
1977 but you need to force the bind path to match the configurationNamingContext from the rootDSE
1980 ADS_STATUS ads_workgroup_name(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char **workgroup)
1982 char *expr;
1983 ADS_STATUS rc;
1984 char **principles;
1985 char *prefix;
1986 int prefix_length;
1987 int i;
1988 void *res;
1989 const char *attrs[] = {"servicePrincipalName", NULL};
1991 (*workgroup) = NULL;
1993 asprintf(&expr, "(&(objectclass=computer)(dnshostname=%s.%s))",
1994 ads->config.ldap_server_name, ads->config.realm);
1995 rc = ads_search(ads, &res, expr, attrs);
1996 free(expr);
1998 if (!ADS_ERR_OK(rc)) {
1999 return rc;
2002 principles = ads_pull_strings(ads, mem_ctx, res, "servicePrincipalName");
2004 ads_msgfree(ads, res);
2006 if (!principles) {
2007 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2010 asprintf(&prefix, "HOST/%s.%s/",
2011 ads->config.ldap_server_name,
2012 ads->config.realm);
2014 prefix_length = strlen(prefix);
2016 for (i=0;principles[i]; i++) {
2017 if (strncasecmp(principles[i], prefix, prefix_length) == 0 &&
2018 strcasecmp(ads->config.realm, principles[i]+prefix_length) != 0 &&
2019 !strchr(principles[i]+prefix_length, '.')) {
2020 /* found an alternate (short) name for the domain. */
2021 DEBUG(3,("Found alternate name '%s' for realm '%s'\n",
2022 principles[i]+prefix_length,
2023 ads->config.realm));
2024 (*workgroup) = talloc_strdup(mem_ctx, principles[i]+prefix_length);
2025 break;
2028 free(prefix);
2030 if (!*workgroup) {
2031 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2034 return ADS_SUCCESS;
2037 #endif