Fix a small typo in a comment and pretty it up a bit.
[Samba/gebeck_regimport.git] / source3 / libads / ldap.c
blob92f7f7645ac827a115fb8899d43b5afd46c088a2
1 /*
2 Unix SMB/CIFS implementation.
3 ads (active directory) utility library
4 Copyright (C) Andrew Tridgell 2001
5 Copyright (C) Remus Koos 2001
6 Copyright (C) Jim McDonough 2002
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 #include "includes.h"
25 #ifdef HAVE_LDAP
27 /**
28 * @file ldap.c
29 * @brief basic ldap client-side routines for ads server communications
31 * The routines contained here should do the necessary ldap calls for
32 * ads setups.
34 * Important note: attribute names passed into ads_ routines must
35 * already be in UTF-8 format. We do not convert them because in almost
36 * all cases, they are just ascii (which is represented with the same
37 * codepoints in UTF-8). This may have to change at some point
38 **/
42 try a connection to a given ldap server, returning True and setting the servers IP
43 in the ads struct if successful
45 TODO : add a negative connection cache in here leveraged off of the one
46 found in the rpc code. --jerry
48 static BOOL ads_try_connect(ADS_STRUCT *ads, const char *server, unsigned port)
50 char *srv;
52 if (!server || !*server) {
53 return False;
56 DEBUG(5,("ads_try_connect: trying ldap server '%s' port %u\n", server, port));
58 /* this copes with inet_ntoa brokenness */
59 srv = strdup(server);
61 ads->ld = ldap_open(srv, port);
62 if (!ads->ld) {
63 free(srv);
64 return False;
66 ads->ldap_port = port;
67 ads->ldap_ip = *interpret_addr2(srv);
68 free(srv);
70 return True;
74 try a connection to a given ldap server, based on URL, returning True if successful
76 static BOOL ads_try_connect_uri(ADS_STRUCT *ads)
78 #if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
79 DEBUG(5,("ads_try_connect: trying ldap server at URI '%s'\n",
80 ads->server.ldap_uri));
83 if (ldap_initialize((LDAP**)&(ads->ld), ads->server.ldap_uri) == LDAP_SUCCESS) {
84 return True;
86 DEBUG(0, ("ldap_initialize: %s\n", strerror(errno)));
88 #else
90 DEBUG(1, ("no URL support in LDAP libs!\n"));
91 #endif
93 return False;
96 /**********************************************************************
97 Try to find an AD dc using our internal name resolution routines
98 Try the realm first and then then workgroup name if netbios is not
99 disabled
100 **********************************************************************/
102 static BOOL ads_find_dc(ADS_STRUCT *ads)
104 const char *c_realm;
105 int count, i=0;
106 struct ip_service *ip_list;
107 pstring realm;
108 BOOL got_realm = False;
110 /* realm */
111 c_realm = ads->server.realm;
112 if (!c_realm || !*c_realm) {
113 c_realm = lp_realm();
115 if ( c_realm )
116 got_realm = True;
119 again:
120 /* we need to try once with the realm name and fallback to the
121 netbios domain name if we fail (if netbios has not been disabled */
123 if ( !got_realm && !lp_disable_netbios() ) {
124 c_realm = ads->server.workgroup;
125 if (!c_realm || !*c_realm)
126 c_realm = lp_workgroup();
127 if (!c_realm)
128 return False;
131 pstrcpy( realm, c_realm );
133 DEBUG(6,("ads_find_dc: looking for %s '%s'\n",
134 (got_realm ? "realm" : "domain"), realm));
136 if ( !get_sorted_dc_list(realm, &ip_list, &count, got_realm) ) {
137 /* fall back to netbios if we can */
138 if ( got_realm && !lp_disable_netbios() ) {
139 got_realm = False;
140 goto again;
143 return False;
146 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
147 for ( i=0; i<count; i++ ) {
148 /* since this is an ads conection request, default to LDAP_PORT is not set */
149 int port = (ip_list[i].port!=PORT_NONE) ? ip_list[i].port : LDAP_PORT;
150 fstring server;
152 fstrcpy( server, inet_ntoa(ip_list[i].ip) );
154 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
155 continue;
157 if ( ads_try_connect(ads, server, port) ) {
158 SAFE_FREE(ip_list);
159 return True;
162 /* keep track of failures */
163 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
166 SAFE_FREE(ip_list);
168 return False;
173 * Connect to the LDAP server
174 * @param ads Pointer to an existing ADS_STRUCT
175 * @return status of connection
177 ADS_STATUS ads_connect(ADS_STRUCT *ads)
179 int version = LDAP_VERSION3;
180 ADS_STATUS status;
182 ads->last_attempt = time(NULL);
183 ads->ld = NULL;
185 /* try with a URL based server */
187 if (ads->server.ldap_uri &&
188 ads_try_connect_uri(ads)) {
189 goto got_connection;
192 /* try with a user specified server */
193 if (ads->server.ldap_server &&
194 ads_try_connect(ads, ads->server.ldap_server, LDAP_PORT)) {
195 goto got_connection;
198 if (ads_find_dc(ads)) {
199 goto got_connection;
202 return ADS_ERROR_SYSTEM(errno?errno:ENOENT);
204 got_connection:
205 DEBUG(3,("Connected to LDAP server %s\n", inet_ntoa(ads->ldap_ip)));
207 status = ads_server_info(ads);
208 if (!ADS_ERR_OK(status)) {
209 DEBUG(1,("Failed to get ldap server info\n"));
210 return status;
213 ldap_set_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
215 if (!ads->auth.user_name) {
216 /* by default use the machine account */
217 fstring myname;
218 fstrcpy(myname, global_myname());
219 strlower_m(myname);
220 asprintf(&ads->auth.user_name, "HOST/%s", myname);
223 if (!ads->auth.realm) {
224 ads->auth.realm = strdup(ads->config.realm);
227 if (!ads->auth.kdc_server) {
228 ads->auth.kdc_server = strdup(inet_ntoa(ads->ldap_ip));
231 #if KRB5_DNS_HACK
232 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
233 to MIT kerberos to work (tridge) */
235 char *env;
236 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
237 setenv(env, ads->auth.kdc_server, 1);
238 free(env);
240 #endif
242 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
243 return ADS_SUCCESS;
246 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
247 return ADS_ERROR(ldap_simple_bind_s( ads->ld, NULL, NULL));
250 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
251 return ADS_ERROR(ldap_simple_bind_s( ads->ld, ads->auth.user_name, ads->auth.password));
254 return ads_sasl_bind(ads);
258 Duplicate a struct berval into talloc'ed memory
260 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
262 struct berval *value;
264 if (!in_val) return NULL;
266 value = talloc_zero(ctx, sizeof(struct berval));
267 if (value == NULL)
268 return NULL;
269 if (in_val->bv_len == 0) return value;
271 value->bv_len = in_val->bv_len;
272 value->bv_val = talloc_memdup(ctx, in_val->bv_val, in_val->bv_len);
273 return value;
277 Make a values list out of an array of (struct berval *)
279 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
280 const struct berval **in_vals)
282 struct berval **values;
283 int i;
285 if (!in_vals) return NULL;
286 for (i=0; in_vals[i]; i++); /* count values */
287 values = (struct berval **) talloc_zero(ctx,
288 (i+1)*sizeof(struct berval *));
289 if (!values) return NULL;
291 for (i=0; in_vals[i]; i++) {
292 values[i] = dup_berval(ctx, in_vals[i]);
294 return values;
298 UTF8-encode a values list out of an array of (char *)
300 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
302 char **values;
303 int i;
305 if (!in_vals) return NULL;
306 for (i=0; in_vals[i]; i++); /* count values */
307 values = (char ** ) talloc_zero(ctx, (i+1)*sizeof(char *));
308 if (!values) return NULL;
310 for (i=0; in_vals[i]; i++) {
311 push_utf8_talloc(ctx, &values[i], in_vals[i]);
313 return values;
317 Pull a (char *) array out of a UTF8-encoded values list
319 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
321 char **values;
322 int i;
324 if (!in_vals) return NULL;
325 for (i=0; in_vals[i]; i++); /* count values */
326 values = (char **) talloc_zero(ctx, (i+1)*sizeof(char *));
327 if (!values) return NULL;
329 for (i=0; in_vals[i]; i++) {
330 pull_utf8_talloc(ctx, &values[i], in_vals[i]);
332 return values;
336 * Do a search with paged results. cookie must be null on the first
337 * call, and then returned on each subsequent call. It will be null
338 * again when the entire search is complete
339 * @param ads connection to ads server
340 * @param bind_path Base dn for the search
341 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
342 * @param expr Search expression - specified in local charset
343 * @param attrs Attributes to retrieve - specified in utf8 or ascii
344 * @param res ** which will contain results - free res* with ads_msgfree()
345 * @param count Number of entries retrieved on this page
346 * @param cookie The paged results cookie to be returned on subsequent calls
347 * @return status of search
349 ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
350 int scope, const char *expr,
351 const char **attrs, void **res,
352 int *count, void **cookie)
354 int rc, i, version;
355 char *utf8_expr, *utf8_path, **search_attrs;
356 LDAPControl PagedResults, NoReferrals, *controls[3], **rcontrols;
357 BerElement *cookie_be = NULL;
358 struct berval *cookie_bv= NULL;
359 TALLOC_CTX *ctx;
361 *res = NULL;
363 if (!(ctx = talloc_init("ads_do_paged_search")))
364 return ADS_ERROR(LDAP_NO_MEMORY);
366 /* 0 means the conversion worked but the result was empty
367 so we only fail if it's -1. In any case, it always
368 at least nulls out the dest */
369 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
370 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
371 rc = LDAP_NO_MEMORY;
372 goto done;
375 if (!attrs || !(*attrs))
376 search_attrs = NULL;
377 else {
378 /* This would be the utf8-encoded version...*/
379 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
380 if (!(str_list_copy(&search_attrs, attrs))) {
381 rc = LDAP_NO_MEMORY;
382 goto done;
387 /* Paged results only available on ldap v3 or later */
388 ldap_get_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
389 if (version < LDAP_VERSION3) {
390 rc = LDAP_NOT_SUPPORTED;
391 goto done;
394 cookie_be = ber_alloc_t(LBER_USE_DER);
395 if (cookie && *cookie) {
396 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
397 ber_bvfree(*cookie); /* don't need it from last time */
398 *cookie = NULL;
399 } else {
400 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
402 ber_flatten(cookie_be, &cookie_bv);
403 PagedResults.ldctl_oid = ADS_PAGE_CTL_OID;
404 PagedResults.ldctl_iscritical = (char) 1;
405 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
406 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
408 NoReferrals.ldctl_oid = ADS_NO_REFERRALS_OID;
409 NoReferrals.ldctl_iscritical = (char) 0;
410 NoReferrals.ldctl_value.bv_len = 0;
411 NoReferrals.ldctl_value.bv_val = "";
414 controls[0] = &NoReferrals;
415 controls[1] = &PagedResults;
416 controls[2] = NULL;
418 *res = NULL;
420 /* we need to disable referrals as the openldap libs don't
421 handle them and paged results at the same time. Using them
422 together results in the result record containing the server
423 page control being removed from the result list (tridge/jmcd)
425 leaving this in despite the control that says don't generate
426 referrals, in case the server doesn't support it (jmcd)
428 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
430 rc = ldap_search_ext_s(ads->ld, utf8_path, scope, utf8_expr,
431 search_attrs, 0, controls,
432 NULL, NULL, LDAP_NO_LIMIT, (LDAPMessage **)res);
434 ber_free(cookie_be, 1);
435 ber_bvfree(cookie_bv);
437 if (rc) {
438 DEBUG(3,("ldap_search_ext_s(%s) -> %s\n", expr, ldap_err2string(rc)));
439 goto done;
442 rc = ldap_parse_result(ads->ld, *res, NULL, NULL, NULL,
443 NULL, &rcontrols, 0);
445 if (!rcontrols) {
446 goto done;
449 for (i=0; rcontrols[i]; i++) {
450 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
451 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
452 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
453 &cookie_bv);
454 /* the berval is the cookie, but must be freed when
455 it is all done */
456 if (cookie_bv->bv_len) /* still more to do */
457 *cookie=ber_bvdup(cookie_bv);
458 else
459 *cookie=NULL;
460 ber_bvfree(cookie_bv);
461 ber_free(cookie_be, 1);
462 break;
465 ldap_controls_free(rcontrols);
467 done:
468 talloc_destroy(ctx);
469 /* if/when we decide to utf8-encode attrs, take out this next line */
470 str_list_free(&search_attrs);
472 return ADS_ERROR(rc);
477 * Get all results for a search. This uses ads_do_paged_search() to return
478 * all entries in a large search.
479 * @param ads connection to ads server
480 * @param bind_path Base dn for the search
481 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
482 * @param expr Search expression
483 * @param attrs Attributes to retrieve
484 * @param res ** which will contain results - free res* with ads_msgfree()
485 * @return status of search
487 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
488 int scope, const char *expr,
489 const char **attrs, void **res)
491 void *cookie = NULL;
492 int count = 0;
493 ADS_STATUS status;
495 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, res,
496 &count, &cookie);
498 if (!ADS_ERR_OK(status)) return status;
500 while (cookie) {
501 void *res2 = NULL;
502 ADS_STATUS status2;
503 LDAPMessage *msg, *next;
505 status2 = ads_do_paged_search(ads, bind_path, scope, expr,
506 attrs, &res2, &count, &cookie);
508 if (!ADS_ERR_OK(status2)) break;
510 /* this relies on the way that ldap_add_result_entry() works internally. I hope
511 that this works on all ldap libs, but I have only tested with openldap */
512 for (msg = ads_first_entry(ads, res2); msg; msg = next) {
513 next = ads_next_entry(ads, msg);
514 ldap_add_result_entry((LDAPMessage **)res, msg);
516 /* note that we do not free res2, as the memory is now
517 part of the main returned list */
520 return status;
524 * Run a function on all results for a search. Uses ads_do_paged_search() and
525 * runs the function as each page is returned, using ads_process_results()
526 * @param ads connection to ads server
527 * @param bind_path Base dn for the search
528 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
529 * @param expr Search expression - specified in local charset
530 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
531 * @param fn Function which takes attr name, values list, and data_area
532 * @param data_area Pointer which is passed to function on each call
533 * @return status of search
535 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
536 int scope, const char *expr, const char **attrs,
537 BOOL(*fn)(char *, void **, void *),
538 void *data_area)
540 void *cookie = NULL;
541 int count = 0;
542 ADS_STATUS status;
543 void *res;
545 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
546 &count, &cookie);
548 if (!ADS_ERR_OK(status)) return status;
550 ads_process_results(ads, res, fn, data_area);
551 ads_msgfree(ads, res);
553 while (cookie) {
554 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
555 &res, &count, &cookie);
557 if (!ADS_ERR_OK(status)) break;
559 ads_process_results(ads, res, fn, data_area);
560 ads_msgfree(ads, res);
563 return status;
567 * Do a search with a timeout.
568 * @param ads connection to ads server
569 * @param bind_path Base dn for the search
570 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
571 * @param expr Search expression
572 * @param attrs Attributes to retrieve
573 * @param res ** which will contain results - free res* with ads_msgfree()
574 * @return status of search
576 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
577 const char *expr,
578 const char **attrs, void **res)
580 struct timeval timeout;
581 int rc;
582 char *utf8_expr, *utf8_path, **search_attrs = NULL;
583 TALLOC_CTX *ctx;
585 if (!(ctx = talloc_init("ads_do_search"))) {
586 DEBUG(1,("ads_do_search: talloc_init() failed!"));
587 return ADS_ERROR(LDAP_NO_MEMORY);
590 /* 0 means the conversion worked but the result was empty
591 so we only fail if it's negative. In any case, it always
592 at least nulls out the dest */
593 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
594 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
595 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
596 rc = LDAP_NO_MEMORY;
597 goto done;
600 if (!attrs || !(*attrs))
601 search_attrs = NULL;
602 else {
603 /* This would be the utf8-encoded version...*/
604 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
605 if (!(str_list_copy(&search_attrs, attrs)))
607 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
608 rc = LDAP_NO_MEMORY;
609 goto done;
613 timeout.tv_sec = ADS_SEARCH_TIMEOUT;
614 timeout.tv_usec = 0;
615 *res = NULL;
617 /* see the note in ads_do_paged_search - we *must* disable referrals */
618 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
620 rc = ldap_search_ext_s(ads->ld, utf8_path, scope, utf8_expr,
621 search_attrs, 0, NULL, NULL,
622 &timeout, LDAP_NO_LIMIT, (LDAPMessage **)res);
624 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
625 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
626 rc = 0;
629 done:
630 talloc_destroy(ctx);
631 /* if/when we decide to utf8-encode attrs, take out this next line */
632 str_list_free(&search_attrs);
633 return ADS_ERROR(rc);
636 * Do a general ADS search
637 * @param ads connection to ads server
638 * @param res ** which will contain results - free res* with ads_msgfree()
639 * @param expr Search expression
640 * @param attrs Attributes to retrieve
641 * @return status of search
643 ADS_STATUS ads_search(ADS_STRUCT *ads, void **res,
644 const char *expr,
645 const char **attrs)
647 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
648 expr, attrs, res);
652 * Do a search on a specific DistinguishedName
653 * @param ads connection to ads server
654 * @param res ** which will contain results - free res* with ads_msgfree()
655 * @param dn DistinguishName to search
656 * @param attrs Attributes to retrieve
657 * @return status of search
659 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, void **res,
660 const char *dn,
661 const char **attrs)
663 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)", attrs, res);
667 * Free up memory from a ads_search
668 * @param ads connection to ads server
669 * @param msg Search results to free
671 void ads_msgfree(ADS_STRUCT *ads, void *msg)
673 if (!msg) return;
674 ldap_msgfree(msg);
678 * Free up memory from various ads requests
679 * @param ads connection to ads server
680 * @param mem Area to free
682 void ads_memfree(ADS_STRUCT *ads, void *mem)
684 SAFE_FREE(mem);
688 * Get a dn from search results
689 * @param ads connection to ads server
690 * @param msg Search result
691 * @return dn string
693 char *ads_get_dn(ADS_STRUCT *ads, void *msg)
695 char *utf8_dn, *unix_dn;
697 utf8_dn = ldap_get_dn(ads->ld, msg);
699 pull_utf8_allocate((void **) &unix_dn, utf8_dn);
700 ldap_memfree(utf8_dn);
701 return unix_dn;
705 * Find a machine account given a hostname
706 * @param ads connection to ads server
707 * @param res ** which will contain results - free res* with ads_msgfree()
708 * @param host Hostname to search for
709 * @return status of search
711 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, void **res, const char *host)
713 ADS_STATUS status;
714 char *expr;
715 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
717 /* the easiest way to find a machine account anywhere in the tree
718 is to look for hostname$ */
719 if (asprintf(&expr, "(samAccountName=%s$)", host) == -1) {
720 DEBUG(1, ("asprintf failed!\n"));
721 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
724 status = ads_search(ads, res, expr, attrs);
725 free(expr);
726 return status;
730 * Initialize a list of mods to be used in a modify request
731 * @param ctx An initialized TALLOC_CTX
732 * @return allocated ADS_MODLIST
734 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
736 #define ADS_MODLIST_ALLOC_SIZE 10
737 LDAPMod **mods;
739 if ((mods = (LDAPMod **) talloc_zero(ctx, sizeof(LDAPMod *) *
740 (ADS_MODLIST_ALLOC_SIZE + 1))))
741 /* -1 is safety to make sure we don't go over the end.
742 need to reset it to NULL before doing ldap modify */
743 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
745 return mods;
750 add an attribute to the list, with values list already constructed
752 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
753 int mod_op, const char *name,
754 const void **invals)
756 int curmod;
757 LDAPMod **modlist = (LDAPMod **) *mods;
758 struct berval **ber_values = NULL;
759 char **char_values = NULL;
761 if (!invals) {
762 mod_op = LDAP_MOD_DELETE;
763 } else {
764 if (mod_op & LDAP_MOD_BVALUES)
765 ber_values = ads_dup_values(ctx,
766 (const struct berval **)invals);
767 else
768 char_values = ads_push_strvals(ctx,
769 (const char **) invals);
772 /* find the first empty slot */
773 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
774 curmod++);
775 if (modlist[curmod] == (LDAPMod *) -1) {
776 if (!(modlist = talloc_realloc(ctx, modlist,
777 (curmod+ADS_MODLIST_ALLOC_SIZE+1)*sizeof(LDAPMod *))))
778 return ADS_ERROR(LDAP_NO_MEMORY);
779 memset(&modlist[curmod], 0,
780 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
781 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
782 *mods = modlist;
785 if (!(modlist[curmod] = talloc_zero(ctx, sizeof(LDAPMod))))
786 return ADS_ERROR(LDAP_NO_MEMORY);
787 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
788 if (mod_op & LDAP_MOD_BVALUES) {
789 modlist[curmod]->mod_bvalues = ber_values;
790 } else if (mod_op & LDAP_MOD_DELETE) {
791 modlist[curmod]->mod_values = NULL;
792 } else {
793 modlist[curmod]->mod_values = char_values;
796 modlist[curmod]->mod_op = mod_op;
797 return ADS_ERROR(LDAP_SUCCESS);
801 * Add a single string value to a mod list
802 * @param ctx An initialized TALLOC_CTX
803 * @param mods An initialized ADS_MODLIST
804 * @param name The attribute name to add
805 * @param val The value to add - NULL means DELETE
806 * @return ADS STATUS indicating success of add
808 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
809 const char *name, const char *val)
811 const char *values[2];
813 values[0] = val;
814 values[1] = NULL;
816 if (!val)
817 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
818 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name,
819 (const void **) values);
823 * Add an array of string values 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 vals The array of string values to add - NULL means DELETE
828 * @return ADS STATUS indicating success of add
830 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
831 const char *name, const char **vals)
833 if (!vals)
834 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
835 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
836 name, (const void **) vals);
840 * Add a single ber-encoded value to a mod list
841 * @param ctx An initialized TALLOC_CTX
842 * @param mods An initialized ADS_MODLIST
843 * @param name The attribute name to add
844 * @param val The value to add - NULL means DELETE
845 * @return ADS STATUS indicating success of add
847 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
848 const char *name, const struct berval *val)
850 const struct berval *values[2];
852 values[0] = val;
853 values[1] = NULL;
854 if (!val)
855 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
856 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
857 name, (const void **) values);
861 * Perform an ldap modify
862 * @param ads connection to ads server
863 * @param mod_dn DistinguishedName to modify
864 * @param mods list of modifications to perform
865 * @return status of modify
867 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
869 int ret,i;
870 char *utf8_dn = NULL;
872 this control is needed to modify that contains a currently
873 non-existent attribute (but allowable for the object) to run
875 LDAPControl PermitModify = {
876 ADS_PERMIT_MODIFY_OID,
877 {0, NULL},
878 (char) 1};
879 LDAPControl *controls[2];
881 controls[0] = &PermitModify;
882 controls[1] = NULL;
884 if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) {
885 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
888 /* find the end of the list, marked by NULL or -1 */
889 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
890 /* make sure the end of the list is NULL */
891 mods[i] = NULL;
892 ret = ldap_modify_ext_s(ads->ld, utf8_dn,
893 (LDAPMod **) mods, controls, NULL);
894 SAFE_FREE(utf8_dn);
895 return ADS_ERROR(ret);
899 * Perform an ldap add
900 * @param ads connection to ads server
901 * @param new_dn DistinguishedName to add
902 * @param mods list of attributes and values for DN
903 * @return status of add
905 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
907 int ret, i;
908 char *utf8_dn = NULL;
910 if (push_utf8_allocate(&utf8_dn, new_dn) == -1) {
911 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
912 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
915 /* find the end of the list, marked by NULL or -1 */
916 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
917 /* make sure the end of the list is NULL */
918 mods[i] = NULL;
920 ret = ldap_add_s(ads->ld, utf8_dn, mods);
921 SAFE_FREE(utf8_dn);
922 return ADS_ERROR(ret);
926 * Delete a DistinguishedName
927 * @param ads connection to ads server
928 * @param new_dn DistinguishedName to delete
929 * @return status of delete
931 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
933 int ret;
934 char *utf8_dn = NULL;
935 if (push_utf8_allocate(&utf8_dn, del_dn) == -1) {
936 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
937 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
940 ret = ldap_delete_s(ads->ld, utf8_dn);
941 return ADS_ERROR(ret);
945 * Build an org unit string
946 * if org unit is Computers or blank then assume a container, otherwise
947 * assume a \ separated list of organisational units
948 * @param org_unit Organizational unit
949 * @return org unit string - caller must free
951 char *ads_ou_string(const char *org_unit)
953 if (!org_unit || !*org_unit || strcasecmp(org_unit, "Computers") == 0) {
954 return strdup("cn=Computers");
957 return ads_build_path(org_unit, "\\/", "ou=", 1);
963 add a machine account to the ADS server
965 static ADS_STATUS ads_add_machine_acct(ADS_STRUCT *ads, const char *hostname,
966 uint32 account_type,
967 const char *org_unit)
969 ADS_STATUS ret, status;
970 char *host_spn, *host_upn, *new_dn, *samAccountName, *controlstr;
971 char *ou_str;
972 TALLOC_CTX *ctx;
973 ADS_MODLIST mods;
974 const char *objectClass[] = {"top", "person", "organizationalPerson",
975 "user", "computer", NULL};
976 const char *servicePrincipalName[5] = {NULL, NULL, NULL, NULL, NULL};
977 char *psp, *psp2;
978 unsigned acct_control;
980 if (!(ctx = talloc_init("machine_account")))
981 return ADS_ERROR(LDAP_NO_MEMORY);
983 ret = ADS_ERROR(LDAP_NO_MEMORY);
985 if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", hostname)))
986 goto done;
987 if (!(host_upn = talloc_asprintf(ctx, "%s@%s", host_spn, ads->config.realm)))
988 goto done;
989 ou_str = ads_ou_string(org_unit);
990 if (!ou_str) {
991 DEBUG(1, ("ads_ou_string returned NULL (malloc failure?)\n"));
992 goto done;
994 new_dn = talloc_asprintf(ctx, "cn=%s,%s,%s", hostname, ou_str,
995 ads->config.bind_path);
996 servicePrincipalName[0] = talloc_asprintf(ctx, "HOST/%s", hostname);
997 psp = talloc_asprintf(ctx, "HOST/%s.%s",
998 hostname,
999 ads->config.realm);
1000 strlower_m(&psp[5]);
1001 servicePrincipalName[1] = psp;
1002 servicePrincipalName[2] = talloc_asprintf(ctx, "CIFS/%s", hostname);
1003 psp2 = talloc_asprintf(ctx, "CIFS/%s.%s",
1004 hostname,
1005 ads->config.realm);
1006 strlower_m(&psp2[5]);
1007 servicePrincipalName[3] = psp2;
1009 free(ou_str);
1010 if (!new_dn)
1011 goto done;
1013 if (!(samAccountName = talloc_asprintf(ctx, "%s$", hostname)))
1014 goto done;
1016 acct_control = account_type | UF_DONT_EXPIRE_PASSWD;
1017 #ifndef ENCTYPE_ARCFOUR_HMAC
1018 acct_control |= UF_USE_DES_KEY_ONLY;
1019 #endif
1021 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control)))
1022 goto done;
1024 if (!(mods = ads_init_mods(ctx)))
1025 goto done;
1027 ads_mod_str(ctx, &mods, "cn", hostname);
1028 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1029 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1030 ads_mod_str(ctx, &mods, "userPrincipalName", host_upn);
1031 ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1032 ads_mod_str(ctx, &mods, "dNSHostName", hostname);
1033 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1034 ads_mod_str(ctx, &mods, "operatingSystem", "Samba");
1035 ads_mod_str(ctx, &mods, "operatingSystemVersion", VERSION);
1037 ret = ads_gen_add(ads, new_dn, mods);
1039 if (!ADS_ERR_OK(ret))
1040 goto done;
1042 /* Do not fail if we can't set security descriptor
1043 * it shouldn't be mandatory and probably we just
1044 * don't have enough rights to do it.
1046 status = ads_set_machine_sd(ads, hostname, new_dn);
1048 if (!ADS_ERR_OK(status)) {
1049 DEBUG(0, ("Warning: ads_set_machine_sd: %s\n",
1050 ads_errstr(status)));
1052 done:
1053 talloc_destroy(ctx);
1054 return ret;
1058 dump a binary result from ldap
1060 static void dump_binary(const char *field, struct berval **values)
1062 int i, j;
1063 for (i=0; values[i]; i++) {
1064 printf("%s: ", field);
1065 for (j=0; j<values[i]->bv_len; j++) {
1066 printf("%02X", (unsigned char)values[i]->bv_val[j]);
1068 printf("\n");
1072 struct uuid {
1073 uint32 i1;
1074 uint16 i2;
1075 uint16 i3;
1076 uint8 s[8];
1079 static void dump_guid(const char *field, struct berval **values)
1081 int i;
1082 GUID guid;
1083 for (i=0; values[i]; i++) {
1084 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1085 printf("%s: %s\n", field, smb_uuid_string_static(guid));
1090 dump a sid result from ldap
1092 static void dump_sid(const char *field, struct berval **values)
1094 int i;
1095 for (i=0; values[i]; i++) {
1096 DOM_SID sid;
1097 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1098 printf("%s: %s\n", field, sid_string_static(&sid));
1103 dump ntSecurityDescriptor
1105 static void dump_sd(const char *filed, struct berval **values)
1107 prs_struct ps;
1109 SEC_DESC *psd = 0;
1110 TALLOC_CTX *ctx = 0;
1112 if (!(ctx = talloc_init("sec_io_desc")))
1113 return;
1115 /* prepare data */
1116 prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
1117 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
1118 prs_set_offset(&ps,0);
1120 /* parse secdesc */
1121 if (!sec_io_desc("sd", &psd, &ps, 1)) {
1122 prs_mem_free(&ps);
1123 talloc_destroy(ctx);
1124 return;
1126 if (psd) ads_disp_sd(psd);
1128 prs_mem_free(&ps);
1129 talloc_destroy(ctx);
1133 dump a string result from ldap
1135 static void dump_string(const char *field, char **values)
1137 int i;
1138 for (i=0; values[i]; i++) {
1139 printf("%s: %s\n", field, values[i]);
1144 dump a field from LDAP on stdout
1145 used for debugging
1148 static BOOL ads_dump_field(char *field, void **values, void *data_area)
1150 const struct {
1151 const char *name;
1152 BOOL string;
1153 void (*handler)(const char *, struct berval **);
1154 } handlers[] = {
1155 {"objectGUID", False, dump_guid},
1156 {"nTSecurityDescriptor", False, dump_sd},
1157 {"dnsRecord", False, dump_binary},
1158 {"objectSid", False, dump_sid},
1159 {"tokenGroups", False, dump_sid},
1160 {NULL, True, NULL}
1162 int i;
1164 if (!field) { /* must be end of an entry */
1165 printf("\n");
1166 return False;
1169 for (i=0; handlers[i].name; i++) {
1170 if (StrCaseCmp(handlers[i].name, field) == 0) {
1171 if (!values) /* first time, indicate string or not */
1172 return handlers[i].string;
1173 handlers[i].handler(field, (struct berval **) values);
1174 break;
1177 if (!handlers[i].name) {
1178 if (!values) /* first time, indicate string conversion */
1179 return True;
1180 dump_string(field, (char **)values);
1182 return False;
1186 * Dump a result from LDAP on stdout
1187 * used for debugging
1188 * @param ads connection to ads server
1189 * @param res Results to dump
1192 void ads_dump(ADS_STRUCT *ads, void *res)
1194 ads_process_results(ads, res, ads_dump_field, NULL);
1198 * Walk through results, calling a function for each entry found.
1199 * The function receives a field name, a berval * array of values,
1200 * and a data area passed through from the start. The function is
1201 * called once with null for field and values at the end of each
1202 * entry.
1203 * @param ads connection to ads server
1204 * @param res Results to process
1205 * @param fn Function for processing each result
1206 * @param data_area user-defined area to pass to function
1208 void ads_process_results(ADS_STRUCT *ads, void *res,
1209 BOOL(*fn)(char *, void **, void *),
1210 void *data_area)
1212 void *msg;
1213 TALLOC_CTX *ctx;
1215 if (!(ctx = talloc_init("ads_process_results")))
1216 return;
1218 for (msg = ads_first_entry(ads, res); msg;
1219 msg = ads_next_entry(ads, msg)) {
1220 char *utf8_field;
1221 BerElement *b;
1223 for (utf8_field=ldap_first_attribute(ads->ld,
1224 (LDAPMessage *)msg,&b);
1225 utf8_field;
1226 utf8_field=ldap_next_attribute(ads->ld,
1227 (LDAPMessage *)msg,b)) {
1228 struct berval **ber_vals;
1229 char **str_vals, **utf8_vals;
1230 char *field;
1231 BOOL string;
1233 pull_utf8_talloc(ctx, &field, utf8_field);
1234 string = fn(field, NULL, data_area);
1236 if (string) {
1237 utf8_vals = ldap_get_values(ads->ld,
1238 (LDAPMessage *)msg, field);
1239 str_vals = ads_pull_strvals(ctx,
1240 (const char **) utf8_vals);
1241 fn(field, (void **) str_vals, data_area);
1242 ldap_value_free(utf8_vals);
1243 } else {
1244 ber_vals = ldap_get_values_len(ads->ld,
1245 (LDAPMessage *)msg, field);
1246 fn(field, (void **) ber_vals, data_area);
1248 ldap_value_free_len(ber_vals);
1250 ldap_memfree(utf8_field);
1252 ber_free(b, 0);
1253 talloc_destroy_pool(ctx);
1254 fn(NULL, NULL, data_area); /* completed an entry */
1257 talloc_destroy(ctx);
1261 * count how many replies are in a LDAPMessage
1262 * @param ads connection to ads server
1263 * @param res Results to count
1264 * @return number of replies
1266 int ads_count_replies(ADS_STRUCT *ads, void *res)
1268 return ldap_count_entries(ads->ld, (LDAPMessage *)res);
1272 * Join a machine to a realm
1273 * Creates the machine account and sets the machine password
1274 * @param ads connection to ads server
1275 * @param hostname name of host to add
1276 * @param org_unit Organizational unit to place machine in
1277 * @return status of join
1279 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *hostname,
1280 uint32 account_type, const char *org_unit)
1282 ADS_STATUS status;
1283 LDAPMessage *res;
1284 char *host;
1286 /* hostname must be lowercase */
1287 host = strdup(hostname);
1288 strlower_m(host);
1290 status = ads_find_machine_acct(ads, (void **)&res, host);
1291 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1292 DEBUG(0, ("Host account for %s already exists - deleting old account\n", host));
1293 status = ads_leave_realm(ads, host);
1294 if (!ADS_ERR_OK(status)) {
1295 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
1296 host, ads->config.realm));
1297 return status;
1301 status = ads_add_machine_acct(ads, host, account_type, org_unit);
1302 if (!ADS_ERR_OK(status)) {
1303 DEBUG(0, ("ads_add_machine_acct: %s\n", ads_errstr(status)));
1304 return status;
1307 status = ads_find_machine_acct(ads, (void **)&res, host);
1308 if (!ADS_ERR_OK(status)) {
1309 DEBUG(0, ("Host account test failed\n"));
1310 return status;
1313 free(host);
1315 return status;
1319 * Delete a machine from the realm
1320 * @param ads connection to ads server
1321 * @param hostname Machine to remove
1322 * @return status of delete
1324 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
1326 ADS_STATUS status;
1327 void *res, *msg;
1328 char *hostnameDN, *host;
1329 int rc;
1331 /* hostname must be lowercase */
1332 host = strdup(hostname);
1333 strlower_m(host);
1335 status = ads_find_machine_acct(ads, &res, host);
1336 if (!ADS_ERR_OK(status)) {
1337 DEBUG(0, ("Host account for %s does not exist.\n", host));
1338 return status;
1341 msg = ads_first_entry(ads, res);
1342 if (!msg) {
1343 return ADS_ERROR_SYSTEM(ENOENT);
1346 hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
1347 rc = ldap_delete_s(ads->ld, hostnameDN);
1348 ads_memfree(ads, hostnameDN);
1349 if (rc != LDAP_SUCCESS) {
1350 return ADS_ERROR(rc);
1353 status = ads_find_machine_acct(ads, &res, host);
1354 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1355 DEBUG(0, ("Failed to remove host account.\n"));
1356 return status;
1359 free(host);
1361 return status;
1365 * add machine account to existing security descriptor
1366 * @param ads connection to ads server
1367 * @param hostname machine to add
1368 * @param dn DN of security descriptor
1369 * @return status
1371 ADS_STATUS ads_set_machine_sd(ADS_STRUCT *ads, const char *hostname, char *dn)
1373 const char *attrs[] = {"nTSecurityDescriptor", "objectSid", 0};
1374 char *expr = 0;
1375 size_t sd_size = 0;
1376 struct berval bval = {0, NULL};
1377 prs_struct ps_wire;
1378 char *escaped_hostname = escape_ldap_string_alloc(hostname);
1380 LDAPMessage *res = 0;
1381 LDAPMessage *msg = 0;
1382 ADS_MODLIST mods = 0;
1384 NTSTATUS status;
1385 ADS_STATUS ret;
1386 DOM_SID sid;
1387 SEC_DESC *psd = NULL;
1388 TALLOC_CTX *ctx = NULL;
1390 /* Avoid segmentation fault in prs_mem_free if
1391 * we have to bail out before prs_init */
1392 ps_wire.is_dynamic = False;
1394 if (!ads) return ADS_ERROR(LDAP_SERVER_DOWN);
1396 ret = ADS_ERROR(LDAP_SUCCESS);
1398 if (!escaped_hostname) {
1399 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1402 if (asprintf(&expr, "(samAccountName=%s$)", escaped_hostname) == -1) {
1403 DEBUG(1, ("ads_set_machine_sd: asprintf failed!\n"));
1404 SAFE_FREE(escaped_hostname);
1405 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1408 SAFE_FREE(escaped_hostname);
1410 ret = ads_search(ads, (void *) &res, expr, attrs);
1412 if (!ADS_ERR_OK(ret)) return ret;
1414 if ( !(msg = ads_first_entry(ads, res) )) {
1415 ret = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1416 goto ads_set_sd_error;
1419 if (!ads_pull_sid(ads, msg, attrs[1], &sid)) {
1420 ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
1421 goto ads_set_sd_error;
1424 if (!(ctx = talloc_init("sec_io_desc"))) {
1425 ret = ADS_ERROR(LDAP_NO_MEMORY);
1426 goto ads_set_sd_error;
1429 if (!ads_pull_sd(ads, ctx, msg, attrs[0], &psd)) {
1430 ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
1431 goto ads_set_sd_error;
1434 status = sec_desc_add_sid(ctx, &psd, &sid, SEC_RIGHTS_FULL_CTRL, &sd_size);
1436 if (!NT_STATUS_IS_OK(status)) {
1437 ret = ADS_ERROR_NT(status);
1438 goto ads_set_sd_error;
1441 if (!prs_init(&ps_wire, sd_size, ctx, MARSHALL)) {
1442 ret = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1445 if (!sec_io_desc("sd_wire", &psd, &ps_wire, 1)) {
1446 ret = ADS_ERROR(LDAP_NO_MEMORY);
1447 goto ads_set_sd_error;
1450 #if 0
1451 file_save("/tmp/sec_desc.new", ps_wire.data_p, sd_size);
1452 #endif
1453 if (!(mods = ads_init_mods(ctx))) return ADS_ERROR(LDAP_NO_MEMORY);
1455 bval.bv_len = prs_offset(&ps_wire);
1456 bval.bv_val = talloc(ctx, bval.bv_len);
1457 if (!bval.bv_val) {
1458 ret = ADS_ERROR(LDAP_NO_MEMORY);
1459 goto ads_set_sd_error;
1462 prs_set_offset(&ps_wire, 0);
1464 if (!prs_copy_data_out(bval.bv_val, &ps_wire, bval.bv_len)) {
1465 ret = ADS_ERROR(LDAP_NO_MEMORY);
1466 goto ads_set_sd_error;
1469 ret = ads_mod_ber(ctx, &mods, attrs[0], &bval);
1470 if (ADS_ERR_OK(ret)) {
1471 ret = ads_gen_mod(ads, dn, mods);
1474 ads_set_sd_error:
1475 ads_msgfree(ads, res);
1476 prs_mem_free(&ps_wire);
1477 talloc_destroy(ctx);
1478 return ret;
1482 * pull the first entry from a ADS result
1483 * @param ads connection to ads server
1484 * @param res Results of search
1485 * @return first entry from result
1487 void *ads_first_entry(ADS_STRUCT *ads, void *res)
1489 return (void *)ldap_first_entry(ads->ld, (LDAPMessage *)res);
1493 * pull the next entry from a ADS result
1494 * @param ads connection to ads server
1495 * @param res Results of search
1496 * @return next entry from result
1498 void *ads_next_entry(ADS_STRUCT *ads, void *res)
1500 return (void *)ldap_next_entry(ads->ld, (LDAPMessage *)res);
1504 * pull a single string from a ADS result
1505 * @param ads connection to ads server
1506 * @param mem_ctx TALLOC_CTX to use for allocating result string
1507 * @param msg Results of search
1508 * @param field Attribute to retrieve
1509 * @return Result string in talloc context
1511 char *ads_pull_string(ADS_STRUCT *ads,
1512 TALLOC_CTX *mem_ctx, void *msg, const char *field)
1514 char **values;
1515 char *ret = NULL;
1516 char *ux_string;
1517 size_t rc;
1519 values = ldap_get_values(ads->ld, msg, field);
1520 if (!values)
1521 return NULL;
1523 if (values[0]) {
1524 rc = pull_utf8_talloc(mem_ctx, &ux_string,
1525 values[0]);
1526 if (rc != (size_t)-1)
1527 ret = ux_string;
1530 ldap_value_free(values);
1531 return ret;
1535 * pull an array of strings from a ADS result
1536 * @param ads connection to ads server
1537 * @param mem_ctx TALLOC_CTX to use for allocating result string
1538 * @param msg Results of search
1539 * @param field Attribute to retrieve
1540 * @return Result strings in talloc context
1542 char **ads_pull_strings(ADS_STRUCT *ads,
1543 TALLOC_CTX *mem_ctx, void *msg, const char *field)
1545 char **values;
1546 char **ret = NULL;
1547 int i, n;
1549 values = ldap_get_values(ads->ld, msg, field);
1550 if (!values)
1551 return NULL;
1553 for (i=0;values[i];i++)
1554 /* noop */ ;
1555 n = i;
1557 ret = talloc(mem_ctx, sizeof(char *) * (n+1));
1558 if (!ret) {
1559 ldap_value_free(values);
1560 return NULL;
1563 for (i=0;i<n;i++) {
1564 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
1565 ldap_value_free(values);
1566 return NULL;
1569 ret[i] = NULL;
1571 ldap_value_free(values);
1572 return ret;
1577 * pull a single uint32 from a ADS result
1578 * @param ads connection to ads server
1579 * @param msg Results of search
1580 * @param field Attribute to retrieve
1581 * @param v Pointer to int to store result
1582 * @return boolean inidicating success
1584 BOOL ads_pull_uint32(ADS_STRUCT *ads,
1585 void *msg, const char *field, uint32 *v)
1587 char **values;
1589 values = ldap_get_values(ads->ld, msg, field);
1590 if (!values)
1591 return False;
1592 if (!values[0]) {
1593 ldap_value_free(values);
1594 return False;
1597 *v = atoi(values[0]);
1598 ldap_value_free(values);
1599 return True;
1603 * pull a single objectGUID from an ADS result
1604 * @param ads connection to ADS server
1605 * @param msg results of search
1606 * @param guid 37-byte area to receive text guid
1607 * @return boolean indicating success
1609 BOOL ads_pull_guid(ADS_STRUCT *ads,
1610 void *msg, GUID *guid)
1612 char **values;
1614 values = ldap_get_values(ads->ld, msg, "objectGUID");
1615 if (!values)
1616 return False;
1618 if (values[0]) {
1619 memcpy(guid, values[0], sizeof(GUID));
1620 ldap_value_free(values);
1621 return True;
1623 ldap_value_free(values);
1624 return False;
1630 * pull a single DOM_SID from a ADS result
1631 * @param ads connection to ads server
1632 * @param msg Results of search
1633 * @param field Attribute to retrieve
1634 * @param sid Pointer to sid to store result
1635 * @return boolean inidicating success
1637 BOOL ads_pull_sid(ADS_STRUCT *ads,
1638 void *msg, const char *field, DOM_SID *sid)
1640 struct berval **values;
1641 BOOL ret = False;
1643 values = ldap_get_values_len(ads->ld, msg, field);
1645 if (!values)
1646 return False;
1648 if (values[0])
1649 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
1651 ldap_value_free_len(values);
1652 return ret;
1656 * pull an array of DOM_SIDs from a ADS result
1657 * @param ads connection to ads server
1658 * @param mem_ctx TALLOC_CTX for allocating sid array
1659 * @param msg Results of search
1660 * @param field Attribute to retrieve
1661 * @param sids pointer to sid array to allocate
1662 * @return the count of SIDs pulled
1664 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
1665 void *msg, const char *field, DOM_SID **sids)
1667 struct berval **values;
1668 BOOL ret;
1669 int count, i;
1671 values = ldap_get_values_len(ads->ld, msg, field);
1673 if (!values)
1674 return 0;
1676 for (i=0; values[i]; i++)
1677 /* nop */ ;
1679 (*sids) = talloc(mem_ctx, sizeof(DOM_SID) * i);
1680 if (!(*sids)) {
1681 ldap_value_free_len(values);
1682 return 0;
1685 count = 0;
1686 for (i=0; values[i]; i++) {
1687 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
1688 if (ret) {
1689 fstring sid;
1690 DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid, &(*sids)[count])));
1691 count++;
1695 ldap_value_free_len(values);
1696 return count;
1700 * pull a SEC_DESC from a ADS result
1701 * @param ads connection to ads server
1702 * @param mem_ctx TALLOC_CTX for allocating sid array
1703 * @param msg Results of search
1704 * @param field Attribute to retrieve
1705 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
1706 * @return boolean inidicating success
1708 BOOL ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
1709 void *msg, const char *field, SEC_DESC **sd)
1711 struct berval **values;
1712 prs_struct ps;
1713 BOOL ret = False;
1715 values = ldap_get_values_len(ads->ld, msg, field);
1717 if (!values) return False;
1719 if (values[0]) {
1720 prs_init(&ps, values[0]->bv_len, mem_ctx, UNMARSHALL);
1721 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
1722 prs_set_offset(&ps,0);
1724 ret = sec_io_desc("sd", sd, &ps, 1);
1727 ldap_value_free_len(values);
1728 return ret;
1732 * in order to support usernames longer than 21 characters we need to
1733 * use both the sAMAccountName and the userPrincipalName attributes
1734 * It seems that not all users have the userPrincipalName attribute set
1736 * @param ads connection to ads server
1737 * @param mem_ctx TALLOC_CTX for allocating sid array
1738 * @param msg Results of search
1739 * @return the username
1741 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, void *msg)
1743 char *ret, *p;
1745 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
1746 if (ret && (p = strchr(ret, '@'))) {
1747 *p = 0;
1748 return ret;
1750 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
1755 * find the update serial number - this is the core of the ldap cache
1756 * @param ads connection to ads server
1757 * @param ads connection to ADS server
1758 * @param usn Pointer to retrieved update serial number
1759 * @return status of search
1761 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
1763 const char *attrs[] = {"highestCommittedUSN", NULL};
1764 ADS_STATUS status;
1765 void *res;
1767 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
1768 if (!ADS_ERR_OK(status)) return status;
1770 if (ads_count_replies(ads, res) != 1) {
1771 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1774 ads_pull_uint32(ads, res, "highestCommittedUSN", usn);
1775 ads_msgfree(ads, res);
1776 return ADS_SUCCESS;
1779 /* parse a ADS timestring - typical string is
1780 '20020917091222.0Z0' which means 09:12.22 17th September
1781 2002, timezone 0 */
1782 static time_t ads_parse_time(const char *str)
1784 struct tm tm;
1786 ZERO_STRUCT(tm);
1788 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
1789 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
1790 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
1791 return 0;
1793 tm.tm_year -= 1900;
1794 tm.tm_mon -= 1;
1796 return timegm(&tm);
1801 * Find the servers name and realm - this can be done before authentication
1802 * The ldapServiceName field on w2k looks like this:
1803 * vnet3.home.samba.org:win2000-vnet3$@VNET3.HOME.SAMBA.ORG
1804 * @param ads connection to ads server
1805 * @return status of search
1807 ADS_STATUS ads_server_info(ADS_STRUCT *ads)
1809 const char *attrs[] = {"ldapServiceName", "currentTime", NULL};
1810 ADS_STATUS status;
1811 void *res;
1812 char *value;
1813 char *p;
1814 char *timestr;
1815 TALLOC_CTX *ctx;
1817 if (!(ctx = talloc_init("ads_server_info"))) {
1818 return ADS_ERROR(LDAP_NO_MEMORY);
1821 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
1822 if (!ADS_ERR_OK(status)) return status;
1824 value = ads_pull_string(ads, ctx, res, "ldapServiceName");
1825 if (!value) {
1826 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1829 timestr = ads_pull_string(ads, ctx, res, "currentTime");
1830 if (!timestr) {
1831 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1834 ldap_msgfree(res);
1836 p = strchr(value, ':');
1837 if (!p) {
1838 talloc_destroy(ctx);
1839 DEBUG(1, ("ads_server_info: returned ldap server name did not contain a ':' so was deemed invalid\n"));
1840 return ADS_ERROR(LDAP_DECODING_ERROR);
1843 SAFE_FREE(ads->config.ldap_server_name);
1845 ads->config.ldap_server_name = strdup(p+1);
1846 p = strchr(ads->config.ldap_server_name, '$');
1847 if (!p || p[1] != '@') {
1848 talloc_destroy(ctx);
1849 DEBUG(1, ("ads_server_info: returned ldap server name (%s) does not contain '$@' so was deemed invalid\n", ads->config.ldap_server_name));
1850 SAFE_FREE(ads->config.ldap_server_name);
1851 return ADS_ERROR(LDAP_DECODING_ERROR);
1854 *p = 0;
1856 SAFE_FREE(ads->config.realm);
1857 SAFE_FREE(ads->config.bind_path);
1859 ads->config.realm = strdup(p+2);
1860 ads->config.bind_path = ads_build_dn(ads->config.realm);
1862 DEBUG(3,("got ldap server name %s@%s, using bind path: %s\n",
1863 ads->config.ldap_server_name, ads->config.realm,
1864 ads->config.bind_path));
1866 ads->config.current_time = ads_parse_time(timestr);
1868 if (ads->config.current_time != 0) {
1869 ads->auth.time_offset = ads->config.current_time - time(NULL);
1870 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
1873 talloc_destroy(ctx);
1875 return ADS_SUCCESS;
1880 * find the list of trusted domains
1881 * @param ads connection to ads server
1882 * @param mem_ctx TALLOC_CTX for allocating results
1883 * @param num_trusts pointer to number of trusts
1884 * @param names pointer to trusted domain name list
1885 * @param sids pointer to list of sids of trusted domains
1886 * @return the count of SIDs pulled
1888 ADS_STATUS ads_trusted_domains(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
1889 int *num_trusts,
1890 char ***names,
1891 char ***alt_names,
1892 DOM_SID **sids)
1894 const char *attrs[] = {"name", "flatname", "securityIdentifier",
1895 "trustDirection", NULL};
1896 ADS_STATUS status;
1897 void *res, *msg;
1898 int count, i;
1900 *num_trusts = 0;
1902 status = ads_search(ads, &res, "(objectcategory=trustedDomain)", attrs);
1903 if (!ADS_ERR_OK(status)) return status;
1905 count = ads_count_replies(ads, res);
1906 if (count == 0) {
1907 ads_msgfree(ads, res);
1908 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1911 (*names) = talloc(mem_ctx, sizeof(char *) * count);
1912 (*alt_names) = talloc(mem_ctx, sizeof(char *) * count);
1913 (*sids) = talloc(mem_ctx, sizeof(DOM_SID) * count);
1914 if (! *names || ! *sids) return ADS_ERROR(LDAP_NO_MEMORY);
1916 for (i=0, msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
1917 uint32 direction;
1919 /* direction is a 2 bit bitfield, 1 means they trust us
1920 but we don't trust them, so we should not list them
1921 as users from that domain can't login */
1922 if (ads_pull_uint32(ads, msg, "trustDirection", &direction) &&
1923 direction == 1) {
1924 continue;
1927 (*names)[i] = ads_pull_string(ads, mem_ctx, msg, "name");
1928 (*alt_names)[i] = ads_pull_string(ads, mem_ctx, msg, "flatname");
1930 if ((*alt_names)[i] && (*alt_names)[i][0]) {
1931 /* we prefer the flatname as the primary name
1932 for consistency with RPC */
1933 char *name = (*alt_names)[i];
1934 (*alt_names)[i] = (*names)[i];
1935 (*names)[i] = name;
1937 if (ads_pull_sid(ads, msg, "securityIdentifier", &(*sids)[i])) {
1938 i++;
1942 ads_msgfree(ads, res);
1944 *num_trusts = i;
1946 return ADS_SUCCESS;
1950 * find the domain sid for our domain
1951 * @param ads connection to ads server
1952 * @param sid Pointer to domain sid
1953 * @return status of search
1955 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
1957 const char *attrs[] = {"objectSid", NULL};
1958 void *res;
1959 ADS_STATUS rc;
1961 rc = ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
1962 attrs, &res);
1963 if (!ADS_ERR_OK(rc)) return rc;
1964 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
1965 return ADS_ERROR_SYSTEM(ENOENT);
1967 ads_msgfree(ads, res);
1969 return ADS_SUCCESS;
1972 /* this is rather complex - we need to find the allternate (netbios) name
1973 for the domain, but there isn't a simple query to do this. Instead
1974 we look for the principle names on the DCs account and find one that has
1975 the right form, then extract the netbios name of the domain from that
1977 NOTE! better method is this:
1979 bin/net -Uadministrator%XXXXX ads search '(&(objectclass=crossref)(dnsroot=VNET3.HOME.SAMBA.ORG))' nETBIOSName
1981 but you need to force the bind path to match the configurationNamingContext from the rootDSE
1984 ADS_STATUS ads_workgroup_name(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char **workgroup)
1986 char *expr;
1987 ADS_STATUS rc;
1988 char **principles;
1989 char *prefix;
1990 int prefix_length;
1991 int i;
1992 void *res;
1993 const char *attrs[] = {"servicePrincipalName", NULL};
1995 (*workgroup) = NULL;
1997 asprintf(&expr, "(&(objectclass=computer)(dnshostname=%s.%s))",
1998 ads->config.ldap_server_name, ads->config.realm);
1999 rc = ads_search(ads, &res, expr, attrs);
2000 free(expr);
2002 if (!ADS_ERR_OK(rc)) {
2003 return rc;
2006 principles = ads_pull_strings(ads, mem_ctx, res, "servicePrincipalName");
2008 ads_msgfree(ads, res);
2010 if (!principles) {
2011 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2014 asprintf(&prefix, "HOST/%s.%s/",
2015 ads->config.ldap_server_name,
2016 ads->config.realm);
2018 prefix_length = strlen(prefix);
2020 for (i=0;principles[i]; i++) {
2021 if (strncasecmp(principles[i], prefix, prefix_length) == 0 &&
2022 strcasecmp(ads->config.realm, principles[i]+prefix_length) != 0 &&
2023 !strchr(principles[i]+prefix_length, '.')) {
2024 /* found an alternate (short) name for the domain. */
2025 DEBUG(3,("Found alternate name '%s' for realm '%s'\n",
2026 principles[i]+prefix_length,
2027 ads->config.realm));
2028 (*workgroup) = talloc_strdup(mem_ctx, principles[i]+prefix_length);
2029 break;
2032 free(prefix);
2034 if (!*workgroup) {
2035 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2038 return ADS_SUCCESS;
2041 #endif