r6114: the marker is const and is 0x434B 'CK'
[Samba.git] / source / libads / ldap.c
blobf760843b59698769da51e76fd936423d63560a5e
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"
24 #include "version.h"
26 #ifdef HAVE_LDAP
28 /**
29 * @file ldap.c
30 * @brief basic ldap client-side routines for ads server communications
32 * The routines contained here should do the necessary ldap calls for
33 * ads setups.
35 * Important note: attribute names passed into ads_ routines must
36 * already be in UTF-8 format. We do not convert them because in almost
37 * all cases, they are just ascii (which is represented with the same
38 * codepoints in UTF-8). This may have to change at some point
39 **/
43 try a connection to a given ldap server, returning True and setting the servers IP
44 in the ads struct if successful
46 TODO : add a negative connection cache in here leveraged off of the one
47 found in the rpc code. --jerry
49 static BOOL ads_try_connect(ADS_STRUCT *ads, const char *server, uint_t port)
51 char *srv;
53 if (!server || !*server) {
54 return False;
57 DEBUG(5,("ads_try_connect: trying ldap server '%s' port %u\n", server, port));
59 /* this copes with inet_ntoa brokenness */
60 srv = strdup(server);
62 ads->ld = ldap_open(srv, port);
63 if (!ads->ld) {
64 free(srv);
65 return False;
67 ads->ldap_port = port;
68 ads->ldap_ip = interpret_addr2(srv);
69 free(srv);
71 return True;
75 try a connection to a given ldap server, based on URL, returning True if successful
77 static BOOL ads_try_connect_uri(ADS_STRUCT *ads)
79 #if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
80 DEBUG(5,("ads_try_connect: trying ldap server at URI '%s'\n",
81 ads->server.ldap_uri));
84 if (ldap_initialize((LDAP**)&(ads->ld), ads->server.ldap_uri) == LDAP_SUCCESS) {
85 return True;
87 DEBUG(0, ("ldap_initialize: %s\n", strerror(errno)));
89 #else
91 DEBUG(1, ("no URL support in LDAP libs!\n"));
92 #endif
94 return False;
97 /**********************************************************************
98 Try to find an AD dc using our internal name resolution routines
99 Try the realm first and then then workgroup name if netbios is not
100 disabled
101 **********************************************************************/
103 static BOOL ads_find_dc(ADS_STRUCT *ads)
105 const char *c_realm;
106 int count, i=0;
107 struct ip_service *ip_list;
108 pstring realm;
109 BOOL got_realm = False;
110 BOOL use_own_domain = False;
112 /* if the realm and workgroup are both empty, assume they are ours */
114 /* realm */
115 c_realm = ads->server.realm;
117 if ( !c_realm || !*c_realm ) {
118 /* special case where no realm and no workgroup means our own */
119 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
120 use_own_domain = True;
121 c_realm = lp_realm();
125 if (c_realm && *c_realm)
126 got_realm = True;
128 again:
129 /* we need to try once with the realm name and fallback to the
130 netbios domain name if we fail (if netbios has not been disabled */
132 if ( !got_realm && !lp_disable_netbios() ) {
133 c_realm = ads->server.workgroup;
134 if (!c_realm || !*c_realm) {
135 if ( use_own_domain )
136 c_realm = lp_workgroup();
139 if ( !c_realm || !*c_realm ) {
140 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
141 return False;
145 pstrcpy( realm, c_realm );
147 DEBUG(6,("ads_find_dc: looking for %s '%s'\n",
148 (got_realm ? "realm" : "domain"), realm));
150 if ( !get_sorted_dc_list(realm, &ip_list, &count, got_realm) ) {
151 /* fall back to netbios if we can */
152 if ( got_realm && !lp_disable_netbios() ) {
153 got_realm = False;
154 goto again;
157 return False;
160 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
161 for ( i=0; i<count; i++ ) {
162 /* since this is an ads conection request, default to LDAP_PORT is not set */
163 int port = (ip_list[i].port!=PORT_NONE) ? ip_list[i].port : LDAP_PORT;
164 fstring server;
166 fstrcpy( server, inet_ntoa(ip_list[i].ip) );
168 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
169 continue;
171 if ( ads_try_connect(ads, server, port) ) {
172 SAFE_FREE(ip_list);
173 return True;
176 /* keep track of failures */
177 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
180 SAFE_FREE(ip_list);
182 return False;
187 * Connect to the LDAP server
188 * @param ads Pointer to an existing ADS_STRUCT
189 * @return status of connection
191 ADS_STATUS ads_connect(ADS_STRUCT *ads)
193 int version = LDAP_VERSION3;
194 ADS_STATUS status;
196 ads->last_attempt = time(NULL);
197 ads->ld = NULL;
199 /* try with a URL based server */
201 if (ads->server.ldap_uri &&
202 ads_try_connect_uri(ads)) {
203 goto got_connection;
206 /* try with a user specified server */
207 if (ads->server.ldap_server &&
208 ads_try_connect(ads, ads->server.ldap_server, LDAP_PORT)) {
209 goto got_connection;
212 if (ads_find_dc(ads)) {
213 goto got_connection;
216 return ADS_ERROR_SYSTEM(errno?errno:ENOENT);
218 got_connection:
219 DEBUG(3,("Connected to LDAP server %s\n", inet_ntoa(ads->ldap_ip)));
221 status = ads_server_info(ads);
222 if (!ADS_ERR_OK(status)) {
223 DEBUG(1,("Failed to get ldap server info\n"));
224 return status;
227 ldap_set_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
229 if (!ads->auth.user_name) {
230 /* by default use the machine account */
231 fstring myname;
232 fstrcpy(myname, lp_netbios_name());
233 strlower_m(myname);
234 asprintf(&ads->auth.user_name, "HOST/%s", myname);
237 if (!ads->auth.realm) {
238 ads->auth.realm = strdup(ads->config.realm);
241 if (!ads->auth.kdc_server) {
242 ads->auth.kdc_server = strdup(inet_ntoa(ads->ldap_ip));
245 #if KRB5_DNS_HACK
246 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
247 to MIT kerberos to work (tridge) */
249 char *env;
250 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
251 setenv(env, ads->auth.kdc_server, 1);
252 free(env);
254 #endif
256 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
257 return ADS_SUCCESS;
260 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
261 return ADS_ERROR(ldap_simple_bind_s( ads->ld, NULL, NULL));
264 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
265 return ADS_ERROR(ldap_simple_bind_s( ads->ld, ads->auth.user_name, ads->auth.password));
268 return ads_sasl_bind(ads);
272 Duplicate a struct berval into talloc'ed memory
274 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
276 struct berval *value;
278 if (!in_val) return NULL;
280 value = talloc_zero(ctx, struct berval);
281 if (value == NULL)
282 return NULL;
283 if (in_val->bv_len == 0) return value;
285 value->bv_len = in_val->bv_len;
286 value->bv_val = talloc_memdup(ctx, in_val->bv_val, in_val->bv_len);
287 return value;
291 Make a values list out of an array of (struct berval *)
293 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
294 const struct berval **in_vals)
296 struct berval **values;
297 int i;
299 if (!in_vals) return NULL;
300 for (i=0; in_vals[i]; i++); /* count values */
301 values = (struct berval **) talloc_zero(ctx,
302 (i+1)*sizeof(struct berval *));
303 if (!values) return NULL;
305 for (i=0; in_vals[i]; i++) {
306 values[i] = dup_berval(ctx, in_vals[i]);
308 return values;
312 UTF8-encode a values list out of an array of (char *)
314 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
316 char **values;
317 int i;
319 if (!in_vals) return NULL;
320 for (i=0; in_vals[i]; i++); /* count values */
321 values = talloc_zero_array_p(ctx, char *, i+1);
322 if (!values) return NULL;
324 for (i=0; in_vals[i]; i++) {
325 push_utf8_talloc(ctx, &values[i], in_vals[i]);
327 return values;
331 Pull a (char *) array out of a UTF8-encoded values list
333 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
335 char **values;
336 int i;
338 if (!in_vals) return NULL;
339 for (i=0; in_vals[i]; i++); /* count values */
340 values = talloc_zero_array_p(ctx, char *, i+1);
341 if (!values) return NULL;
343 for (i=0; in_vals[i]; i++) {
344 pull_utf8_talloc(ctx, &values[i], in_vals[i]);
346 return values;
350 * Do a search with paged results. cookie must be null on the first
351 * call, and then returned on each subsequent call. It will be null
352 * again when the entire search is complete
353 * @param ads connection to ads server
354 * @param bind_path Base dn for the search
355 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
356 * @param expr Search expression - specified in local charset
357 * @param attrs Attributes to retrieve - specified in utf8 or ascii
358 * @param res ** which will contain results - free res* with ads_msgfree()
359 * @param count Number of entries retrieved on this page
360 * @param cookie The paged results cookie to be returned on subsequent calls
361 * @return status of search
363 ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
364 int scope, const char *expr,
365 const char **attrs, void **res,
366 int *count, void **cookie)
368 int rc, i, version;
369 char *utf8_expr, *utf8_path, **search_attrs;
370 LDAPControl PagedResults, NoReferrals, *controls[3], **rcontrols;
371 BerElement *cookie_be = NULL;
372 struct berval *cookie_bv= NULL;
373 TALLOC_CTX *ctx;
375 *res = NULL;
377 if (!(ctx = talloc_init("ads_do_paged_search")))
378 return ADS_ERROR(LDAP_NO_MEMORY);
380 /* 0 means the conversion worked but the result was empty
381 so we only fail if it's -1. In any case, it always
382 at least nulls out the dest */
383 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
384 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
385 rc = LDAP_NO_MEMORY;
386 goto done;
389 if (!attrs || !(*attrs))
390 search_attrs = NULL;
391 else {
392 /* This would be the utf8-encoded version...*/
393 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
394 search_attrs = str_list_copy(ctx, attrs);
395 if (search_attrs == NULL) {
396 rc = LDAP_NO_MEMORY;
397 goto done;
402 /* Paged results only available on ldap v3 or later */
403 ldap_get_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
404 if (version < LDAP_VERSION3) {
405 rc = LDAP_NOT_SUPPORTED;
406 goto done;
409 cookie_be = ber_alloc_t(LBER_USE_DER);
410 if (cookie && *cookie) {
411 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
412 ber_bvfree(*cookie); /* don't need it from last time */
413 *cookie = NULL;
414 } else {
415 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
417 ber_flatten(cookie_be, &cookie_bv);
418 PagedResults.ldctl_oid = ADS_PAGE_CTL_OID;
419 PagedResults.ldctl_iscritical = (char) 1;
420 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
421 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
423 NoReferrals.ldctl_oid = ADS_NO_REFERRALS_OID;
424 NoReferrals.ldctl_iscritical = (char) 0;
425 NoReferrals.ldctl_value.bv_len = 0;
426 NoReferrals.ldctl_value.bv_val = "";
429 controls[0] = &NoReferrals;
430 controls[1] = &PagedResults;
431 controls[2] = NULL;
433 *res = NULL;
435 /* we need to disable referrals as the openldap libs don't
436 handle them and paged results at the same time. Using them
437 together results in the result record containing the server
438 page control being removed from the result list (tridge/jmcd)
440 leaving this in despite the control that says don't generate
441 referrals, in case the server doesn't support it (jmcd)
443 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
445 rc = ldap_search_ext_s(ads->ld, utf8_path, scope, utf8_expr,
446 search_attrs, 0, controls,
447 NULL, NULL, LDAP_NO_LIMIT, (LDAPMessage **)res);
449 ber_free(cookie_be, 1);
450 ber_bvfree(cookie_bv);
452 if (rc) {
453 DEBUG(3,("ldap_search_ext_s(%s) -> %s\n", expr, ldap_err2string(rc)));
454 goto done;
457 rc = ldap_parse_result(ads->ld, *res, NULL, NULL, NULL,
458 NULL, &rcontrols, 0);
460 if (!rcontrols) {
461 goto done;
464 for (i=0; rcontrols[i]; i++) {
465 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
466 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
467 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
468 &cookie_bv);
469 /* the berval is the cookie, but must be freed when
470 it is all done */
471 if (cookie_bv->bv_len) /* still more to do */
472 *cookie=ber_bvdup(cookie_bv);
473 else
474 *cookie=NULL;
475 ber_bvfree(cookie_bv);
476 ber_free(cookie_be, 1);
477 break;
480 ldap_controls_free(rcontrols);
482 done:
483 talloc_free(ctx);
484 /* if/when we decide to utf8-encode attrs, take out this next line */
485 str_list_free(&search_attrs);
487 return ADS_ERROR(rc);
492 * Get all results for a search. This uses ads_do_paged_search() to return
493 * all entries in a large search.
494 * @param ads connection to ads server
495 * @param bind_path Base dn for the search
496 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
497 * @param expr Search expression
498 * @param attrs Attributes to retrieve
499 * @param res ** which will contain results - free res* with ads_msgfree()
500 * @return status of search
502 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
503 int scope, const char *expr,
504 const char **attrs, void **res)
506 void *cookie = NULL;
507 int count = 0;
508 ADS_STATUS status;
510 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, res,
511 &count, &cookie);
513 if (!ADS_ERR_OK(status)) return status;
515 while (cookie) {
516 void *res2 = NULL;
517 ADS_STATUS status2;
518 LDAPMessage *msg, *next;
520 status2 = ads_do_paged_search(ads, bind_path, scope, expr,
521 attrs, &res2, &count, &cookie);
523 if (!ADS_ERR_OK(status2)) break;
525 /* this relies on the way that ldap_add_result_entry() works internally. I hope
526 that this works on all ldap libs, but I have only tested with openldap */
527 for (msg = ads_first_entry(ads, res2); msg; msg = next) {
528 next = ads_next_entry(ads, msg);
529 ldap_add_result_entry((LDAPMessage **)res, msg);
531 /* note that we do not free res2, as the memory is now
532 part of the main returned list */
535 return status;
539 * Run a function on all results for a search. Uses ads_do_paged_search() and
540 * runs the function as each page is returned, using ads_process_results()
541 * @param ads connection to ads server
542 * @param bind_path Base dn for the search
543 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
544 * @param expr Search expression - specified in local charset
545 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
546 * @param fn Function which takes attr name, values list, and data_area
547 * @param data_area Pointer which is passed to function on each call
548 * @return status of search
550 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
551 int scope, const char *expr, const char **attrs,
552 BOOL(*fn)(char *, void **, void *),
553 void *data_area)
555 void *cookie = NULL;
556 int count = 0;
557 ADS_STATUS status;
558 void *res;
560 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
561 &count, &cookie);
563 if (!ADS_ERR_OK(status)) return status;
565 ads_process_results(ads, res, fn, data_area);
566 ads_msgfree(ads, res);
568 while (cookie) {
569 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
570 &res, &count, &cookie);
572 if (!ADS_ERR_OK(status)) break;
574 ads_process_results(ads, res, fn, data_area);
575 ads_msgfree(ads, res);
578 return status;
582 * Do a search with a timeout.
583 * @param ads connection to ads server
584 * @param bind_path Base dn for the search
585 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
586 * @param expr Search expression
587 * @param attrs Attributes to retrieve
588 * @param res ** which will contain results - free res* with ads_msgfree()
589 * @return status of search
591 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
592 const char *expr,
593 const char **attrs, void **res)
595 struct timeval timeout;
596 int rc;
597 char *utf8_expr, *utf8_path, **search_attrs = NULL;
598 TALLOC_CTX *ctx;
600 if (!(ctx = talloc_init("ads_do_search"))) {
601 DEBUG(1,("ads_do_search: talloc_init() failed!"));
602 return ADS_ERROR(LDAP_NO_MEMORY);
605 /* 0 means the conversion worked but the result was empty
606 so we only fail if it's negative. In any case, it always
607 at least nulls out the dest */
608 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
609 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
610 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
611 rc = LDAP_NO_MEMORY;
612 goto done;
615 if (!attrs || !(*attrs))
616 search_attrs = NULL;
617 else {
618 /* This would be the utf8-encoded version...*/
619 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
620 search_attrs = str_list_copy(ctx, attrs);
621 if (search_attrs == NULL) {
622 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
623 rc = LDAP_NO_MEMORY;
624 goto done;
628 timeout.tv_sec = ADS_SEARCH_TIMEOUT;
629 timeout.tv_usec = 0;
630 *res = NULL;
632 /* see the note in ads_do_paged_search - we *must* disable referrals */
633 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
635 rc = ldap_search_ext_s(ads->ld, utf8_path, scope, utf8_expr,
636 search_attrs, 0, NULL, NULL,
637 &timeout, LDAP_NO_LIMIT, (LDAPMessage **)res);
639 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
640 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
641 rc = 0;
644 done:
645 talloc_free(ctx);
646 /* if/when we decide to utf8-encode attrs, take out this next line */
647 str_list_free(&search_attrs);
648 return ADS_ERROR(rc);
651 * Do a general ADS search
652 * @param ads connection to ads server
653 * @param res ** which will contain results - free res* with ads_msgfree()
654 * @param expr Search expression
655 * @param attrs Attributes to retrieve
656 * @return status of search
658 ADS_STATUS ads_search(ADS_STRUCT *ads, void **res,
659 const char *expr,
660 const char **attrs)
662 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
663 expr, attrs, res);
667 * Do a search on a specific DistinguishedName
668 * @param ads connection to ads server
669 * @param res ** which will contain results - free res* with ads_msgfree()
670 * @param dn DistinguishName to search
671 * @param attrs Attributes to retrieve
672 * @return status of search
674 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, void **res,
675 const char *dn,
676 const char **attrs)
678 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)", attrs, res);
682 * Free up memory from a ads_search
683 * @param ads connection to ads server
684 * @param msg Search results to free
686 void ads_msgfree(ADS_STRUCT *ads, void *msg)
688 if (!msg) return;
689 ldap_msgfree(msg);
693 * Free up memory from various ads requests
694 * @param ads connection to ads server
695 * @param mem Area to free
697 void ads_memfree(ADS_STRUCT *ads, void *mem)
699 SAFE_FREE(mem);
703 * Get a dn from search results
704 * @param ads connection to ads server
705 * @param msg Search result
706 * @return dn string
708 char *ads_get_dn(ADS_STRUCT *ads, void *msg)
710 char *utf8_dn, *unix_dn;
712 utf8_dn = ldap_get_dn(ads->ld, msg);
714 if (!utf8_dn) {
715 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
716 return NULL;
719 if (pull_utf8_allocate(&unix_dn, utf8_dn) == (size_t)-1) {
720 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
721 utf8_dn ));
722 return NULL;
724 ldap_memfree(utf8_dn);
725 return unix_dn;
729 * Find a machine account given a hostname
730 * @param ads connection to ads server
731 * @param res ** which will contain results - free res* with ads_msgfree()
732 * @param host Hostname to search for
733 * @return status of search
735 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, void **res, const char *host)
737 ADS_STATUS status;
738 char *expr;
739 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
741 /* the easiest way to find a machine account anywhere in the tree
742 is to look for hostname$ */
743 if (asprintf(&expr, "(samAccountName=%s$)", host) == -1) {
744 DEBUG(1, ("asprintf failed!\n"));
745 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
748 status = ads_search(ads, res, expr, attrs);
749 free(expr);
750 return status;
754 * Initialize a list of mods to be used in a modify request
755 * @param ctx An initialized TALLOC_CTX
756 * @return allocated ADS_MODLIST
758 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
760 #define ADS_MODLIST_ALLOC_SIZE 10
761 LDAPMod **mods;
763 if ((mods = talloc_zero_array_p(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1))) {
764 /* -1 is safety to make sure we don't go over the end.
765 need to reset it to NULL before doing ldap modify */
766 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
769 return mods;
774 add an attribute to the list, with values list already constructed
776 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
777 int mod_op, const char *name,
778 const void **invals)
780 int curmod;
781 LDAPMod **modlist = (LDAPMod **) *mods;
782 struct berval **ber_values = NULL;
783 char **char_values = NULL;
785 if (!invals) {
786 mod_op = LDAP_MOD_DELETE;
787 } else {
788 if (mod_op & LDAP_MOD_BVALUES)
789 ber_values = ads_dup_values(ctx,
790 (const struct berval **)invals);
791 else
792 char_values = ads_push_strvals(ctx,
793 (const char **) invals);
796 /* find the first empty slot */
797 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
798 curmod++);
799 if (modlist[curmod] == (LDAPMod *) -1) {
800 if (!(modlist = talloc_realloc(modlist,
801 (curmod+ADS_MODLIST_ALLOC_SIZE+1)*sizeof(LDAPMod *))))
802 return ADS_ERROR(LDAP_NO_MEMORY);
803 memset(&modlist[curmod], 0,
804 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
805 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
806 *mods = modlist;
809 if (!(modlist[curmod] = talloc_zero(ctx, LDAPMod)))
810 return ADS_ERROR(LDAP_NO_MEMORY);
811 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
812 if (mod_op & LDAP_MOD_BVALUES) {
813 modlist[curmod]->mod_bvalues = ber_values;
814 } else if (mod_op & LDAP_MOD_DELETE) {
815 modlist[curmod]->mod_values = NULL;
816 } else {
817 modlist[curmod]->mod_values = char_values;
820 modlist[curmod]->mod_op = mod_op;
821 return ADS_ERROR(LDAP_SUCCESS);
825 * Add a single string value to a mod list
826 * @param ctx An initialized TALLOC_CTX
827 * @param mods An initialized ADS_MODLIST
828 * @param name The attribute name to add
829 * @param val The value to add - NULL means DELETE
830 * @return ADS STATUS indicating success of add
832 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
833 const char *name, const char *val)
835 const char *values[2];
837 values[0] = val;
838 values[1] = NULL;
840 if (!val)
841 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
842 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name,
843 (const void **) values);
847 * Add an array of string values to a mod list
848 * @param ctx An initialized TALLOC_CTX
849 * @param mods An initialized ADS_MODLIST
850 * @param name The attribute name to add
851 * @param vals The array of string values to add - NULL means DELETE
852 * @return ADS STATUS indicating success of add
854 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
855 const char *name, const char **vals)
857 if (!vals)
858 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
859 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
860 name, (const void **) vals);
864 * Add a single ber-encoded value to a mod list
865 * @param ctx An initialized TALLOC_CTX
866 * @param mods An initialized ADS_MODLIST
867 * @param name The attribute name to add
868 * @param val The value to add - NULL means DELETE
869 * @return ADS STATUS indicating success of add
871 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
872 const char *name, const struct berval *val)
874 const struct berval *values[2];
876 values[0] = val;
877 values[1] = NULL;
878 if (!val)
879 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
880 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
881 name, (const void **) values);
885 * Perform an ldap modify
886 * @param ads connection to ads server
887 * @param mod_dn DistinguishedName to modify
888 * @param mods list of modifications to perform
889 * @return status of modify
891 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
893 int ret,i;
894 char *utf8_dn = NULL;
896 this control is needed to modify that contains a currently
897 non-existent attribute (but allowable for the object) to run
899 LDAPControl PermitModify = {
900 ADS_PERMIT_MODIFY_OID,
901 {0, NULL},
902 (char) 1};
903 LDAPControl *controls[2];
905 controls[0] = &PermitModify;
906 controls[1] = NULL;
908 if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) {
909 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
912 /* find the end of the list, marked by NULL or -1 */
913 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
914 /* make sure the end of the list is NULL */
915 mods[i] = NULL;
916 ret = ldap_modify_ext_s(ads->ld, utf8_dn,
917 (LDAPMod **) mods, controls, NULL);
918 SAFE_FREE(utf8_dn);
919 return ADS_ERROR(ret);
923 * Perform an ldap add
924 * @param ads connection to ads server
925 * @param new_dn DistinguishedName to add
926 * @param mods list of attributes and values for DN
927 * @return status of add
929 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
931 int ret, i;
932 char *utf8_dn = NULL;
934 if (push_utf8_allocate(&utf8_dn, new_dn) == -1) {
935 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
936 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
939 /* find the end of the list, marked by NULL or -1 */
940 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
941 /* make sure the end of the list is NULL */
942 mods[i] = NULL;
944 ret = ldap_add_s(ads->ld, utf8_dn, mods);
945 SAFE_FREE(utf8_dn);
946 return ADS_ERROR(ret);
950 * Delete a DistinguishedName
951 * @param ads connection to ads server
952 * @param new_dn DistinguishedName to delete
953 * @return status of delete
955 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
957 int ret;
958 char *utf8_dn = NULL;
959 if (push_utf8_allocate(&utf8_dn, del_dn) == -1) {
960 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
961 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
964 ret = ldap_delete_s(ads->ld, utf8_dn);
965 return ADS_ERROR(ret);
969 * Build an org unit string
970 * if org unit is Computers or blank then assume a container, otherwise
971 * assume a \ separated list of organisational units
972 * @param org_unit Organizational unit
973 * @return org unit string - caller must free
975 char *ads_ou_string(const char *org_unit)
977 if (!org_unit || !*org_unit || strequal(org_unit, "Computers")) {
978 return strdup("cn=Computers");
981 return ads_build_path(org_unit, "\\/", "ou=", 1);
987 add a machine account to the ADS server
989 static ADS_STATUS ads_add_machine_acct(ADS_STRUCT *ads, const char *hostname,
990 uint32_t account_type,
991 const char *org_unit)
993 ADS_STATUS ret, status;
994 char *host_spn, *host_upn, *new_dn, *samAccountName, *controlstr;
995 char *ou_str;
996 TALLOC_CTX *ctx;
997 ADS_MODLIST mods;
998 const char *objectClass[] = {"top", "person", "organizationalPerson",
999 "user", "computer", NULL};
1000 const char *servicePrincipalName[5] = {NULL, NULL, NULL, NULL, NULL};
1001 char *psp, *psp2;
1002 uint_t acct_control;
1003 uint_t exists=0;
1004 LDAPMessage *res;
1006 status = ads_find_machine_acct(ads, (void **)&res, hostname);
1007 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1008 DEBUG(0, ("Host account for %s already exists - modifying old account\n", hostname));
1009 exists=1;
1012 if (!(ctx = talloc_init("machine_account")))
1013 return ADS_ERROR(LDAP_NO_MEMORY);
1015 ret = ADS_ERROR(LDAP_NO_MEMORY);
1017 if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", hostname)))
1018 goto done;
1019 if (!(host_upn = talloc_asprintf(ctx, "%s@%s", host_spn, ads->config.realm)))
1020 goto done;
1021 ou_str = ads_ou_string(org_unit);
1022 if (!ou_str) {
1023 DEBUG(1, ("ads_ou_string returned NULL (malloc failure?)\n"));
1024 goto done;
1026 new_dn = talloc_asprintf(ctx, "cn=%s,%s,%s", hostname, ou_str,
1027 ads->config.bind_path);
1028 servicePrincipalName[0] = talloc_asprintf(ctx, "HOST/%s", hostname);
1029 psp = talloc_asprintf(ctx, "HOST/%s.%s",
1030 hostname,
1031 ads->config.realm);
1032 strlower_m(&psp[5]);
1033 servicePrincipalName[1] = psp;
1034 servicePrincipalName[2] = talloc_asprintf(ctx, "CIFS/%s", hostname);
1035 psp2 = talloc_asprintf(ctx, "CIFS/%s.%s",
1036 hostname,
1037 ads->config.realm);
1038 strlower_m(&psp2[5]);
1039 servicePrincipalName[3] = psp2;
1041 free(ou_str);
1042 if (!new_dn)
1043 goto done;
1045 if (!(samAccountName = talloc_asprintf(ctx, "%s$", hostname)))
1046 goto done;
1048 acct_control = account_type | UF_DONT_EXPIRE_PASSWD;
1049 #ifndef ENCTYPE_ARCFOUR_HMAC
1050 acct_control |= UF_USE_DES_KEY_ONLY;
1051 #endif
1053 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control)))
1054 goto done;
1056 if (!(mods = ads_init_mods(ctx)))
1057 goto done;
1059 if (!exists) {
1060 ads_mod_str(ctx, &mods, "cn", hostname);
1061 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1062 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1063 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1065 ads_mod_str(ctx, &mods, "dNSHostName", hostname);
1066 ads_mod_str(ctx, &mods, "userPrincipalName", host_upn);
1067 ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1068 ads_mod_str(ctx, &mods, "operatingSystem", "Samba");
1069 ads_mod_str(ctx, &mods, "operatingSystemVersion", SAMBA_VERSION_STRING);
1071 if (!exists)
1072 ret = ads_gen_add(ads, new_dn, mods);
1073 else
1074 ret = ads_gen_mod(ads, new_dn, mods);
1076 if (!ADS_ERR_OK(ret))
1077 goto done;
1079 /* Do not fail if we can't set security descriptor
1080 * it shouldn't be mandatory and probably we just
1081 * don't have enough rights to do it.
1083 if (!exists) {
1084 status = ads_set_machine_sd(ads, hostname, new_dn);
1086 if (!ADS_ERR_OK(status)) {
1087 DEBUG(0, ("Warning: ads_set_machine_sd: %s\n",
1088 ads_errstr(status)));
1091 done:
1092 talloc_free(ctx);
1093 return ret;
1097 dump a binary result from ldap
1099 static void dump_binary(const char *field, struct berval **values)
1101 int i, j;
1102 for (i=0; values[i]; i++) {
1103 printf("%s: ", field);
1104 for (j=0; j<values[i]->bv_len; j++) {
1105 printf("%02X", (uint8_t)values[i]->bv_val[j]);
1107 printf("\n");
1111 struct uuid {
1112 uint32_t i1;
1113 uint16_t i2;
1114 uint16_t i3;
1115 uint8_t s[8];
1118 static void dump_guid(const char *field, struct berval **values)
1120 int i;
1121 GUID guid;
1122 for (i=0; values[i]; i++) {
1123 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1124 printf("%s: %s\n", field, smb_uuid_string_static(guid));
1129 dump a sid result from ldap
1131 static void dump_sid(const char *field, struct berval **values)
1133 int i;
1134 for (i=0; values[i]; i++) {
1135 DOM_SID sid;
1136 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1137 printf("%s: %s\n", field, sid_string_static(&sid));
1142 dump ntSecurityDescriptor
1144 static void dump_sd(const char *filed, struct berval **values)
1146 prs_struct ps;
1148 SEC_DESC *psd = 0;
1149 TALLOC_CTX *ctx = 0;
1151 if (!(ctx = talloc_init("sec_io_desc")))
1152 return;
1154 /* prepare data */
1155 prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
1156 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
1157 prs_set_offset(&ps,0);
1159 /* parse secdesc */
1160 if (!sec_io_desc("sd", &psd, &ps, 1)) {
1161 prs_mem_free(&ps);
1162 talloc_free(ctx);
1163 return;
1165 if (psd) ads_disp_sd(psd);
1167 prs_mem_free(&ps);
1168 talloc_free(ctx);
1172 dump a string result from ldap
1174 static void dump_string(const char *field, char **values)
1176 int i;
1177 for (i=0; values[i]; i++) {
1178 printf("%s: %s\n", field, values[i]);
1183 dump a field from LDAP on stdout
1184 used for debugging
1187 static BOOL ads_dump_field(char *field, void **values, void *data_area)
1189 const struct {
1190 const char *name;
1191 BOOL string;
1192 void (*handler)(const char *, struct berval **);
1193 } handlers[] = {
1194 {"objectGUID", False, dump_guid},
1195 {"nTSecurityDescriptor", False, dump_sd},
1196 {"dnsRecord", False, dump_binary},
1197 {"objectSid", False, dump_sid},
1198 {"tokenGroups", False, dump_sid},
1199 {NULL, True, NULL}
1201 int i;
1203 if (!field) { /* must be end of an entry */
1204 printf("\n");
1205 return False;
1208 for (i=0; handlers[i].name; i++) {
1209 if (StrCaseCmp(handlers[i].name, field) == 0) {
1210 if (!values) /* first time, indicate string or not */
1211 return handlers[i].string;
1212 handlers[i].handler(field, (struct berval **) values);
1213 break;
1216 if (!handlers[i].name) {
1217 if (!values) /* first time, indicate string conversion */
1218 return True;
1219 dump_string(field, (char **)values);
1221 return False;
1225 * Dump a result from LDAP on stdout
1226 * used for debugging
1227 * @param ads connection to ads server
1228 * @param res Results to dump
1231 void ads_dump(ADS_STRUCT *ads, void *res)
1233 ads_process_results(ads, res, ads_dump_field, NULL);
1237 * Walk through results, calling a function for each entry found.
1238 * The function receives a field name, a berval * array of values,
1239 * and a data area passed through from the start. The function is
1240 * called once with null for field and values at the end of each
1241 * entry.
1242 * @param ads connection to ads server
1243 * @param res Results to process
1244 * @param fn Function for processing each result
1245 * @param data_area user-defined area to pass to function
1247 void ads_process_results(ADS_STRUCT *ads, void *res,
1248 BOOL(*fn)(char *, void **, void *),
1249 void *data_area)
1251 void *msg;
1252 TALLOC_CTX *ctx;
1254 if (!(ctx = talloc_init("ads_process_results")))
1255 return;
1257 for (msg = ads_first_entry(ads, res); msg;
1258 msg = ads_next_entry(ads, msg)) {
1259 char *utf8_field;
1260 BerElement *b;
1262 for (utf8_field=ldap_first_attribute(ads->ld,
1263 (LDAPMessage *)msg,&b);
1264 utf8_field;
1265 utf8_field=ldap_next_attribute(ads->ld,
1266 (LDAPMessage *)msg,b)) {
1267 struct berval **ber_vals;
1268 char **str_vals, **utf8_vals;
1269 char *field;
1270 BOOL string;
1272 pull_utf8_talloc(ctx, &field, utf8_field);
1273 string = fn(field, NULL, data_area);
1275 if (string) {
1276 utf8_vals = ldap_get_values(ads->ld,
1277 (LDAPMessage *)msg, field);
1278 str_vals = ads_pull_strvals(ctx,
1279 (const char **) utf8_vals);
1280 fn(field, (void **) str_vals, data_area);
1281 ldap_value_free(utf8_vals);
1282 } else {
1283 ber_vals = ldap_get_values_len(ads->ld,
1284 (LDAPMessage *)msg, field);
1285 fn(field, (void **) ber_vals, data_area);
1287 ldap_value_free_len(ber_vals);
1289 ldap_memfree(utf8_field);
1291 ber_free(b, 0);
1292 talloc_destroy_pool(ctx);
1293 fn(NULL, NULL, data_area); /* completed an entry */
1296 talloc_free(ctx);
1300 * count how many replies are in a LDAPMessage
1301 * @param ads connection to ads server
1302 * @param res Results to count
1303 * @return number of replies
1305 int ads_count_replies(ADS_STRUCT *ads, void *res)
1307 return ldap_count_entries(ads->ld, (LDAPMessage *)res);
1311 * Join a machine to a realm
1312 * Creates the machine account and sets the machine password
1313 * @param ads connection to ads server
1314 * @param hostname name of host to add
1315 * @param org_unit Organizational unit to place machine in
1316 * @return status of join
1318 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *hostname,
1319 uint32_t account_type, const char *org_unit)
1321 ADS_STATUS status;
1322 LDAPMessage *res;
1323 char *host;
1325 /* hostname must be lowercase */
1326 host = strdup(hostname);
1327 strlower_m(host);
1330 status = ads_find_machine_acct(ads, (void **)&res, host);
1331 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1332 DEBUG(0, ("Host account for %s already exists - deleting old account\n", host));
1333 status = ads_leave_realm(ads, host);
1334 if (!ADS_ERR_OK(status)) {
1335 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
1336 host, ads->config.realm));
1337 return status;
1342 status = ads_add_machine_acct(ads, host, account_type, org_unit);
1343 if (!ADS_ERR_OK(status)) {
1344 DEBUG(0, ("ads_add_machine_acct: %s\n", ads_errstr(status)));
1345 return status;
1348 status = ads_find_machine_acct(ads, (void **)&res, host);
1349 if (!ADS_ERR_OK(status)) {
1350 DEBUG(0, ("Host account test failed\n"));
1351 return status;
1354 free(host);
1356 return status;
1360 * Delete a machine from the realm
1361 * @param ads connection to ads server
1362 * @param hostname Machine to remove
1363 * @return status of delete
1365 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
1367 ADS_STATUS status;
1368 void *res, *msg;
1369 char *hostnameDN, *host;
1370 int rc;
1372 /* hostname must be lowercase */
1373 host = strdup(hostname);
1374 strlower_m(host);
1376 status = ads_find_machine_acct(ads, &res, host);
1377 if (!ADS_ERR_OK(status)) {
1378 DEBUG(0, ("Host account for %s does not exist.\n", host));
1379 return status;
1382 msg = ads_first_entry(ads, res);
1383 if (!msg) {
1384 return ADS_ERROR_SYSTEM(ENOENT);
1387 hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
1388 rc = ldap_delete_s(ads->ld, hostnameDN);
1389 ads_memfree(ads, hostnameDN);
1390 if (rc != LDAP_SUCCESS) {
1391 return ADS_ERROR(rc);
1394 status = ads_find_machine_acct(ads, &res, host);
1395 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1396 DEBUG(0, ("Failed to remove host account.\n"));
1397 return status;
1400 free(host);
1402 return status;
1406 * add machine account to existing security descriptor
1407 * @param ads connection to ads server
1408 * @param hostname machine to add
1409 * @param dn DN of security descriptor
1410 * @return status
1412 ADS_STATUS ads_set_machine_sd(ADS_STRUCT *ads, const char *hostname, char *dn)
1414 const char *attrs[] = {"nTSecurityDescriptor", "objectSid", 0};
1415 char *expr = 0;
1416 size_t sd_size = 0;
1417 struct berval bval = {0, NULL};
1418 prs_struct ps_wire;
1419 char *escaped_hostname = escape_ldap_string_alloc(hostname);
1421 LDAPMessage *res = 0;
1422 LDAPMessage *msg = 0;
1423 ADS_MODLIST mods = 0;
1425 NTSTATUS status;
1426 ADS_STATUS ret;
1427 DOM_SID sid;
1428 SEC_DESC *psd = NULL;
1429 TALLOC_CTX *ctx = NULL;
1431 /* Avoid segmentation fault in prs_mem_free if
1432 * we have to bail out before prs_init */
1433 ps_wire.is_dynamic = False;
1435 if (!ads) return ADS_ERROR(LDAP_SERVER_DOWN);
1437 ret = ADS_ERROR(LDAP_SUCCESS);
1439 if (!escaped_hostname) {
1440 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1443 if (asprintf(&expr, "(samAccountName=%s$)", escaped_hostname) == -1) {
1444 DEBUG(1, ("ads_set_machine_sd: asprintf failed!\n"));
1445 SAFE_FREE(escaped_hostname);
1446 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1449 SAFE_FREE(escaped_hostname);
1451 ret = ads_search(ads, (void *) &res, expr, attrs);
1453 if (!ADS_ERR_OK(ret)) return ret;
1455 if ( !(msg = ads_first_entry(ads, res) )) {
1456 ret = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1457 goto ads_set_sd_error;
1460 if (!ads_pull_sid(ads, msg, attrs[1], &sid)) {
1461 ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
1462 goto ads_set_sd_error;
1465 if (!(ctx = talloc_init("sec_io_desc"))) {
1466 ret = ADS_ERROR(LDAP_NO_MEMORY);
1467 goto ads_set_sd_error;
1470 if (!ads_pull_sd(ads, ctx, msg, attrs[0], &psd)) {
1471 ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
1472 goto ads_set_sd_error;
1475 status = sec_desc_add_sid(ctx, &psd, &sid, SEC_RIGHTS_FULL_CTRL, &sd_size);
1477 if (!NT_STATUS_IS_OK(status)) {
1478 ret = ADS_ERROR_NT(status);
1479 goto ads_set_sd_error;
1482 if (!prs_init(&ps_wire, sd_size, ctx, MARSHALL)) {
1483 ret = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1486 if (!sec_io_desc("sd_wire", &psd, &ps_wire, 1)) {
1487 ret = ADS_ERROR(LDAP_NO_MEMORY);
1488 goto ads_set_sd_error;
1491 #if 0
1492 file_save("/tmp/sec_desc.new", ps_wire.data_p, sd_size);
1493 #endif
1494 if (!(mods = ads_init_mods(ctx))) return ADS_ERROR(LDAP_NO_MEMORY);
1496 bval.bv_len = prs_offset(&ps_wire);
1497 bval.bv_val = talloc(ctx, bval.bv_len);
1498 if (!bval.bv_val) {
1499 ret = ADS_ERROR(LDAP_NO_MEMORY);
1500 goto ads_set_sd_error;
1503 prs_set_offset(&ps_wire, 0);
1505 if (!prs_copy_data_out(bval.bv_val, &ps_wire, bval.bv_len)) {
1506 ret = ADS_ERROR(LDAP_NO_MEMORY);
1507 goto ads_set_sd_error;
1510 ret = ads_mod_ber(ctx, &mods, attrs[0], &bval);
1511 if (ADS_ERR_OK(ret)) {
1512 ret = ads_gen_mod(ads, dn, mods);
1515 ads_set_sd_error:
1516 ads_msgfree(ads, res);
1517 prs_mem_free(&ps_wire);
1518 talloc_free(ctx);
1519 return ret;
1523 * pull the first entry from a ADS result
1524 * @param ads connection to ads server
1525 * @param res Results of search
1526 * @return first entry from result
1528 void *ads_first_entry(ADS_STRUCT *ads, void *res)
1530 return (void *)ldap_first_entry(ads->ld, (LDAPMessage *)res);
1534 * pull the next entry from a ADS result
1535 * @param ads connection to ads server
1536 * @param res Results of search
1537 * @return next entry from result
1539 void *ads_next_entry(ADS_STRUCT *ads, void *res)
1541 return (void *)ldap_next_entry(ads->ld, (LDAPMessage *)res);
1545 * pull a single string from a ADS result
1546 * @param ads connection to ads server
1547 * @param mem_ctx TALLOC_CTX to use for allocating result string
1548 * @param msg Results of search
1549 * @param field Attribute to retrieve
1550 * @return Result string in talloc context
1552 char *ads_pull_string(ADS_STRUCT *ads,
1553 TALLOC_CTX *mem_ctx, void *msg, const char *field)
1555 char **values;
1556 char *ret = NULL;
1557 char *ux_string;
1558 size_t rc;
1560 values = ldap_get_values(ads->ld, msg, field);
1561 if (!values)
1562 return NULL;
1564 if (values[0]) {
1565 rc = pull_utf8_talloc(mem_ctx, &ux_string,
1566 values[0]);
1567 if (rc != (size_t)-1)
1568 ret = ux_string;
1571 ldap_value_free(values);
1572 return ret;
1576 * pull an array of strings from a ADS result
1577 * @param ads connection to ads server
1578 * @param mem_ctx TALLOC_CTX to use for allocating result string
1579 * @param msg Results of search
1580 * @param field Attribute to retrieve
1581 * @return Result strings in talloc context
1583 char **ads_pull_strings(ADS_STRUCT *ads,
1584 TALLOC_CTX *mem_ctx, void *msg, const char *field,
1585 size_t *num_values)
1587 char **values;
1588 char **ret = NULL;
1589 int i;
1591 values = ldap_get_values(ads->ld, msg, field);
1592 if (!values)
1593 return NULL;
1595 *num_values = ldap_count_values(values);
1597 ret = talloc_array(mem_ctx, char *, *num_values+1);
1598 if (!ret) {
1599 ldap_value_free(values);
1600 return NULL;
1603 for (i=0;i<*num_values;i++) {
1604 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
1605 ldap_value_free(values);
1606 return NULL;
1609 ret[i] = NULL;
1611 ldap_value_free(values);
1612 return ret;
1616 * pull an array of strings from a ADS result
1617 * (handle large multivalue attributes with range retrieval)
1618 * @param ads connection to ads server
1619 * @param mem_ctx TALLOC_CTX to use for allocating result string
1620 * @param msg Results of search
1621 * @param field Attribute to retrieve
1622 * @param current_strings strings returned by a previous call to this function
1623 * @param next_attribute The next query should ask for this attribute
1624 * @param num_values How many values did we get this time?
1625 * @param more_values Are there more values to get?
1626 * @return Result strings in talloc context
1628 char **ads_pull_strings_range(ADS_STRUCT *ads,
1629 TALLOC_CTX *mem_ctx,
1630 void *msg, const char *field,
1631 char **current_strings,
1632 const char **next_attribute,
1633 size_t *num_strings,
1634 BOOL *more_strings)
1636 char *attr;
1637 char *expected_range_attrib, *range_attr;
1638 BerElement *ptr = NULL;
1639 char **strings;
1640 char **new_strings;
1641 size_t num_new_strings;
1642 unsigned long int range_start;
1643 unsigned long int range_end;
1645 /* we might have been given the whole lot anyway */
1646 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
1647 *more_strings = False;
1648 return strings;
1651 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
1653 /* look for Range result */
1654 for (attr = ldap_first_attribute(ads->ld, (LDAPMessage *)msg, &ptr);
1655 attr;
1656 attr = ldap_next_attribute(ads->ld, (LDAPMessage *)msg, ptr)) {
1657 /* we ignore the fact that this is utf8, as all attributes are ascii... */
1658 if (strncasecmp(attr, expected_range_attrib, strlen(expected_range_attrib)) == 0) {
1659 range_attr = attr;
1660 break;
1662 ldap_memfree(attr);
1664 if (!attr) {
1665 ber_free(ptr, 0);
1666 /* nothing here - this field is just empty */
1667 *more_strings = False;
1668 return NULL;
1671 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
1672 &range_start, &range_end) == 2) {
1673 *more_strings = True;
1674 } else {
1675 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
1676 &range_start) == 1) {
1677 *more_strings = False;
1678 } else {
1679 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
1680 range_attr));
1681 ldap_memfree(range_attr);
1682 *more_strings = False;
1683 return NULL;
1687 if ((*num_strings) != range_start) {
1688 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
1689 " - aborting range retreival\n",
1690 range_attr, *num_strings + 1, range_start));
1691 ldap_memfree(range_attr);
1692 *more_strings = False;
1693 return NULL;
1696 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
1698 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
1699 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
1700 "strings in this bunch, but we only got %lu - aborting range retreival\n",
1701 range_attr, (unsigned long int)range_end - range_start + 1,
1702 (unsigned long int)num_new_strings));
1703 ldap_memfree(range_attr);
1704 *more_strings = False;
1705 return NULL;
1708 strings = talloc_realloc(current_strings,
1709 sizeof(*current_strings) *
1710 (*num_strings + num_new_strings));
1712 if (strings == NULL) {
1713 ldap_memfree(range_attr);
1714 *more_strings = False;
1715 return NULL;
1718 memcpy(&strings[*num_strings], new_strings,
1719 sizeof(*new_strings) * num_new_strings);
1721 (*num_strings) += num_new_strings;
1723 if (*more_strings) {
1724 *next_attribute = talloc_asprintf(mem_ctx,
1725 "member;range=%d-*",
1726 *num_strings);
1728 if (!*next_attribute) {
1729 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
1730 ldap_memfree(range_attr);
1731 *more_strings = False;
1732 return NULL;
1736 ldap_memfree(range_attr);
1738 return strings;
1742 * pull a single uint32_t from a ADS result
1743 * @param ads connection to ads server
1744 * @param msg Results of search
1745 * @param field Attribute to retrieve
1746 * @param v Pointer to int to store result
1747 * @return boolean inidicating success
1749 BOOL ads_pull_uint32_t(ADS_STRUCT *ads,
1750 void *msg, const char *field, uint32_t *v)
1752 char **values;
1754 values = ldap_get_values(ads->ld, msg, field);
1755 if (!values)
1756 return False;
1757 if (!values[0]) {
1758 ldap_value_free(values);
1759 return False;
1762 *v = atoi(values[0]);
1763 ldap_value_free(values);
1764 return True;
1768 * pull a single objectGUID from an ADS result
1769 * @param ads connection to ADS server
1770 * @param msg results of search
1771 * @param guid 37-byte area to receive text guid
1772 * @return boolean indicating success
1774 BOOL ads_pull_guid(ADS_STRUCT *ads,
1775 void *msg, GUID *guid)
1777 char **values;
1779 values = ldap_get_values(ads->ld, msg, "objectGUID");
1780 if (!values)
1781 return False;
1783 if (values[0]) {
1784 memcpy(guid, values[0], sizeof(GUID));
1785 ldap_value_free(values);
1786 return True;
1788 ldap_value_free(values);
1789 return False;
1795 * pull a single DOM_SID from a ADS result
1796 * @param ads connection to ads server
1797 * @param msg Results of search
1798 * @param field Attribute to retrieve
1799 * @param sid Pointer to sid to store result
1800 * @return boolean inidicating success
1802 BOOL ads_pull_sid(ADS_STRUCT *ads,
1803 void *msg, const char *field, DOM_SID *sid)
1805 struct berval **values;
1806 BOOL ret = False;
1808 values = ldap_get_values_len(ads->ld, msg, field);
1810 if (!values)
1811 return False;
1813 if (values[0])
1814 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
1816 ldap_value_free_len(values);
1817 return ret;
1821 * pull an array of DOM_SIDs from a ADS result
1822 * @param ads connection to ads server
1823 * @param mem_ctx TALLOC_CTX for allocating sid array
1824 * @param msg Results of search
1825 * @param field Attribute to retrieve
1826 * @param sids pointer to sid array to allocate
1827 * @return the count of SIDs pulled
1829 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
1830 void *msg, const char *field, DOM_SID **sids)
1832 struct berval **values;
1833 BOOL ret;
1834 int count, i;
1836 values = ldap_get_values_len(ads->ld, msg, field);
1838 if (!values)
1839 return 0;
1841 for (i=0; values[i]; i++)
1842 /* nop */ ;
1844 (*sids) = talloc_array(mem_ctx, DOM_SID, i);
1845 if (!(*sids)) {
1846 ldap_value_free_len(values);
1847 return 0;
1850 count = 0;
1851 for (i=0; values[i]; i++) {
1852 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
1853 if (ret) {
1854 fstring sid;
1855 DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid, &(*sids)[count])));
1856 count++;
1860 ldap_value_free_len(values);
1861 return count;
1865 * pull a SEC_DESC from a ADS result
1866 * @param ads connection to ads server
1867 * @param mem_ctx TALLOC_CTX for allocating sid array
1868 * @param msg Results of search
1869 * @param field Attribute to retrieve
1870 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
1871 * @return boolean inidicating success
1873 BOOL ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
1874 void *msg, const char *field, SEC_DESC **sd)
1876 struct berval **values;
1877 prs_struct ps;
1878 BOOL ret = False;
1880 values = ldap_get_values_len(ads->ld, msg, field);
1882 if (!values) return False;
1884 if (values[0]) {
1885 prs_init(&ps, values[0]->bv_len, mem_ctx, UNMARSHALL);
1886 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
1887 prs_set_offset(&ps,0);
1889 ret = sec_io_desc("sd", sd, &ps, 1);
1892 ldap_value_free_len(values);
1893 return ret;
1897 * in order to support usernames longer than 21 characters we need to
1898 * use both the sAMAccountName and the userPrincipalName attributes
1899 * It seems that not all users have the userPrincipalName attribute set
1901 * @param ads connection to ads server
1902 * @param mem_ctx TALLOC_CTX for allocating sid array
1903 * @param msg Results of search
1904 * @return the username
1906 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, void *msg)
1908 char *ret, *p;
1910 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
1911 if (ret && (p = strchr(ret, '@'))) {
1912 *p = 0;
1913 return ret;
1915 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
1920 * find the update serial number - this is the core of the ldap cache
1921 * @param ads connection to ads server
1922 * @param ads connection to ADS server
1923 * @param usn Pointer to retrieved update serial number
1924 * @return status of search
1926 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32_t *usn)
1928 const char *attrs[] = {"highestCommittedUSN", NULL};
1929 ADS_STATUS status;
1930 void *res;
1932 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
1933 if (!ADS_ERR_OK(status))
1934 return status;
1936 if (ads_count_replies(ads, res) != 1) {
1937 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1940 ads_pull_uint32_t(ads, res, "highestCommittedUSN", usn);
1941 ads_msgfree(ads, res);
1942 return ADS_SUCCESS;
1945 /* parse a ADS timestring - typical string is
1946 '20020917091222.0Z0' which means 09:12.22 17th September
1947 2002, timezone 0 */
1948 static time_t ads_parse_time(const char *str)
1950 struct tm tm;
1952 ZERO_STRUCT(tm);
1954 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
1955 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
1956 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
1957 return 0;
1959 tm.tm_year -= 1900;
1960 tm.tm_mon -= 1;
1962 return timegm(&tm);
1967 * Find the servers name and realm - this can be done before authentication
1968 * The ldapServiceName field on w2k looks like this:
1969 * vnet3.home.samba.org:win2000-vnet3$@VNET3.HOME.SAMBA.ORG
1970 * @param ads connection to ads server
1971 * @return status of search
1973 ADS_STATUS ads_server_info(ADS_STRUCT *ads)
1975 const char *attrs[] = {"ldapServiceName", "currentTime", NULL};
1976 ADS_STATUS status;
1977 void *res;
1978 char *value;
1979 char *p;
1980 char *timestr;
1981 TALLOC_CTX *ctx;
1983 if (!(ctx = talloc_init("ads_server_info"))) {
1984 return ADS_ERROR(LDAP_NO_MEMORY);
1987 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
1988 if (!ADS_ERR_OK(status)) return status;
1990 value = ads_pull_string(ads, ctx, res, "ldapServiceName");
1991 if (!value) {
1992 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1995 timestr = ads_pull_string(ads, ctx, res, "currentTime");
1996 if (!timestr) {
1997 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2000 ldap_msgfree(res);
2002 p = strchr(value, ':');
2003 if (!p) {
2004 talloc_free(ctx);
2005 DEBUG(1, ("ads_server_info: returned ldap server name did not contain a ':' "
2006 "so was deemed invalid\n"));
2007 return ADS_ERROR(LDAP_DECODING_ERROR);
2010 SAFE_FREE(ads->config.ldap_server_name);
2012 ads->config.ldap_server_name = strdup(p+1);
2013 p = strchr(ads->config.ldap_server_name, '$');
2014 if (!p || p[1] != '@') {
2015 talloc_free(ctx);
2016 DEBUG(1, ("ads_server_info: returned ldap server name (%s) does not contain '$@'"
2017 " so was deemed invalid\n", ads->config.ldap_server_name));
2018 SAFE_FREE(ads->config.ldap_server_name);
2019 return ADS_ERROR(LDAP_DECODING_ERROR);
2022 *p = 0;
2024 SAFE_FREE(ads->config.realm);
2025 SAFE_FREE(ads->config.bind_path);
2027 ads->config.realm = strdup(p+2);
2028 ads->config.bind_path = ads_build_dn(ads->config.realm);
2030 DEBUG(3,("got ldap server name %s@%s, using bind path: %s\n",
2031 ads->config.ldap_server_name, ads->config.realm,
2032 ads->config.bind_path));
2034 ads->config.current_time = ads_parse_time(timestr);
2036 if (ads->config.current_time != 0) {
2037 ads->auth.time_offset = ads->config.current_time - time(NULL);
2038 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2041 talloc_free(ctx);
2043 return ADS_SUCCESS;
2047 * find the domain sid for our domain
2048 * @param ads connection to ads server
2049 * @param sid Pointer to domain sid
2050 * @return status of search
2052 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2054 const char *attrs[] = {"objectSid", NULL};
2055 void *res;
2056 ADS_STATUS rc;
2058 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2059 attrs, &res);
2060 if (!ADS_ERR_OK(rc)) return rc;
2061 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2062 return ADS_ERROR_SYSTEM(ENOENT);
2064 ads_msgfree(ads, res);
2066 return ADS_SUCCESS;
2069 /* this is rather complex - we need to find the allternate (netbios) name
2070 for the domain, but there isn't a simple query to do this. Instead
2071 we look for the principle names on the DCs account and find one that has
2072 the right form, then extract the netbios name of the domain from that
2074 NOTE! better method is this:
2076 bin/net -Uadministrator%XXXXX ads search '(&(objectclass=crossref)(dnsroot=VNET3.HOME.SAMBA.ORG))' nETBIOSName
2078 but you need to force the bind path to match the configurationNamingContext from the rootDSE
2081 ADS_STATUS ads_workgroup_name(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **workgroup)
2083 char *expr;
2084 ADS_STATUS rc;
2085 char **principles;
2086 char *prefix;
2087 int prefix_length;
2088 int i;
2089 void *res;
2090 const char *attrs[] = {"servicePrincipalName", NULL};
2091 int num_principals;
2093 (*workgroup) = NULL;
2095 asprintf(&expr, "(&(objectclass=computer)(dnshostname=%s.%s))",
2096 ads->config.ldap_server_name, ads->config.realm);
2097 rc = ads_search(ads, &res, expr, attrs);
2098 free(expr);
2100 if (!ADS_ERR_OK(rc)) {
2101 return rc;
2104 principles = ads_pull_strings(ads, mem_ctx, res,
2105 "servicePrincipalName", &num_principals);
2107 ads_msgfree(ads, res);
2109 if (!principles) {
2110 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2113 asprintf(&prefix, "HOST/%s.%s/",
2114 ads->config.ldap_server_name,
2115 ads->config.realm);
2117 prefix_length = strlen(prefix);
2119 for (i=0;principles[i]; i++) {
2120 if (strnequal(principles[i], prefix, prefix_length) &&
2121 !strequal(ads->config.realm, principles[i]+prefix_length) &&
2122 !strchr(principles[i]+prefix_length, '.')) {
2123 /* found an alternate (short) name for the domain. */
2124 DEBUG(3,("Found alternate name '%s' for realm '%s'\n",
2125 principles[i]+prefix_length,
2126 ads->config.realm));
2127 (*workgroup) = talloc_strdup(mem_ctx, principles[i]+prefix_length);
2128 break;
2131 free(prefix);
2133 if (!*workgroup) {
2134 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2137 return ADS_SUCCESS;
2140 #endif