Merge from Samba 3.0:
[Samba/gebeck_regimport.git] / source3 / libads / ldap.c
blob48401cc3d803f4c20418e881d32088a3d82ebdcd
1 /*
2 Unix SMB/CIFS implementation.
3 ads (active directory) utility library
4 Copyright (C) Andrew Tridgell 2001
5 Copyright (C) Remus Koos 2001
6 Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 #include "includes.h"
25 #ifdef HAVE_LDAP
27 /**
28 * @file ldap.c
29 * @brief basic ldap client-side routines for ads server communications
31 * The routines contained here should do the necessary ldap calls for
32 * ads setups.
34 * Important note: attribute names passed into ads_ routines must
35 * already be in UTF-8 format. We do not convert them because in almost
36 * all cases, they are just ascii (which is represented with the same
37 * codepoints in UTF-8). This may have to change at some point
38 **/
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;
109 BOOL use_own_domain = False;
111 /* if the realm and workgroup are both empty, assume they are ours */
113 /* realm */
114 c_realm = ads->server.realm;
116 if ( !c_realm || !*c_realm ) {
117 /* special case where no realm and no workgroup means our own */
118 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
119 use_own_domain = True;
120 c_realm = lp_realm();
124 if (c_realm && *c_realm)
125 got_realm = True;
127 again:
128 /* we need to try once with the realm name and fallback to the
129 netbios domain name if we fail (if netbios has not been disabled */
131 if ( !got_realm && !lp_disable_netbios() ) {
132 c_realm = ads->server.workgroup;
133 if (!c_realm || !*c_realm) {
134 if ( use_own_domain )
135 c_realm = lp_workgroup();
138 if ( !c_realm || !*c_realm ) {
139 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
140 return False;
144 pstrcpy( realm, c_realm );
146 DEBUG(6,("ads_find_dc: looking for %s '%s'\n",
147 (got_realm ? "realm" : "domain"), realm));
149 if ( !get_sorted_dc_list(realm, &ip_list, &count, got_realm) ) {
150 /* fall back to netbios if we can */
151 if ( got_realm && !lp_disable_netbios() ) {
152 got_realm = False;
153 goto again;
156 return False;
159 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
160 for ( i=0; i<count; i++ ) {
161 /* since this is an ads conection request, default to LDAP_PORT is not set */
162 int port = (ip_list[i].port!=PORT_NONE) ? ip_list[i].port : LDAP_PORT;
163 fstring server;
165 fstrcpy( server, inet_ntoa(ip_list[i].ip) );
167 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
168 continue;
170 if ( ads_try_connect(ads, server, port) ) {
171 SAFE_FREE(ip_list);
172 return True;
175 /* keep track of failures */
176 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
179 SAFE_FREE(ip_list);
181 return False;
186 * Connect to the LDAP server
187 * @param ads Pointer to an existing ADS_STRUCT
188 * @return status of connection
190 ADS_STATUS ads_connect(ADS_STRUCT *ads)
192 int version = LDAP_VERSION3;
193 ADS_STATUS status;
195 ads->last_attempt = time(NULL);
196 ads->ld = NULL;
198 /* try with a URL based server */
200 if (ads->server.ldap_uri &&
201 ads_try_connect_uri(ads)) {
202 goto got_connection;
205 /* try with a user specified server */
206 if (ads->server.ldap_server &&
207 ads_try_connect(ads, ads->server.ldap_server, LDAP_PORT)) {
208 goto got_connection;
211 if (ads_find_dc(ads)) {
212 goto got_connection;
215 return ADS_ERROR_SYSTEM(errno?errno:ENOENT);
217 got_connection:
218 DEBUG(3,("Connected to LDAP server %s\n", inet_ntoa(ads->ldap_ip)));
220 status = ads_server_info(ads);
221 if (!ADS_ERR_OK(status)) {
222 DEBUG(1,("Failed to get ldap server info\n"));
223 return status;
226 ldap_set_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
228 if (!ads->auth.user_name) {
229 /* by default use the machine account */
230 fstring myname;
231 fstrcpy(myname, global_myname());
232 strlower_m(myname);
233 asprintf(&ads->auth.user_name, "HOST/%s", myname);
236 if (!ads->auth.realm) {
237 ads->auth.realm = strdup(ads->config.realm);
240 if (!ads->auth.kdc_server) {
241 ads->auth.kdc_server = strdup(inet_ntoa(ads->ldap_ip));
244 #if KRB5_DNS_HACK
245 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
246 to MIT kerberos to work (tridge) */
248 char *env;
249 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
250 setenv(env, ads->auth.kdc_server, 1);
251 free(env);
253 #endif
255 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
256 return ADS_SUCCESS;
259 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
260 return ADS_ERROR(ldap_simple_bind_s( ads->ld, NULL, NULL));
263 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
264 return ADS_ERROR(ldap_simple_bind_s( ads->ld, ads->auth.user_name, ads->auth.password));
267 return ads_sasl_bind(ads);
271 Duplicate a struct berval into talloc'ed memory
273 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
275 struct berval *value;
277 if (!in_val) return NULL;
279 value = talloc_zero(ctx, sizeof(struct berval));
280 if (value == NULL)
281 return NULL;
282 if (in_val->bv_len == 0) return value;
284 value->bv_len = in_val->bv_len;
285 value->bv_val = talloc_memdup(ctx, in_val->bv_val, in_val->bv_len);
286 return value;
290 Make a values list out of an array of (struct berval *)
292 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
293 const struct berval **in_vals)
295 struct berval **values;
296 int i;
298 if (!in_vals) return NULL;
299 for (i=0; in_vals[i]; i++); /* count values */
300 values = (struct berval **) talloc_zero(ctx,
301 (i+1)*sizeof(struct berval *));
302 if (!values) return NULL;
304 for (i=0; in_vals[i]; i++) {
305 values[i] = dup_berval(ctx, in_vals[i]);
307 return values;
311 UTF8-encode a values list out of an array of (char *)
313 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
315 char **values;
316 int i;
318 if (!in_vals) return NULL;
319 for (i=0; in_vals[i]; i++); /* count values */
320 values = (char ** ) talloc_zero(ctx, (i+1)*sizeof(char *));
321 if (!values) return NULL;
323 for (i=0; in_vals[i]; i++) {
324 push_utf8_talloc(ctx, &values[i], in_vals[i]);
326 return values;
330 Pull a (char *) array out of a UTF8-encoded values list
332 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
334 char **values;
335 int i;
337 if (!in_vals) return NULL;
338 for (i=0; in_vals[i]; i++); /* count values */
339 values = (char **) talloc_zero(ctx, (i+1)*sizeof(char *));
340 if (!values) return NULL;
342 for (i=0; in_vals[i]; i++) {
343 pull_utf8_talloc(ctx, &values[i], in_vals[i]);
345 return values;
349 * Do a search with paged results. cookie must be null on the first
350 * call, and then returned on each subsequent call. It will be null
351 * again when the entire search is complete
352 * @param ads connection to ads server
353 * @param bind_path Base dn for the search
354 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
355 * @param expr Search expression - specified in local charset
356 * @param attrs Attributes to retrieve - specified in utf8 or ascii
357 * @param res ** which will contain results - free res* with ads_msgfree()
358 * @param count Number of entries retrieved on this page
359 * @param cookie The paged results cookie to be returned on subsequent calls
360 * @return status of search
362 ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
363 int scope, const char *expr,
364 const char **attrs, void **res,
365 int *count, void **cookie)
367 int rc, i, version;
368 char *utf8_expr, *utf8_path, **search_attrs;
369 LDAPControl PagedResults, NoReferrals, *controls[3], **rcontrols;
370 BerElement *cookie_be = NULL;
371 struct berval *cookie_bv= NULL;
372 TALLOC_CTX *ctx;
374 *res = NULL;
376 if (!(ctx = talloc_init("ads_do_paged_search")))
377 return ADS_ERROR(LDAP_NO_MEMORY);
379 /* 0 means the conversion worked but the result was empty
380 so we only fail if it's -1. In any case, it always
381 at least nulls out the dest */
382 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
383 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
384 rc = LDAP_NO_MEMORY;
385 goto done;
388 if (!attrs || !(*attrs))
389 search_attrs = NULL;
390 else {
391 /* This would be the utf8-encoded version...*/
392 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
393 if (!(str_list_copy(&search_attrs, attrs))) {
394 rc = LDAP_NO_MEMORY;
395 goto done;
400 /* Paged results only available on ldap v3 or later */
401 ldap_get_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
402 if (version < LDAP_VERSION3) {
403 rc = LDAP_NOT_SUPPORTED;
404 goto done;
407 cookie_be = ber_alloc_t(LBER_USE_DER);
408 if (cookie && *cookie) {
409 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
410 ber_bvfree(*cookie); /* don't need it from last time */
411 *cookie = NULL;
412 } else {
413 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
415 ber_flatten(cookie_be, &cookie_bv);
416 PagedResults.ldctl_oid = ADS_PAGE_CTL_OID;
417 PagedResults.ldctl_iscritical = (char) 1;
418 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
419 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
421 NoReferrals.ldctl_oid = ADS_NO_REFERRALS_OID;
422 NoReferrals.ldctl_iscritical = (char) 0;
423 NoReferrals.ldctl_value.bv_len = 0;
424 NoReferrals.ldctl_value.bv_val = "";
427 controls[0] = &NoReferrals;
428 controls[1] = &PagedResults;
429 controls[2] = NULL;
431 *res = NULL;
433 /* we need to disable referrals as the openldap libs don't
434 handle them and paged results at the same time. Using them
435 together results in the result record containing the server
436 page control being removed from the result list (tridge/jmcd)
438 leaving this in despite the control that says don't generate
439 referrals, in case the server doesn't support it (jmcd)
441 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
443 rc = ldap_search_ext_s(ads->ld, utf8_path, scope, utf8_expr,
444 search_attrs, 0, controls,
445 NULL, NULL, LDAP_NO_LIMIT, (LDAPMessage **)res);
447 ber_free(cookie_be, 1);
448 ber_bvfree(cookie_bv);
450 if (rc) {
451 DEBUG(3,("ldap_search_ext_s(%s) -> %s\n", expr, ldap_err2string(rc)));
452 goto done;
455 rc = ldap_parse_result(ads->ld, *res, NULL, NULL, NULL,
456 NULL, &rcontrols, 0);
458 if (!rcontrols) {
459 goto done;
462 for (i=0; rcontrols[i]; i++) {
463 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
464 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
465 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
466 &cookie_bv);
467 /* the berval is the cookie, but must be freed when
468 it is all done */
469 if (cookie_bv->bv_len) /* still more to do */
470 *cookie=ber_bvdup(cookie_bv);
471 else
472 *cookie=NULL;
473 ber_bvfree(cookie_bv);
474 ber_free(cookie_be, 1);
475 break;
478 ldap_controls_free(rcontrols);
480 done:
481 talloc_destroy(ctx);
482 /* if/when we decide to utf8-encode attrs, take out this next line */
483 str_list_free(&search_attrs);
485 return ADS_ERROR(rc);
490 * Get all results for a search. This uses ads_do_paged_search() to return
491 * all entries in a large search.
492 * @param ads connection to ads server
493 * @param bind_path Base dn for the search
494 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
495 * @param expr Search expression
496 * @param attrs Attributes to retrieve
497 * @param res ** which will contain results - free res* with ads_msgfree()
498 * @return status of search
500 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
501 int scope, const char *expr,
502 const char **attrs, void **res)
504 void *cookie = NULL;
505 int count = 0;
506 ADS_STATUS status;
508 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, res,
509 &count, &cookie);
511 if (!ADS_ERR_OK(status)) return status;
513 while (cookie) {
514 void *res2 = NULL;
515 ADS_STATUS status2;
516 LDAPMessage *msg, *next;
518 status2 = ads_do_paged_search(ads, bind_path, scope, expr,
519 attrs, &res2, &count, &cookie);
521 if (!ADS_ERR_OK(status2)) break;
523 /* this relies on the way that ldap_add_result_entry() works internally. I hope
524 that this works on all ldap libs, but I have only tested with openldap */
525 for (msg = ads_first_entry(ads, res2); msg; msg = next) {
526 next = ads_next_entry(ads, msg);
527 ldap_add_result_entry((LDAPMessage **)res, msg);
529 /* note that we do not free res2, as the memory is now
530 part of the main returned list */
533 return status;
537 * Run a function on all results for a search. Uses ads_do_paged_search() and
538 * runs the function as each page is returned, using ads_process_results()
539 * @param ads connection to ads server
540 * @param bind_path Base dn for the search
541 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
542 * @param expr Search expression - specified in local charset
543 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
544 * @param fn Function which takes attr name, values list, and data_area
545 * @param data_area Pointer which is passed to function on each call
546 * @return status of search
548 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
549 int scope, const char *expr, const char **attrs,
550 BOOL(*fn)(char *, void **, void *),
551 void *data_area)
553 void *cookie = NULL;
554 int count = 0;
555 ADS_STATUS status;
556 void *res;
558 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
559 &count, &cookie);
561 if (!ADS_ERR_OK(status)) return status;
563 ads_process_results(ads, res, fn, data_area);
564 ads_msgfree(ads, res);
566 while (cookie) {
567 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
568 &res, &count, &cookie);
570 if (!ADS_ERR_OK(status)) break;
572 ads_process_results(ads, res, fn, data_area);
573 ads_msgfree(ads, res);
576 return status;
580 * Do a search with a timeout.
581 * @param ads connection to ads server
582 * @param bind_path Base dn for the search
583 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
584 * @param expr Search expression
585 * @param attrs Attributes to retrieve
586 * @param res ** which will contain results - free res* with ads_msgfree()
587 * @return status of search
589 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
590 const char *expr,
591 const char **attrs, void **res)
593 struct timeval timeout;
594 int rc;
595 char *utf8_expr, *utf8_path, **search_attrs = NULL;
596 TALLOC_CTX *ctx;
598 if (!(ctx = talloc_init("ads_do_search"))) {
599 DEBUG(1,("ads_do_search: talloc_init() failed!"));
600 return ADS_ERROR(LDAP_NO_MEMORY);
603 /* 0 means the conversion worked but the result was empty
604 so we only fail if it's negative. In any case, it always
605 at least nulls out the dest */
606 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
607 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
608 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
609 rc = LDAP_NO_MEMORY;
610 goto done;
613 if (!attrs || !(*attrs))
614 search_attrs = NULL;
615 else {
616 /* This would be the utf8-encoded version...*/
617 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
618 if (!(str_list_copy(&search_attrs, attrs)))
620 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
621 rc = LDAP_NO_MEMORY;
622 goto done;
626 timeout.tv_sec = ADS_SEARCH_TIMEOUT;
627 timeout.tv_usec = 0;
628 *res = NULL;
630 /* see the note in ads_do_paged_search - we *must* disable referrals */
631 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
633 rc = ldap_search_ext_s(ads->ld, utf8_path, scope, utf8_expr,
634 search_attrs, 0, NULL, NULL,
635 &timeout, LDAP_NO_LIMIT, (LDAPMessage **)res);
637 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
638 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
639 rc = 0;
642 done:
643 talloc_destroy(ctx);
644 /* if/when we decide to utf8-encode attrs, take out this next line */
645 str_list_free(&search_attrs);
646 return ADS_ERROR(rc);
649 * Do a general ADS search
650 * @param ads connection to ads server
651 * @param res ** which will contain results - free res* with ads_msgfree()
652 * @param expr Search expression
653 * @param attrs Attributes to retrieve
654 * @return status of search
656 ADS_STATUS ads_search(ADS_STRUCT *ads, void **res,
657 const char *expr,
658 const char **attrs)
660 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
661 expr, attrs, res);
665 * Do a search on a specific DistinguishedName
666 * @param ads connection to ads server
667 * @param res ** which will contain results - free res* with ads_msgfree()
668 * @param dn DistinguishName to search
669 * @param attrs Attributes to retrieve
670 * @return status of search
672 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, void **res,
673 const char *dn,
674 const char **attrs)
676 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)", attrs, res);
680 * Free up memory from a ads_search
681 * @param ads connection to ads server
682 * @param msg Search results to free
684 void ads_msgfree(ADS_STRUCT *ads, void *msg)
686 if (!msg) return;
687 ldap_msgfree(msg);
691 * Free up memory from various ads requests
692 * @param ads connection to ads server
693 * @param mem Area to free
695 void ads_memfree(ADS_STRUCT *ads, void *mem)
697 SAFE_FREE(mem);
701 * Get a dn from search results
702 * @param ads connection to ads server
703 * @param msg Search result
704 * @return dn string
706 char *ads_get_dn(ADS_STRUCT *ads, void *msg)
708 char *utf8_dn, *unix_dn;
710 utf8_dn = ldap_get_dn(ads->ld, msg);
712 if (!utf8_dn) {
713 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
714 return NULL;
717 if (pull_utf8_allocate((void **) &unix_dn, utf8_dn) == (size_t)-1) {
718 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
719 utf8_dn ));
720 return NULL;
722 ldap_memfree(utf8_dn);
723 return unix_dn;
727 * Find a machine account given a hostname
728 * @param ads connection to ads server
729 * @param res ** which will contain results - free res* with ads_msgfree()
730 * @param host Hostname to search for
731 * @return status of search
733 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, void **res, const char *host)
735 ADS_STATUS status;
736 char *expr;
737 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
739 /* the easiest way to find a machine account anywhere in the tree
740 is to look for hostname$ */
741 if (asprintf(&expr, "(samAccountName=%s$)", host) == -1) {
742 DEBUG(1, ("asprintf failed!\n"));
743 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
746 status = ads_search(ads, res, expr, attrs);
747 free(expr);
748 return status;
752 * Initialize a list of mods to be used in a modify request
753 * @param ctx An initialized TALLOC_CTX
754 * @return allocated ADS_MODLIST
756 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
758 #define ADS_MODLIST_ALLOC_SIZE 10
759 LDAPMod **mods;
761 if ((mods = (LDAPMod **) talloc_zero(ctx, sizeof(LDAPMod *) *
762 (ADS_MODLIST_ALLOC_SIZE + 1))))
763 /* -1 is safety to make sure we don't go over the end.
764 need to reset it to NULL before doing ldap modify */
765 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
767 return mods;
772 add an attribute to the list, with values list already constructed
774 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
775 int mod_op, const char *name,
776 const void **invals)
778 int curmod;
779 LDAPMod **modlist = (LDAPMod **) *mods;
780 struct berval **ber_values = NULL;
781 char **char_values = NULL;
783 if (!invals) {
784 mod_op = LDAP_MOD_DELETE;
785 } else {
786 if (mod_op & LDAP_MOD_BVALUES)
787 ber_values = ads_dup_values(ctx,
788 (const struct berval **)invals);
789 else
790 char_values = ads_push_strvals(ctx,
791 (const char **) invals);
794 /* find the first empty slot */
795 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
796 curmod++);
797 if (modlist[curmod] == (LDAPMod *) -1) {
798 if (!(modlist = talloc_realloc(ctx, modlist,
799 (curmod+ADS_MODLIST_ALLOC_SIZE+1)*sizeof(LDAPMod *))))
800 return ADS_ERROR(LDAP_NO_MEMORY);
801 memset(&modlist[curmod], 0,
802 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
803 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
804 *mods = modlist;
807 if (!(modlist[curmod] = talloc_zero(ctx, sizeof(LDAPMod))))
808 return ADS_ERROR(LDAP_NO_MEMORY);
809 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
810 if (mod_op & LDAP_MOD_BVALUES) {
811 modlist[curmod]->mod_bvalues = ber_values;
812 } else if (mod_op & LDAP_MOD_DELETE) {
813 modlist[curmod]->mod_values = NULL;
814 } else {
815 modlist[curmod]->mod_values = char_values;
818 modlist[curmod]->mod_op = mod_op;
819 return ADS_ERROR(LDAP_SUCCESS);
823 * Add a single string value to a mod list
824 * @param ctx An initialized TALLOC_CTX
825 * @param mods An initialized ADS_MODLIST
826 * @param name The attribute name to add
827 * @param val The value to add - NULL means DELETE
828 * @return ADS STATUS indicating success of add
830 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
831 const char *name, const char *val)
833 const char *values[2];
835 values[0] = val;
836 values[1] = NULL;
838 if (!val)
839 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
840 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name,
841 (const void **) values);
845 * Add an array of string values to a mod list
846 * @param ctx An initialized TALLOC_CTX
847 * @param mods An initialized ADS_MODLIST
848 * @param name The attribute name to add
849 * @param vals The array of string values to add - NULL means DELETE
850 * @return ADS STATUS indicating success of add
852 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
853 const char *name, const char **vals)
855 if (!vals)
856 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
857 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
858 name, (const void **) vals);
862 * Add a single ber-encoded value to a mod list
863 * @param ctx An initialized TALLOC_CTX
864 * @param mods An initialized ADS_MODLIST
865 * @param name The attribute name to add
866 * @param val The value to add - NULL means DELETE
867 * @return ADS STATUS indicating success of add
869 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
870 const char *name, const struct berval *val)
872 const struct berval *values[2];
874 values[0] = val;
875 values[1] = NULL;
876 if (!val)
877 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
878 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
879 name, (const void **) values);
883 * Perform an ldap modify
884 * @param ads connection to ads server
885 * @param mod_dn DistinguishedName to modify
886 * @param mods list of modifications to perform
887 * @return status of modify
889 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
891 int ret,i;
892 char *utf8_dn = NULL;
894 this control is needed to modify that contains a currently
895 non-existent attribute (but allowable for the object) to run
897 LDAPControl PermitModify = {
898 ADS_PERMIT_MODIFY_OID,
899 {0, NULL},
900 (char) 1};
901 LDAPControl *controls[2];
903 controls[0] = &PermitModify;
904 controls[1] = NULL;
906 if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) {
907 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
910 /* find the end of the list, marked by NULL or -1 */
911 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
912 /* make sure the end of the list is NULL */
913 mods[i] = NULL;
914 ret = ldap_modify_ext_s(ads->ld, utf8_dn,
915 (LDAPMod **) mods, controls, NULL);
916 SAFE_FREE(utf8_dn);
917 return ADS_ERROR(ret);
921 * Perform an ldap add
922 * @param ads connection to ads server
923 * @param new_dn DistinguishedName to add
924 * @param mods list of attributes and values for DN
925 * @return status of add
927 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
929 int ret, i;
930 char *utf8_dn = NULL;
932 if (push_utf8_allocate(&utf8_dn, new_dn) == -1) {
933 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
934 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
937 /* find the end of the list, marked by NULL or -1 */
938 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
939 /* make sure the end of the list is NULL */
940 mods[i] = NULL;
942 ret = ldap_add_s(ads->ld, utf8_dn, mods);
943 SAFE_FREE(utf8_dn);
944 return ADS_ERROR(ret);
948 * Delete a DistinguishedName
949 * @param ads connection to ads server
950 * @param new_dn DistinguishedName to delete
951 * @return status of delete
953 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
955 int ret;
956 char *utf8_dn = NULL;
957 if (push_utf8_allocate(&utf8_dn, del_dn) == -1) {
958 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
959 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
962 ret = ldap_delete_s(ads->ld, utf8_dn);
963 return ADS_ERROR(ret);
967 * Build an org unit string
968 * if org unit is Computers or blank then assume a container, otherwise
969 * assume a \ separated list of organisational units
970 * @param org_unit Organizational unit
971 * @return org unit string - caller must free
973 char *ads_ou_string(const char *org_unit)
975 if (!org_unit || !*org_unit || strcasecmp(org_unit, "Computers") == 0) {
976 return strdup("cn=Computers");
979 return ads_build_path(org_unit, "\\/", "ou=", 1);
985 add a machine account to the ADS server
987 static ADS_STATUS ads_add_machine_acct(ADS_STRUCT *ads, const char *hostname,
988 uint32 account_type,
989 const char *org_unit)
991 ADS_STATUS ret, status;
992 char *host_spn, *host_upn, *new_dn, *samAccountName, *controlstr;
993 char *ou_str;
994 TALLOC_CTX *ctx;
995 ADS_MODLIST mods;
996 const char *objectClass[] = {"top", "person", "organizationalPerson",
997 "user", "computer", NULL};
998 const char *servicePrincipalName[5] = {NULL, NULL, NULL, NULL, NULL};
999 char *psp, *psp2;
1000 unsigned acct_control;
1002 if (!(ctx = talloc_init("machine_account")))
1003 return ADS_ERROR(LDAP_NO_MEMORY);
1005 ret = ADS_ERROR(LDAP_NO_MEMORY);
1007 if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", hostname)))
1008 goto done;
1009 if (!(host_upn = talloc_asprintf(ctx, "%s@%s", host_spn, ads->config.realm)))
1010 goto done;
1011 ou_str = ads_ou_string(org_unit);
1012 if (!ou_str) {
1013 DEBUG(1, ("ads_ou_string returned NULL (malloc failure?)\n"));
1014 goto done;
1016 new_dn = talloc_asprintf(ctx, "cn=%s,%s,%s", hostname, ou_str,
1017 ads->config.bind_path);
1018 servicePrincipalName[0] = talloc_asprintf(ctx, "HOST/%s", hostname);
1019 psp = talloc_asprintf(ctx, "HOST/%s.%s",
1020 hostname,
1021 ads->config.realm);
1022 strlower_m(&psp[5]);
1023 servicePrincipalName[1] = psp;
1024 servicePrincipalName[2] = talloc_asprintf(ctx, "CIFS/%s", hostname);
1025 psp2 = talloc_asprintf(ctx, "CIFS/%s.%s",
1026 hostname,
1027 ads->config.realm);
1028 strlower_m(&psp2[5]);
1029 servicePrincipalName[3] = psp2;
1031 free(ou_str);
1032 if (!new_dn)
1033 goto done;
1035 if (!(samAccountName = talloc_asprintf(ctx, "%s$", hostname)))
1036 goto done;
1038 acct_control = account_type | UF_DONT_EXPIRE_PASSWD;
1039 #ifndef ENCTYPE_ARCFOUR_HMAC
1040 acct_control |= UF_USE_DES_KEY_ONLY;
1041 #endif
1043 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control)))
1044 goto done;
1046 if (!(mods = ads_init_mods(ctx)))
1047 goto done;
1049 ads_mod_str(ctx, &mods, "cn", hostname);
1050 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1051 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1052 ads_mod_str(ctx, &mods, "userPrincipalName", host_upn);
1053 ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1054 ads_mod_str(ctx, &mods, "dNSHostName", hostname);
1055 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1056 ads_mod_str(ctx, &mods, "operatingSystem", "Samba");
1057 ads_mod_str(ctx, &mods, "operatingSystemVersion", SAMBA_VERSION_STRING);
1059 ret = ads_gen_add(ads, new_dn, mods);
1061 if (!ADS_ERR_OK(ret))
1062 goto done;
1064 /* Do not fail if we can't set security descriptor
1065 * it shouldn't be mandatory and probably we just
1066 * don't have enough rights to do it.
1068 status = ads_set_machine_sd(ads, hostname, new_dn);
1070 if (!ADS_ERR_OK(status)) {
1071 DEBUG(0, ("Warning: ads_set_machine_sd: %s\n",
1072 ads_errstr(status)));
1074 done:
1075 talloc_destroy(ctx);
1076 return ret;
1080 dump a binary result from ldap
1082 static void dump_binary(const char *field, struct berval **values)
1084 int i, j;
1085 for (i=0; values[i]; i++) {
1086 printf("%s: ", field);
1087 for (j=0; j<values[i]->bv_len; j++) {
1088 printf("%02X", (unsigned char)values[i]->bv_val[j]);
1090 printf("\n");
1094 struct uuid {
1095 uint32 i1;
1096 uint16 i2;
1097 uint16 i3;
1098 uint8 s[8];
1101 static void dump_guid(const char *field, struct berval **values)
1103 int i;
1104 GUID guid;
1105 for (i=0; values[i]; i++) {
1106 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1107 printf("%s: %s\n", field, smb_uuid_string_static(guid));
1112 dump a sid result from ldap
1114 static void dump_sid(const char *field, struct berval **values)
1116 int i;
1117 for (i=0; values[i]; i++) {
1118 DOM_SID sid;
1119 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1120 printf("%s: %s\n", field, sid_string_static(&sid));
1125 dump ntSecurityDescriptor
1127 static void dump_sd(const char *filed, struct berval **values)
1129 prs_struct ps;
1131 SEC_DESC *psd = 0;
1132 TALLOC_CTX *ctx = 0;
1134 if (!(ctx = talloc_init("sec_io_desc")))
1135 return;
1137 /* prepare data */
1138 prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
1139 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
1140 prs_set_offset(&ps,0);
1142 /* parse secdesc */
1143 if (!sec_io_desc("sd", &psd, &ps, 1)) {
1144 prs_mem_free(&ps);
1145 talloc_destroy(ctx);
1146 return;
1148 if (psd) ads_disp_sd(psd);
1150 prs_mem_free(&ps);
1151 talloc_destroy(ctx);
1155 dump a string result from ldap
1157 static void dump_string(const char *field, char **values)
1159 int i;
1160 for (i=0; values[i]; i++) {
1161 printf("%s: %s\n", field, values[i]);
1166 dump a field from LDAP on stdout
1167 used for debugging
1170 static BOOL ads_dump_field(char *field, void **values, void *data_area)
1172 const struct {
1173 const char *name;
1174 BOOL string;
1175 void (*handler)(const char *, struct berval **);
1176 } handlers[] = {
1177 {"objectGUID", False, dump_guid},
1178 {"nTSecurityDescriptor", False, dump_sd},
1179 {"dnsRecord", False, dump_binary},
1180 {"objectSid", False, dump_sid},
1181 {"tokenGroups", False, dump_sid},
1182 {NULL, True, NULL}
1184 int i;
1186 if (!field) { /* must be end of an entry */
1187 printf("\n");
1188 return False;
1191 for (i=0; handlers[i].name; i++) {
1192 if (StrCaseCmp(handlers[i].name, field) == 0) {
1193 if (!values) /* first time, indicate string or not */
1194 return handlers[i].string;
1195 handlers[i].handler(field, (struct berval **) values);
1196 break;
1199 if (!handlers[i].name) {
1200 if (!values) /* first time, indicate string conversion */
1201 return True;
1202 dump_string(field, (char **)values);
1204 return False;
1208 * Dump a result from LDAP on stdout
1209 * used for debugging
1210 * @param ads connection to ads server
1211 * @param res Results to dump
1214 void ads_dump(ADS_STRUCT *ads, void *res)
1216 ads_process_results(ads, res, ads_dump_field, NULL);
1220 * Walk through results, calling a function for each entry found.
1221 * The function receives a field name, a berval * array of values,
1222 * and a data area passed through from the start. The function is
1223 * called once with null for field and values at the end of each
1224 * entry.
1225 * @param ads connection to ads server
1226 * @param res Results to process
1227 * @param fn Function for processing each result
1228 * @param data_area user-defined area to pass to function
1230 void ads_process_results(ADS_STRUCT *ads, void *res,
1231 BOOL(*fn)(char *, void **, void *),
1232 void *data_area)
1234 void *msg;
1235 TALLOC_CTX *ctx;
1237 if (!(ctx = talloc_init("ads_process_results")))
1238 return;
1240 for (msg = ads_first_entry(ads, res); msg;
1241 msg = ads_next_entry(ads, msg)) {
1242 char *utf8_field;
1243 BerElement *b;
1245 for (utf8_field=ldap_first_attribute(ads->ld,
1246 (LDAPMessage *)msg,&b);
1247 utf8_field;
1248 utf8_field=ldap_next_attribute(ads->ld,
1249 (LDAPMessage *)msg,b)) {
1250 struct berval **ber_vals;
1251 char **str_vals, **utf8_vals;
1252 char *field;
1253 BOOL string;
1255 pull_utf8_talloc(ctx, &field, utf8_field);
1256 string = fn(field, NULL, data_area);
1258 if (string) {
1259 utf8_vals = ldap_get_values(ads->ld,
1260 (LDAPMessage *)msg, field);
1261 str_vals = ads_pull_strvals(ctx,
1262 (const char **) utf8_vals);
1263 fn(field, (void **) str_vals, data_area);
1264 ldap_value_free(utf8_vals);
1265 } else {
1266 ber_vals = ldap_get_values_len(ads->ld,
1267 (LDAPMessage *)msg, field);
1268 fn(field, (void **) ber_vals, data_area);
1270 ldap_value_free_len(ber_vals);
1272 ldap_memfree(utf8_field);
1274 ber_free(b, 0);
1275 talloc_destroy_pool(ctx);
1276 fn(NULL, NULL, data_area); /* completed an entry */
1279 talloc_destroy(ctx);
1283 * count how many replies are in a LDAPMessage
1284 * @param ads connection to ads server
1285 * @param res Results to count
1286 * @return number of replies
1288 int ads_count_replies(ADS_STRUCT *ads, void *res)
1290 return ldap_count_entries(ads->ld, (LDAPMessage *)res);
1294 * Join a machine to a realm
1295 * Creates the machine account and sets the machine password
1296 * @param ads connection to ads server
1297 * @param hostname name of host to add
1298 * @param org_unit Organizational unit to place machine in
1299 * @return status of join
1301 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *hostname,
1302 uint32 account_type, const char *org_unit)
1304 ADS_STATUS status;
1305 LDAPMessage *res;
1306 char *host;
1308 /* hostname must be lowercase */
1309 host = strdup(hostname);
1310 strlower_m(host);
1312 status = ads_find_machine_acct(ads, (void **)&res, host);
1313 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1314 DEBUG(0, ("Host account for %s already exists - deleting old account\n", host));
1315 status = ads_leave_realm(ads, host);
1316 if (!ADS_ERR_OK(status)) {
1317 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
1318 host, ads->config.realm));
1319 return status;
1323 status = ads_add_machine_acct(ads, host, account_type, org_unit);
1324 if (!ADS_ERR_OK(status)) {
1325 DEBUG(0, ("ads_add_machine_acct: %s\n", ads_errstr(status)));
1326 return status;
1329 status = ads_find_machine_acct(ads, (void **)&res, host);
1330 if (!ADS_ERR_OK(status)) {
1331 DEBUG(0, ("Host account test failed\n"));
1332 return status;
1335 free(host);
1337 return status;
1341 * Delete a machine from the realm
1342 * @param ads connection to ads server
1343 * @param hostname Machine to remove
1344 * @return status of delete
1346 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
1348 ADS_STATUS status;
1349 void *res, *msg;
1350 char *hostnameDN, *host;
1351 int rc;
1353 /* hostname must be lowercase */
1354 host = strdup(hostname);
1355 strlower_m(host);
1357 status = ads_find_machine_acct(ads, &res, host);
1358 if (!ADS_ERR_OK(status)) {
1359 DEBUG(0, ("Host account for %s does not exist.\n", host));
1360 return status;
1363 msg = ads_first_entry(ads, res);
1364 if (!msg) {
1365 return ADS_ERROR_SYSTEM(ENOENT);
1368 hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
1369 rc = ldap_delete_s(ads->ld, hostnameDN);
1370 ads_memfree(ads, hostnameDN);
1371 if (rc != LDAP_SUCCESS) {
1372 return ADS_ERROR(rc);
1375 status = ads_find_machine_acct(ads, &res, host);
1376 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1377 DEBUG(0, ("Failed to remove host account.\n"));
1378 return status;
1381 free(host);
1383 return status;
1387 * add machine account to existing security descriptor
1388 * @param ads connection to ads server
1389 * @param hostname machine to add
1390 * @param dn DN of security descriptor
1391 * @return status
1393 ADS_STATUS ads_set_machine_sd(ADS_STRUCT *ads, const char *hostname, char *dn)
1395 const char *attrs[] = {"nTSecurityDescriptor", "objectSid", 0};
1396 char *expr = 0;
1397 size_t sd_size = 0;
1398 struct berval bval = {0, NULL};
1399 prs_struct ps_wire;
1400 char *escaped_hostname = escape_ldap_string_alloc(hostname);
1402 LDAPMessage *res = 0;
1403 LDAPMessage *msg = 0;
1404 ADS_MODLIST mods = 0;
1406 NTSTATUS status;
1407 ADS_STATUS ret;
1408 DOM_SID sid;
1409 SEC_DESC *psd = NULL;
1410 TALLOC_CTX *ctx = NULL;
1412 /* Avoid segmentation fault in prs_mem_free if
1413 * we have to bail out before prs_init */
1414 ps_wire.is_dynamic = False;
1416 if (!ads) return ADS_ERROR(LDAP_SERVER_DOWN);
1418 ret = ADS_ERROR(LDAP_SUCCESS);
1420 if (!escaped_hostname) {
1421 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1424 if (asprintf(&expr, "(samAccountName=%s$)", escaped_hostname) == -1) {
1425 DEBUG(1, ("ads_set_machine_sd: asprintf failed!\n"));
1426 SAFE_FREE(escaped_hostname);
1427 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1430 SAFE_FREE(escaped_hostname);
1432 ret = ads_search(ads, (void *) &res, expr, attrs);
1434 if (!ADS_ERR_OK(ret)) return ret;
1436 if ( !(msg = ads_first_entry(ads, res) )) {
1437 ret = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1438 goto ads_set_sd_error;
1441 if (!ads_pull_sid(ads, msg, attrs[1], &sid)) {
1442 ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
1443 goto ads_set_sd_error;
1446 if (!(ctx = talloc_init("sec_io_desc"))) {
1447 ret = ADS_ERROR(LDAP_NO_MEMORY);
1448 goto ads_set_sd_error;
1451 if (!ads_pull_sd(ads, ctx, msg, attrs[0], &psd)) {
1452 ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
1453 goto ads_set_sd_error;
1456 status = sec_desc_add_sid(ctx, &psd, &sid, SEC_RIGHTS_FULL_CTRL, &sd_size);
1458 if (!NT_STATUS_IS_OK(status)) {
1459 ret = ADS_ERROR_NT(status);
1460 goto ads_set_sd_error;
1463 if (!prs_init(&ps_wire, sd_size, ctx, MARSHALL)) {
1464 ret = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1467 if (!sec_io_desc("sd_wire", &psd, &ps_wire, 1)) {
1468 ret = ADS_ERROR(LDAP_NO_MEMORY);
1469 goto ads_set_sd_error;
1472 #if 0
1473 file_save("/tmp/sec_desc.new", ps_wire.data_p, sd_size);
1474 #endif
1475 if (!(mods = ads_init_mods(ctx))) return ADS_ERROR(LDAP_NO_MEMORY);
1477 bval.bv_len = prs_offset(&ps_wire);
1478 bval.bv_val = talloc(ctx, bval.bv_len);
1479 if (!bval.bv_val) {
1480 ret = ADS_ERROR(LDAP_NO_MEMORY);
1481 goto ads_set_sd_error;
1484 prs_set_offset(&ps_wire, 0);
1486 if (!prs_copy_data_out(bval.bv_val, &ps_wire, bval.bv_len)) {
1487 ret = ADS_ERROR(LDAP_NO_MEMORY);
1488 goto ads_set_sd_error;
1491 ret = ads_mod_ber(ctx, &mods, attrs[0], &bval);
1492 if (ADS_ERR_OK(ret)) {
1493 ret = ads_gen_mod(ads, dn, mods);
1496 ads_set_sd_error:
1497 ads_msgfree(ads, res);
1498 prs_mem_free(&ps_wire);
1499 talloc_destroy(ctx);
1500 return ret;
1504 * pull the first entry from a ADS result
1505 * @param ads connection to ads server
1506 * @param res Results of search
1507 * @return first entry from result
1509 void *ads_first_entry(ADS_STRUCT *ads, void *res)
1511 return (void *)ldap_first_entry(ads->ld, (LDAPMessage *)res);
1515 * pull the next entry from a ADS result
1516 * @param ads connection to ads server
1517 * @param res Results of search
1518 * @return next entry from result
1520 void *ads_next_entry(ADS_STRUCT *ads, void *res)
1522 return (void *)ldap_next_entry(ads->ld, (LDAPMessage *)res);
1526 * pull a single string from a ADS result
1527 * @param ads connection to ads server
1528 * @param mem_ctx TALLOC_CTX to use for allocating result string
1529 * @param msg Results of search
1530 * @param field Attribute to retrieve
1531 * @return Result string in talloc context
1533 char *ads_pull_string(ADS_STRUCT *ads,
1534 TALLOC_CTX *mem_ctx, void *msg, const char *field)
1536 char **values;
1537 char *ret = NULL;
1538 char *ux_string;
1539 size_t rc;
1541 values = ldap_get_values(ads->ld, msg, field);
1542 if (!values)
1543 return NULL;
1545 if (values[0]) {
1546 rc = pull_utf8_talloc(mem_ctx, &ux_string,
1547 values[0]);
1548 if (rc != (size_t)-1)
1549 ret = ux_string;
1552 ldap_value_free(values);
1553 return ret;
1557 * pull an array of strings from a ADS result
1558 * @param ads connection to ads server
1559 * @param mem_ctx TALLOC_CTX to use for allocating result string
1560 * @param msg Results of search
1561 * @param field Attribute to retrieve
1562 * @return Result strings in talloc context
1564 char **ads_pull_strings(ADS_STRUCT *ads,
1565 TALLOC_CTX *mem_ctx, void *msg, const char *field)
1567 char **values;
1568 char **ret = NULL;
1569 int i, n;
1571 values = ldap_get_values(ads->ld, msg, field);
1572 if (!values)
1573 return NULL;
1575 for (i=0;values[i];i++)
1576 /* noop */ ;
1577 n = i;
1579 ret = talloc(mem_ctx, sizeof(char *) * (n+1));
1580 if (!ret) {
1581 ldap_value_free(values);
1582 return NULL;
1585 for (i=0;i<n;i++) {
1586 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
1587 ldap_value_free(values);
1588 return NULL;
1591 ret[i] = NULL;
1593 ldap_value_free(values);
1594 return ret;
1599 * pull a single uint32 from a ADS result
1600 * @param ads connection to ads server
1601 * @param msg Results of search
1602 * @param field Attribute to retrieve
1603 * @param v Pointer to int to store result
1604 * @return boolean inidicating success
1606 BOOL ads_pull_uint32(ADS_STRUCT *ads,
1607 void *msg, const char *field, uint32 *v)
1609 char **values;
1611 values = ldap_get_values(ads->ld, msg, field);
1612 if (!values)
1613 return False;
1614 if (!values[0]) {
1615 ldap_value_free(values);
1616 return False;
1619 *v = atoi(values[0]);
1620 ldap_value_free(values);
1621 return True;
1625 * pull a single objectGUID from an ADS result
1626 * @param ads connection to ADS server
1627 * @param msg results of search
1628 * @param guid 37-byte area to receive text guid
1629 * @return boolean indicating success
1631 BOOL ads_pull_guid(ADS_STRUCT *ads,
1632 void *msg, GUID *guid)
1634 char **values;
1636 values = ldap_get_values(ads->ld, msg, "objectGUID");
1637 if (!values)
1638 return False;
1640 if (values[0]) {
1641 memcpy(guid, values[0], sizeof(GUID));
1642 ldap_value_free(values);
1643 return True;
1645 ldap_value_free(values);
1646 return False;
1652 * pull a single DOM_SID from a ADS result
1653 * @param ads connection to ads server
1654 * @param msg Results of search
1655 * @param field Attribute to retrieve
1656 * @param sid Pointer to sid to store result
1657 * @return boolean inidicating success
1659 BOOL ads_pull_sid(ADS_STRUCT *ads,
1660 void *msg, const char *field, DOM_SID *sid)
1662 struct berval **values;
1663 BOOL ret = False;
1665 values = ldap_get_values_len(ads->ld, msg, field);
1667 if (!values)
1668 return False;
1670 if (values[0])
1671 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
1673 ldap_value_free_len(values);
1674 return ret;
1678 * pull an array of DOM_SIDs from a ADS result
1679 * @param ads connection to ads server
1680 * @param mem_ctx TALLOC_CTX for allocating sid array
1681 * @param msg Results of search
1682 * @param field Attribute to retrieve
1683 * @param sids pointer to sid array to allocate
1684 * @return the count of SIDs pulled
1686 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
1687 void *msg, const char *field, DOM_SID **sids)
1689 struct berval **values;
1690 BOOL ret;
1691 int count, i;
1693 values = ldap_get_values_len(ads->ld, msg, field);
1695 if (!values)
1696 return 0;
1698 for (i=0; values[i]; i++)
1699 /* nop */ ;
1701 (*sids) = talloc(mem_ctx, sizeof(DOM_SID) * i);
1702 if (!(*sids)) {
1703 ldap_value_free_len(values);
1704 return 0;
1707 count = 0;
1708 for (i=0; values[i]; i++) {
1709 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
1710 if (ret) {
1711 fstring sid;
1712 DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid, &(*sids)[count])));
1713 count++;
1717 ldap_value_free_len(values);
1718 return count;
1722 * pull a SEC_DESC from a ADS result
1723 * @param ads connection to ads server
1724 * @param mem_ctx TALLOC_CTX for allocating sid array
1725 * @param msg Results of search
1726 * @param field Attribute to retrieve
1727 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
1728 * @return boolean inidicating success
1730 BOOL ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
1731 void *msg, const char *field, SEC_DESC **sd)
1733 struct berval **values;
1734 prs_struct ps;
1735 BOOL ret = False;
1737 values = ldap_get_values_len(ads->ld, msg, field);
1739 if (!values) return False;
1741 if (values[0]) {
1742 prs_init(&ps, values[0]->bv_len, mem_ctx, UNMARSHALL);
1743 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
1744 prs_set_offset(&ps,0);
1746 ret = sec_io_desc("sd", sd, &ps, 1);
1749 ldap_value_free_len(values);
1750 return ret;
1754 * in order to support usernames longer than 21 characters we need to
1755 * use both the sAMAccountName and the userPrincipalName attributes
1756 * It seems that not all users have the userPrincipalName attribute set
1758 * @param ads connection to ads server
1759 * @param mem_ctx TALLOC_CTX for allocating sid array
1760 * @param msg Results of search
1761 * @return the username
1763 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, void *msg)
1765 char *ret, *p;
1767 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
1768 if (ret && (p = strchr(ret, '@'))) {
1769 *p = 0;
1770 return ret;
1772 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
1777 * find the update serial number - this is the core of the ldap cache
1778 * @param ads connection to ads server
1779 * @param ads connection to ADS server
1780 * @param usn Pointer to retrieved update serial number
1781 * @return status of search
1783 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
1785 const char *attrs[] = {"highestCommittedUSN", NULL};
1786 ADS_STATUS status;
1787 void *res;
1789 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
1790 if (!ADS_ERR_OK(status)) return status;
1792 if (ads_count_replies(ads, res) != 1) {
1793 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1796 ads_pull_uint32(ads, res, "highestCommittedUSN", usn);
1797 ads_msgfree(ads, res);
1798 return ADS_SUCCESS;
1801 /* parse a ADS timestring - typical string is
1802 '20020917091222.0Z0' which means 09:12.22 17th September
1803 2002, timezone 0 */
1804 static time_t ads_parse_time(const char *str)
1806 struct tm tm;
1808 ZERO_STRUCT(tm);
1810 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
1811 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
1812 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
1813 return 0;
1815 tm.tm_year -= 1900;
1816 tm.tm_mon -= 1;
1818 return timegm(&tm);
1823 * Find the servers name and realm - this can be done before authentication
1824 * The ldapServiceName field on w2k looks like this:
1825 * vnet3.home.samba.org:win2000-vnet3$@VNET3.HOME.SAMBA.ORG
1826 * @param ads connection to ads server
1827 * @return status of search
1829 ADS_STATUS ads_server_info(ADS_STRUCT *ads)
1831 const char *attrs[] = {"ldapServiceName", "currentTime", NULL};
1832 ADS_STATUS status;
1833 void *res;
1834 char *value;
1835 char *p;
1836 char *timestr;
1837 TALLOC_CTX *ctx;
1839 if (!(ctx = talloc_init("ads_server_info"))) {
1840 return ADS_ERROR(LDAP_NO_MEMORY);
1843 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
1844 if (!ADS_ERR_OK(status)) return status;
1846 value = ads_pull_string(ads, ctx, res, "ldapServiceName");
1847 if (!value) {
1848 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1851 timestr = ads_pull_string(ads, ctx, res, "currentTime");
1852 if (!timestr) {
1853 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1856 ldap_msgfree(res);
1858 p = strchr(value, ':');
1859 if (!p) {
1860 talloc_destroy(ctx);
1861 DEBUG(1, ("ads_server_info: returned ldap server name did not contain a ':' so was deemed invalid\n"));
1862 return ADS_ERROR(LDAP_DECODING_ERROR);
1865 SAFE_FREE(ads->config.ldap_server_name);
1867 ads->config.ldap_server_name = strdup(p+1);
1868 p = strchr(ads->config.ldap_server_name, '$');
1869 if (!p || p[1] != '@') {
1870 talloc_destroy(ctx);
1871 DEBUG(1, ("ads_server_info: returned ldap server name (%s) does not contain '$@' so was deemed invalid\n", ads->config.ldap_server_name));
1872 SAFE_FREE(ads->config.ldap_server_name);
1873 return ADS_ERROR(LDAP_DECODING_ERROR);
1876 *p = 0;
1878 SAFE_FREE(ads->config.realm);
1879 SAFE_FREE(ads->config.bind_path);
1881 ads->config.realm = strdup(p+2);
1882 ads->config.bind_path = ads_build_dn(ads->config.realm);
1884 DEBUG(3,("got ldap server name %s@%s, using bind path: %s\n",
1885 ads->config.ldap_server_name, ads->config.realm,
1886 ads->config.bind_path));
1888 ads->config.current_time = ads_parse_time(timestr);
1890 if (ads->config.current_time != 0) {
1891 ads->auth.time_offset = ads->config.current_time - time(NULL);
1892 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
1895 talloc_destroy(ctx);
1897 return ADS_SUCCESS;
1901 * find the domain sid for our domain
1902 * @param ads connection to ads server
1903 * @param sid Pointer to domain sid
1904 * @return status of search
1906 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
1908 const char *attrs[] = {"objectSid", NULL};
1909 void *res;
1910 ADS_STATUS rc;
1912 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
1913 attrs, &res);
1914 if (!ADS_ERR_OK(rc)) return rc;
1915 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
1916 return ADS_ERROR_SYSTEM(ENOENT);
1918 ads_msgfree(ads, res);
1920 return ADS_SUCCESS;
1923 /* this is rather complex - we need to find the allternate (netbios) name
1924 for the domain, but there isn't a simple query to do this. Instead
1925 we look for the principle names on the DCs account and find one that has
1926 the right form, then extract the netbios name of the domain from that
1928 NOTE! better method is this:
1930 bin/net -Uadministrator%XXXXX ads search '(&(objectclass=crossref)(dnsroot=VNET3.HOME.SAMBA.ORG))' nETBIOSName
1932 but you need to force the bind path to match the configurationNamingContext from the rootDSE
1935 ADS_STATUS ads_workgroup_name(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char **workgroup)
1937 char *expr;
1938 ADS_STATUS rc;
1939 char **principles;
1940 char *prefix;
1941 int prefix_length;
1942 int i;
1943 void *res;
1944 const char *attrs[] = {"servicePrincipalName", NULL};
1946 (*workgroup) = NULL;
1948 asprintf(&expr, "(&(objectclass=computer)(dnshostname=%s.%s))",
1949 ads->config.ldap_server_name, ads->config.realm);
1950 rc = ads_search(ads, &res, expr, attrs);
1951 free(expr);
1953 if (!ADS_ERR_OK(rc)) {
1954 return rc;
1957 principles = ads_pull_strings(ads, mem_ctx, res, "servicePrincipalName");
1959 ads_msgfree(ads, res);
1961 if (!principles) {
1962 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1965 asprintf(&prefix, "HOST/%s.%s/",
1966 ads->config.ldap_server_name,
1967 ads->config.realm);
1969 prefix_length = strlen(prefix);
1971 for (i=0;principles[i]; i++) {
1972 if (strncasecmp(principles[i], prefix, prefix_length) == 0 &&
1973 strcasecmp(ads->config.realm, principles[i]+prefix_length) != 0 &&
1974 !strchr(principles[i]+prefix_length, '.')) {
1975 /* found an alternate (short) name for the domain. */
1976 DEBUG(3,("Found alternate name '%s' for realm '%s'\n",
1977 principles[i]+prefix_length,
1978 ads->config.realm));
1979 (*workgroup) = talloc_strdup(mem_ctx, principles[i]+prefix_length);
1980 break;
1983 free(prefix);
1985 if (!*workgroup) {
1986 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1989 return ADS_SUCCESS;
1992 #endif