Add clock skew handling to our kerberos code. This allows us to cope with
[Samba.git] / source / libads / ldap.c
blob385a9bd93f90e326d4ab79d410c11485363f1009
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_ADS
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 static BOOL ads_try_connect(ADS_STRUCT *ads, const char *server, unsigned port)
47 char *srv;
49 if (!server || !*server) {
50 return False;
53 DEBUG(5,("ads_try_connect: trying ldap server '%s' port %u\n", server, port));
55 /* this copes with inet_ntoa brokenness */
56 srv = strdup(server);
58 ads->ld = ldap_open(srv, port);
59 if (!ads->ld) {
60 free(srv);
61 return False;
63 ads->ldap_port = port;
64 ads->ldap_ip = *interpret_addr2(srv);
65 free(srv);
67 return True;
70 /* used by the IP comparison function */
71 struct ldap_ip {
72 struct in_addr ip;
73 unsigned port;
76 /* compare 2 ldap IPs by nearness to our interfaces - used in qsort */
77 static int ldap_ip_compare(struct ldap_ip *ip1, struct ldap_ip *ip2)
79 return ip_compare(&ip1->ip, &ip2->ip);
82 /* try connecting to a ldap server via DNS */
83 static BOOL ads_try_dns(ADS_STRUCT *ads)
85 char *realm, *ptr;
86 char *list = NULL;
87 pstring tok;
88 struct ldap_ip *ip_list;
89 int count, i=0;
91 realm = ads->server.realm;
92 if (!realm || !*realm) {
93 realm = lp_realm();
95 if (!realm || !*realm) {
96 realm = ads->server.workgroup;
98 if (!realm || !*realm) {
99 realm = lp_workgroup();
101 if (!realm) {
102 return False;
104 realm = smb_xstrdup(realm);
106 DEBUG(6,("ads_try_dns: looking for realm '%s'\n", realm));
107 if (ldap_domain2hostlist(realm, &list) != LDAP_SUCCESS) {
108 SAFE_FREE(realm);
109 return False;
112 DEBUG(6,("ads_try_dns: ldap realm '%s' host list '%s'\n", realm, list));
113 SAFE_FREE(realm);
115 count = count_chars(list, ' ') + 1;
116 ip_list = malloc(count * sizeof(struct ldap_ip));
117 if (!ip_list) {
118 return False;
121 ptr = list;
122 while (next_token(&ptr, tok, " ", sizeof(tok))) {
123 unsigned port = LDAP_PORT;
124 char *p = strchr(tok, ':');
125 if (p) {
126 *p = 0;
127 port = atoi(p+1);
129 ip_list[i].ip = *interpret_addr2(tok);
130 ip_list[i].port = port;
131 if (!is_zero_ip(ip_list[i].ip)) {
132 i++;
135 free(list);
137 count = i;
139 /* we sort the list of addresses by closeness to our interfaces. This
140 tries to prevent us using a DC on the other side of the country */
141 if (count > 1) {
142 qsort(ip_list, count, sizeof(struct ldap_ip),
143 QSORT_CAST ldap_ip_compare);
146 for (i=0;i<count;i++) {
147 if (ads_try_connect(ads, inet_ntoa(ip_list[i].ip), ip_list[i].port)) {
148 free(ip_list);
149 return True;
153 SAFE_FREE(ip_list);
154 return False;
157 /* try connecting to a ldap server via netbios */
158 static BOOL ads_try_netbios(ADS_STRUCT *ads)
160 struct in_addr *ip_list;
161 int count;
162 int i;
163 char *workgroup = ads->server.workgroup;
165 if (!workgroup) {
166 workgroup = lp_workgroup();
169 DEBUG(6,("ads_try_netbios: looking for workgroup '%s'\n", workgroup));
171 /* try the PDC first */
172 if (get_dc_list(True, workgroup, &ip_list, &count)) {
173 for (i=0;i<count;i++) {
174 DEBUG(6,("ads_try_netbios: trying server '%s'\n",
175 inet_ntoa(ip_list[i])));
176 if (ads_try_connect(ads, inet_ntoa(ip_list[i]), LDAP_PORT)) {
177 free(ip_list);
178 return True;
181 free(ip_list);
184 /* now any DC, including backups */
185 if (get_dc_list(False, workgroup, &ip_list, &count)) {
186 for (i=0;i<count;i++) {
187 DEBUG(6,("ads_try_netbios: trying server '%s'\n",
188 inet_ntoa(ip_list[i])));
189 if (ads_try_connect(ads, inet_ntoa(ip_list[i]), LDAP_PORT)) {
190 free(ip_list);
191 return True;
194 free(ip_list);
197 return False;
201 * Connect to the LDAP server
202 * @param ads Pointer to an existing ADS_STRUCT
203 * @return status of connection
205 ADS_STATUS ads_connect(ADS_STRUCT *ads)
207 int version = LDAP_VERSION3;
208 ADS_STATUS status;
210 ads->last_attempt = time(NULL);
211 ads->ld = NULL;
213 /* try with a user specified server */
214 if (ads->server.ldap_server &&
215 ads_try_connect(ads, ads->server.ldap_server, LDAP_PORT)) {
216 goto got_connection;
219 /* try with a smb.conf ads server setting if we are connecting
220 to the primary workgroup or realm */
221 if (!ads->server.foreign &&
222 ads_try_connect(ads, lp_ads_server(), LDAP_PORT)) {
223 goto got_connection;
226 /* try via DNS */
227 if (ads_try_dns(ads)) {
228 goto got_connection;
231 /* try via netbios lookups */
232 if (!lp_disable_netbios() && ads_try_netbios(ads)) {
233 goto got_connection;
236 return ADS_ERROR_SYSTEM(errno?errno:ENOENT);
238 got_connection:
239 DEBUG(3,("Connected to LDAP server %s\n", inet_ntoa(ads->ldap_ip)));
241 status = ads_server_info(ads);
242 if (!ADS_ERR_OK(status)) {
243 DEBUG(1,("Failed to get ldap server info\n"));
244 return status;
247 ldap_set_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
249 if (!ads->auth.user_name) {
250 /* by default use the machine account */
251 extern pstring global_myname;
252 fstring myname;
253 fstrcpy(myname, global_myname);
254 strlower(myname);
255 asprintf(&ads->auth.user_name, "HOST/%s", myname);
258 if (!ads->auth.realm) {
259 ads->auth.realm = strdup(ads->config.realm);
262 if (!ads->auth.kdc_server) {
263 ads->auth.kdc_server = strdup(inet_ntoa(ads->ldap_ip));
266 #if KRB5_DNS_HACK
267 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
268 to MIT kerberos to work (tridge) */
270 char *env;
271 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
272 setenv(env, ads->auth.kdc_server, 1);
273 free(env);
275 #endif
277 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
278 return ADS_SUCCESS;
281 return ads_sasl_bind(ads);
285 Duplicate a struct berval into talloc'ed memory
287 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
289 struct berval *value;
291 if (!in_val) return NULL;
293 value = talloc_zero(ctx, sizeof(struct berval));
294 if (in_val->bv_len == 0) return value;
296 value->bv_len = in_val->bv_len;
297 value->bv_val = talloc_memdup(ctx, in_val->bv_val, in_val->bv_len);
298 return value;
302 Make a values list out of an array of (struct berval *)
304 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
305 const struct berval **in_vals)
307 struct berval **values;
308 int i;
310 if (!in_vals) return NULL;
311 for (i=0; in_vals[i]; i++); /* count values */
312 values = (struct berval **) talloc_zero(ctx,
313 (i+1)*sizeof(struct berval *));
314 if (!values) return NULL;
316 for (i=0; in_vals[i]; i++) {
317 values[i] = dup_berval(ctx, in_vals[i]);
319 return values;
323 UTF8-encode a values list out of an array of (char *)
325 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
327 char **values;
328 int i;
330 if (!in_vals) return NULL;
331 for (i=0; in_vals[i]; i++); /* count values */
332 values = (char ** ) talloc_zero(ctx, (i+1)*sizeof(char *));
333 if (!values) return NULL;
335 for (i=0; in_vals[i]; i++) {
336 push_utf8_talloc(ctx, &values[i], in_vals[i]);
338 return values;
342 Pull a (char *) array out of a UTF8-encoded values list
344 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
346 char **values;
347 int i;
349 if (!in_vals) return NULL;
350 for (i=0; in_vals[i]; i++); /* count values */
351 values = (char **) talloc_zero(ctx, (i+1)*sizeof(char *));
352 if (!values) return NULL;
354 for (i=0; in_vals[i]; i++) {
355 pull_utf8_talloc(ctx, &values[i], in_vals[i]);
357 return values;
361 * Do a search with paged results. cookie must be null on the first
362 * call, and then returned on each subsequent call. It will be null
363 * again when the entire search is complete
364 * @param ads connection to ads server
365 * @param bind_path Base dn for the search
366 * @param scope Scope of search (LDAP_BASE | LDAP_ONE | LDAP_SUBTREE)
367 * @param exp Search expression - specified in local charset
368 * @param attrs Attributes to retrieve - specified in utf8 or ascii
369 * @param res ** which will contain results - free res* with ads_msgfree()
370 * @param count Number of entries retrieved on this page
371 * @param cookie The paged results cookie to be returned on subsequent calls
372 * @return status of search
374 ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
375 int scope, const char *exp,
376 const char **attrs, void **res,
377 int *count, void **cookie)
379 int rc, i, version;
380 char *utf8_exp, *utf8_path, **search_attrs;
381 LDAPControl PagedResults, NoReferrals, *controls[3], **rcontrols;
382 BerElement *cookie_be = NULL;
383 struct berval *cookie_bv= NULL;
384 TALLOC_CTX *ctx;
386 *res = NULL;
388 if (!(ctx = talloc_init()))
389 return ADS_ERROR(LDAP_NO_MEMORY);
391 /* 0 means the conversion worked but the result was empty
392 so we only fail if it's negative. In any case, it always
393 at least nulls out the dest */
394 if ((push_utf8_talloc(ctx, &utf8_exp, exp) < 0) ||
395 (push_utf8_talloc(ctx, &utf8_path, bind_path) < 0)) {
396 rc = LDAP_NO_MEMORY;
397 goto done;
400 if (!attrs || !(*attrs))
401 search_attrs = NULL;
402 else {
403 /* This would be the utf8-encoded version...*/
404 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
405 if (!(str_list_copy(&search_attrs, attrs)))
407 rc = LDAP_NO_MEMORY;
408 goto done;
413 /* Paged results only available on ldap v3 or later */
414 ldap_get_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
415 if (version < LDAP_VERSION3) {
416 rc = LDAP_NOT_SUPPORTED;
417 goto done;
420 cookie_be = ber_alloc_t(LBER_USE_DER);
421 if (cookie && *cookie) {
422 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
423 ber_bvfree(*cookie); /* don't need it from last time */
424 *cookie = NULL;
425 } else {
426 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
428 ber_flatten(cookie_be, &cookie_bv);
429 PagedResults.ldctl_oid = ADS_PAGE_CTL_OID;
430 PagedResults.ldctl_iscritical = (char) 1;
431 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
432 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
434 NoReferrals.ldctl_oid = ADS_NO_REFERRALS_OID;
435 NoReferrals.ldctl_iscritical = (char) 0;
436 NoReferrals.ldctl_value.bv_len = 0;
437 NoReferrals.ldctl_value.bv_val = "";
440 controls[0] = &NoReferrals;
441 controls[1] = &PagedResults;
442 controls[2] = NULL;
444 *res = NULL;
446 /* we need to disable referrals as the openldap libs don't
447 handle them and paged results at the same time. Using them
448 together results in the result record containing the server
449 page control being removed from the result list (tridge/jmcd)
451 leaving this in despite the control that says don't generate
452 referrals, in case the server doesn't support it (jmcd)
454 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
456 rc = ldap_search_ext_s(ads->ld, utf8_path, scope, utf8_exp,
457 search_attrs, 0, controls,
458 NULL, NULL, LDAP_NO_LIMIT, (LDAPMessage **)res);
460 ber_free(cookie_be, 1);
461 ber_bvfree(cookie_bv);
463 if (rc) {
464 DEBUG(3,("ldap_search_ext_s(%s) -> %s\n", exp, ldap_err2string(rc)));
465 goto done;
468 rc = ldap_parse_result(ads->ld, *res, NULL, NULL, NULL,
469 NULL, &rcontrols, 0);
471 if (!rcontrols) {
472 goto done;
475 for (i=0; rcontrols[i]; i++) {
476 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
477 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
478 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
479 &cookie_bv);
480 /* the berval is the cookie, but must be freed when
481 it is all done */
482 if (cookie_bv->bv_len) /* still more to do */
483 *cookie=ber_bvdup(cookie_bv);
484 else
485 *cookie=NULL;
486 ber_bvfree(cookie_bv);
487 ber_free(cookie_be, 1);
488 break;
491 ldap_controls_free(rcontrols);
493 done:
494 talloc_destroy(ctx);
495 /* if/when we decide to utf8-encode attrs, take out this next line */
496 str_list_free(&search_attrs);
498 return ADS_ERROR(rc);
503 * Get all results for a search. This uses ads_do_paged_search() to return
504 * all entries in a large search.
505 * @param ads connection to ads server
506 * @param bind_path Base dn for the search
507 * @param scope Scope of search (LDAP_BASE | LDAP_ONE | LDAP_SUBTREE)
508 * @param exp Search expression
509 * @param attrs Attributes to retrieve
510 * @param res ** which will contain results - free res* with ads_msgfree()
511 * @return status of search
513 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
514 int scope, const char *exp,
515 const char **attrs, void **res)
517 void *cookie = NULL;
518 int count = 0;
519 ADS_STATUS status;
521 status = ads_do_paged_search(ads, bind_path, scope, exp, attrs, res,
522 &count, &cookie);
524 if (!ADS_ERR_OK(status)) return status;
526 while (cookie) {
527 void *res2 = NULL;
528 ADS_STATUS status2;
529 LDAPMessage *msg, *next;
531 status2 = ads_do_paged_search(ads, bind_path, scope, exp,
532 attrs, &res2, &count, &cookie);
534 if (!ADS_ERR_OK(status2)) break;
536 /* this relies on the way that ldap_add_result_entry() works internally. I hope
537 that this works on all ldap libs, but I have only tested with openldap */
538 for (msg = ads_first_entry(ads, res2); msg; msg = next) {
539 next = ads_next_entry(ads, msg);
540 ldap_add_result_entry((LDAPMessage **)res, msg);
542 /* note that we do not free res2, as the memory is now
543 part of the main returned list */
546 return status;
550 * Run a function on all results for a search. Uses ads_do_paged_search() and
551 * runs the function as each page is returned, using ads_process_results()
552 * @param ads connection to ads server
553 * @param bind_path Base dn for the search
554 * @param scope Scope of search (LDAP_BASE | LDAP_ONE | LDAP_SUBTREE)
555 * @param exp Search expression - specified in local charset
556 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
557 * @param fn Function which takes attr name, values list, and data_area
558 * @param data_area Pointer which is passed to function on each call
559 * @return status of search
561 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
562 int scope, const char *exp, const char **attrs,
563 BOOL(*fn)(char *, void **, void *),
564 void *data_area)
566 void *cookie = NULL;
567 int count = 0;
568 ADS_STATUS status;
569 void *res;
571 status = ads_do_paged_search(ads, bind_path, scope, exp, attrs, &res,
572 &count, &cookie);
574 if (!ADS_ERR_OK(status)) return status;
576 ads_process_results(ads, res, fn, data_area);
577 ads_msgfree(ads, res);
579 while (cookie) {
580 status = ads_do_paged_search(ads, bind_path, scope, exp, attrs,
581 &res, &count, &cookie);
583 if (!ADS_ERR_OK(status)) break;
585 ads_process_results(ads, res, fn, data_area);
586 ads_msgfree(ads, res);
589 return status;
593 * Do a search with a timeout.
594 * @param ads connection to ads server
595 * @param bind_path Base dn for the search
596 * @param scope Scope of search (LDAP_BASE | LDAP_ONE | LDAP_SUBTREE)
597 * @param exp Search expression
598 * @param attrs Attributes to retrieve
599 * @param res ** which will contain results - free res* with ads_msgfree()
600 * @return status of search
602 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
603 const char *exp,
604 const char **attrs, void **res)
606 struct timeval timeout;
607 int rc;
608 char *utf8_exp, *utf8_path, **search_attrs = NULL;
609 TALLOC_CTX *ctx;
611 if (!(ctx = talloc_init())) {
612 DEBUG(1,("ads_do_search: talloc_init() failed!"));
613 return ADS_ERROR(LDAP_NO_MEMORY);
616 /* 0 means the conversion worked but the result was empty
617 so we only fail if it's negative. In any case, it always
618 at least nulls out the dest */
619 if ((push_utf8_talloc(ctx, &utf8_exp, exp) < 0) ||
620 (push_utf8_talloc(ctx, &utf8_path, bind_path) < 0)) {
621 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
622 rc = LDAP_NO_MEMORY;
623 goto done;
626 if (!attrs || !(*attrs))
627 search_attrs = NULL;
628 else {
629 /* This would be the utf8-encoded version...*/
630 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
631 if (!(str_list_copy(&search_attrs, attrs)))
633 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
634 rc = LDAP_NO_MEMORY;
635 goto done;
639 timeout.tv_sec = ADS_SEARCH_TIMEOUT;
640 timeout.tv_usec = 0;
641 *res = NULL;
643 /* see the note in ads_do_paged_search - we *must* disable referrals */
644 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
646 rc = ldap_search_ext_s(ads->ld, utf8_path, scope, utf8_exp,
647 search_attrs, 0, NULL, NULL,
648 &timeout, LDAP_NO_LIMIT, (LDAPMessage **)res);
650 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
651 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
652 rc = 0;
655 done:
656 talloc_destroy(ctx);
657 /* if/when we decide to utf8-encode attrs, take out this next line */
658 str_list_free(&search_attrs);
659 return ADS_ERROR(rc);
662 * Do a general ADS search
663 * @param ads connection to ads server
664 * @param res ** which will contain results - free res* with ads_msgfree()
665 * @param exp Search expression
666 * @param attrs Attributes to retrieve
667 * @return status of search
669 ADS_STATUS ads_search(ADS_STRUCT *ads, void **res,
670 const char *exp,
671 const char **attrs)
673 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
674 exp, attrs, res);
678 * Do a search on a specific DistinguishedName
679 * @param ads connection to ads server
680 * @param res ** which will contain results - free res* with ads_msgfree()
681 * @param dn DistinguishName to search
682 * @param attrs Attributes to retrieve
683 * @return status of search
685 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, void **res,
686 const char *dn,
687 const char **attrs)
689 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)", attrs, res);
693 * Free up memory from a ads_search
694 * @param ads connection to ads server
695 * @param msg Search results to free
697 void ads_msgfree(ADS_STRUCT *ads, void *msg)
699 if (!msg) return;
700 ldap_msgfree(msg);
704 * Free up memory from various ads requests
705 * @param ads connection to ads server
706 * @param mem Area to free
708 void ads_memfree(ADS_STRUCT *ads, void *mem)
710 SAFE_FREE(mem);
714 * Get a dn from search results
715 * @param ads connection to ads server
716 * @param res Search results
717 * @return dn string
719 char *ads_get_dn(ADS_STRUCT *ads, void *res)
721 char *utf8_dn, *unix_dn;
723 utf8_dn = ldap_get_dn(ads->ld, res);
724 pull_utf8_allocate((void **) &unix_dn, utf8_dn);
725 ldap_memfree(utf8_dn);
726 return unix_dn;
730 * Find a machine account given a hostname
731 * @param ads connection to ads server
732 * @param res ** which will contain results - free res* with ads_msgfree()
733 * @param host Hostname to search for
734 * @return status of search
736 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, void **res, const char *host)
738 ADS_STATUS status;
739 char *exp;
740 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
742 /* the easiest way to find a machine account anywhere in the tree
743 is to look for hostname$ */
744 asprintf(&exp, "(samAccountName=%s$)", host);
745 status = ads_search(ads, res, exp, attrs);
746 free(exp);
747 return status;
751 * Initialize a list of mods to be used in a modify request
752 * @param ctx An initialized TALLOC_CTX
753 * @return allocated ADS_MODLIST
755 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
757 #define ADS_MODLIST_ALLOC_SIZE 10
758 LDAPMod **mods;
760 if ((mods = (LDAPMod **) talloc_zero(ctx, sizeof(LDAPMod *) *
761 (ADS_MODLIST_ALLOC_SIZE + 1))))
762 /* -1 is safety to make sure we don't go over the end.
763 need to reset it to NULL before doing ldap modify */
764 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
766 return mods;
771 add an attribute to the list, with values list already constructed
773 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
774 int mod_op, const char *name,
775 const void **invals)
777 int curmod;
778 LDAPMod **modlist = (LDAPMod **) *mods;
779 void **values;
781 if (!invals) {
782 values = NULL;
783 mod_op = LDAP_MOD_DELETE;
784 } else {
785 if (mod_op & LDAP_MOD_BVALUES)
786 values = (void **) ads_dup_values(ctx,
787 (const struct berval **)invals);
788 else
789 values = (void **) ads_push_strvals(ctx,
790 (const char **) invals);
793 /* find the first empty slot */
794 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
795 curmod++);
796 if (modlist[curmod] == (LDAPMod *) -1) {
797 if (!(modlist = talloc_realloc(ctx, modlist,
798 (curmod+ADS_MODLIST_ALLOC_SIZE+1)*sizeof(LDAPMod *))))
799 return ADS_ERROR(LDAP_NO_MEMORY);
800 memset(&modlist[curmod], 0,
801 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
802 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
803 *mods = modlist;
806 if (!(modlist[curmod] = talloc_zero(ctx, sizeof(LDAPMod))))
807 return ADS_ERROR(LDAP_NO_MEMORY);
808 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
809 if (mod_op & LDAP_MOD_BVALUES)
810 modlist[curmod]->mod_bvalues = (struct berval **) values;
811 else
812 modlist[curmod]->mod_values = (char **) values;
813 modlist[curmod]->mod_op = mod_op;
814 return ADS_ERROR(LDAP_SUCCESS);
818 * Add a single string value to a mod list
819 * @param ctx An initialized TALLOC_CTX
820 * @param mods An initialized ADS_MODLIST
821 * @param name The attribute name to add
822 * @param val The value to add - NULL means DELETE
823 * @return ADS STATUS indicating success of add
825 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
826 const char *name, const char *val)
828 const char *values[2];
830 values[0] = val;
831 values[1] = NULL;
833 if (!val)
834 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
835 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name,
836 (const void **) values);
840 * Add an array of string values 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 vals The array of string values to add - NULL means DELETE
845 * @return ADS STATUS indicating success of add
847 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
848 const char *name, const char **vals)
850 if (!vals)
851 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
852 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
853 name, (const void **) vals);
857 * Add a single ber-encoded value to a mod list
858 * @param ctx An initialized TALLOC_CTX
859 * @param mods An initialized ADS_MODLIST
860 * @param name The attribute name to add
861 * @param val The value to add - NULL means DELETE
862 * @return ADS STATUS indicating success of add
864 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
865 const char *name, const struct berval *val)
867 const struct berval *values[2];
869 values[0] = val;
870 values[1] = NULL;
871 if (!val)
872 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
873 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
874 name, (const void **) values);
878 * Perform an ldap modify
879 * @param ads connection to ads server
880 * @param mod_dn DistinguishedName to modify
881 * @param mods list of modifications to perform
882 * @return status of modify
884 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
886 int ret,i;
887 char *utf8_dn = NULL;
889 this control is needed to modify that contains a currently
890 non-existent attribute (but allowable for the object) to run
892 LDAPControl PermitModify = {
893 "1.2.840.113556.1.4.1413",
894 {0, NULL},
895 (char) 1};
896 LDAPControl *controls[2];
898 controls[0] = &PermitModify;
899 controls[1] = NULL;
901 push_utf8_allocate((void **) &utf8_dn, mod_dn);
903 /* find the end of the list, marked by NULL or -1 */
904 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
905 /* make sure the end of the list is NULL */
906 mods[i] = NULL;
907 ret = ldap_modify_ext_s(ads->ld, utf8_dn ? utf8_dn : mod_dn,
908 (LDAPMod **) mods, controls, NULL);
909 SAFE_FREE(utf8_dn);
910 return ADS_ERROR(ret);
914 * Perform an ldap add
915 * @param ads connection to ads server
916 * @param new_dn DistinguishedName to add
917 * @param mods list of attributes and values for DN
918 * @return status of add
920 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
922 int ret, i;
923 char *utf8_dn = NULL;
925 push_utf8_allocate((void **) &utf8_dn, new_dn);
927 /* find the end of the list, marked by NULL or -1 */
928 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
929 /* make sure the end of the list is NULL */
930 mods[i] = NULL;
932 ret = ldap_add_s(ads->ld, utf8_dn ? utf8_dn : new_dn, mods);
933 SAFE_FREE(utf8_dn);
934 return ADS_ERROR(ret);
938 * Delete a DistinguishedName
939 * @param ads connection to ads server
940 * @param new_dn DistinguishedName to delete
941 * @return status of delete
943 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
945 int ret;
946 char *utf8_dn = NULL;
947 push_utf8_allocate((void **) &utf8_dn, del_dn);
948 ret = ldap_delete(ads->ld, utf8_dn ? utf8_dn : del_dn);
949 return ADS_ERROR(ret);
953 * Build an org unit string
954 * if org unit is Computers or blank then assume a container, otherwise
955 * assume a \ separated list of organisational units
956 * @param org_unit Organizational unit
957 * @return org unit string - caller must free
959 char *ads_ou_string(const char *org_unit)
961 if (!org_unit || !*org_unit || strcasecmp(org_unit, "Computers") == 0) {
962 return strdup("cn=Computers");
965 return ads_build_path(org_unit, "\\/", "ou=", 1);
971 add a machine account to the ADS server
973 static ADS_STATUS ads_add_machine_acct(ADS_STRUCT *ads, const char *hostname,
974 const char *org_unit)
976 ADS_STATUS ret;
977 char *host_spn, *host_upn, *new_dn, *samAccountName, *controlstr;
978 char *ou_str;
979 TALLOC_CTX *ctx;
980 ADS_MODLIST mods;
981 const char *objectClass[] = {"top", "person", "organizationalPerson",
982 "user", "computer", NULL};
984 if (!(ctx = talloc_init_named("machine_account")))
985 return ADS_ERROR(LDAP_NO_MEMORY);
987 ret = ADS_ERROR(LDAP_NO_MEMORY);
989 if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", hostname)))
990 goto done;
991 if (!(host_upn = talloc_asprintf(ctx, "%s@%s", host_spn, ads->config.realm)))
992 goto done;
993 ou_str = ads_ou_string(org_unit);
994 new_dn = talloc_asprintf(ctx, "cn=%s,%s,%s", hostname, ou_str,
995 ads->config.bind_path);
996 free(ou_str);
997 if (!new_dn)
998 goto done;
1000 if (!(samAccountName = talloc_asprintf(ctx, "%s$", hostname)))
1001 goto done;
1002 if (!(controlstr = talloc_asprintf(ctx, "%u",
1003 UF_DONT_EXPIRE_PASSWD | UF_WORKSTATION_TRUST_ACCOUNT |
1004 UF_TRUSTED_FOR_DELEGATION | UF_USE_DES_KEY_ONLY)))
1005 goto done;
1007 if (!(mods = ads_init_mods(ctx)))
1008 goto done;
1010 ads_mod_str(ctx, &mods, "cn", hostname);
1011 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1012 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1013 ads_mod_str(ctx, &mods, "userPrincipalName", host_upn);
1014 ads_mod_str(ctx, &mods, "servicePrincipalName", host_spn);
1015 ads_mod_str(ctx, &mods, "dNSHostName", hostname);
1016 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1017 ads_mod_str(ctx, &mods, "operatingSystem", "Samba");
1018 ads_mod_str(ctx, &mods, "operatingSystemVersion", VERSION);
1020 ads_gen_add(ads, new_dn, mods);
1021 ret = ads_set_machine_sd(ads, hostname, new_dn);
1023 done:
1024 talloc_destroy(ctx);
1025 return ret;
1029 dump a binary result from ldap
1031 static void dump_binary(const char *field, struct berval **values)
1033 int i, j;
1034 for (i=0; values[i]; i++) {
1035 printf("%s: ", field);
1036 for (j=0; j<values[i]->bv_len; j++) {
1037 printf("%02X", (unsigned char)values[i]->bv_val[j]);
1039 printf("\n");
1044 dump a sid result from ldap
1046 static void dump_sid(const char *field, struct berval **values)
1048 int i;
1049 for (i=0; values[i]; i++) {
1050 DOM_SID sid;
1051 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1052 printf("%s: %s\n", field, sid_string_static(&sid));
1057 dump ntSecurityDescriptor
1059 static void dump_sd(const char *filed, struct berval **values)
1061 prs_struct ps;
1063 SEC_DESC *psd = 0;
1064 TALLOC_CTX *ctx = 0;
1066 if (!(ctx = talloc_init_named("sec_io_desc")))
1067 return;
1069 /* prepare data */
1070 prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
1071 prs_append_data(&ps, values[0]->bv_val, values[0]->bv_len);
1072 ps.data_offset = 0;
1074 /* parse secdesc */
1075 if (!sec_io_desc("sd", &psd, &ps, 1)) {
1076 prs_mem_free(&ps);
1077 talloc_destroy(ctx);
1078 return;
1080 if (psd) ads_disp_sd(psd);
1082 prs_mem_free(&ps);
1083 talloc_destroy(ctx);
1087 dump a string result from ldap
1089 static void dump_string(const char *field, char **values)
1091 int i;
1092 for (i=0; values[i]; i++) {
1093 printf("%s: %s\n", field, values[i]);
1098 dump a field from LDAP on stdout
1099 used for debugging
1102 static BOOL ads_dump_field(char *field, void **values, void *data_area)
1104 struct {
1105 char *name;
1106 BOOL string;
1107 void (*handler)(const char *, struct berval **);
1108 } handlers[] = {
1109 {"objectGUID", False, dump_binary},
1110 {"nTSecurityDescriptor", False, dump_sd},
1111 {"dnsRecord", False, dump_binary},
1112 {"objectSid", False, dump_sid},
1113 {NULL, True, NULL}
1115 int i;
1117 if (!field) { /* must be end of an entry */
1118 printf("\n");
1119 return False;
1122 for (i=0; handlers[i].name; i++) {
1123 if (StrCaseCmp(handlers[i].name, field) == 0) {
1124 if (!values) /* first time, indicate string or not */
1125 return handlers[i].string;
1126 handlers[i].handler(field, (struct berval **) values);
1127 break;
1130 if (!handlers[i].name) {
1131 if (!values) /* first time, indicate string conversion */
1132 return True;
1133 dump_string(field, (char **)values);
1135 return False;
1139 * Dump a result from LDAP on stdout
1140 * used for debugging
1141 * @param ads connection to ads server
1142 * @param res Results to dump
1145 void ads_dump(ADS_STRUCT *ads, void *res)
1147 ads_process_results(ads, res, ads_dump_field, NULL);
1151 * Walk through results, calling a function for each entry found.
1152 * The function receives a field name, a berval * array of values,
1153 * and a data area passed through from the start. The function is
1154 * called once with null for field and values at the end of each
1155 * entry.
1156 * @param ads connection to ads server
1157 * @param res Results to process
1158 * @param fn Function for processing each result
1159 * @param data_area user-defined area to pass to function
1161 void ads_process_results(ADS_STRUCT *ads, void *res,
1162 BOOL(*fn)(char *, void **, void *),
1163 void *data_area)
1165 void *msg;
1166 TALLOC_CTX *ctx;
1168 if (!(ctx = talloc_init()))
1169 return;
1171 for (msg = ads_first_entry(ads, res); msg;
1172 msg = ads_next_entry(ads, msg)) {
1173 char *utf8_field;
1174 BerElement *b;
1176 for (utf8_field=ldap_first_attribute(ads->ld,
1177 (LDAPMessage *)msg,&b);
1178 utf8_field;
1179 utf8_field=ldap_next_attribute(ads->ld,
1180 (LDAPMessage *)msg,b)) {
1181 struct berval **ber_vals;
1182 char **str_vals, **utf8_vals;
1183 char *field;
1184 BOOL string;
1186 pull_utf8_talloc(ctx, &field, utf8_field);
1187 string = fn(field, NULL, data_area);
1189 if (string) {
1190 utf8_vals = ldap_get_values(ads->ld,
1191 (LDAPMessage *)msg, field);
1192 str_vals = ads_pull_strvals(ctx,
1193 (const char **) utf8_vals);
1194 fn(field, (void **) str_vals, data_area);
1195 ldap_value_free(utf8_vals);
1196 } else {
1197 ber_vals = ldap_get_values_len(ads->ld,
1198 (LDAPMessage *)msg, field);
1199 fn(field, (void **) ber_vals, data_area);
1201 ldap_value_free_len(ber_vals);
1203 ldap_memfree(utf8_field);
1205 ber_free(b, 0);
1206 talloc_destroy_pool(ctx);
1207 fn(NULL, NULL, data_area); /* completed an entry */
1210 talloc_destroy(ctx);
1214 * count how many replies are in a LDAPMessage
1215 * @param ads connection to ads server
1216 * @param res Results to count
1217 * @return number of replies
1219 int ads_count_replies(ADS_STRUCT *ads, void *res)
1221 return ldap_count_entries(ads->ld, (LDAPMessage *)res);
1225 * Join a machine to a realm
1226 * Creates the machine account and sets the machine password
1227 * @param ads connection to ads server
1228 * @param hostname name of host to add
1229 * @param org_unit Organizational unit to place machine in
1230 * @return status of join
1232 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *hostname, const char *org_unit)
1234 ADS_STATUS status;
1235 LDAPMessage *res;
1236 char *host;
1238 /* hostname must be lowercase */
1239 host = strdup(hostname);
1240 strlower(host);
1242 status = ads_find_machine_acct(ads, (void **)&res, host);
1243 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1244 DEBUG(0, ("Host account for %s already exists - deleting old account\n", host));
1245 status = ads_leave_realm(ads, host);
1246 if (!ADS_ERR_OK(status)) {
1247 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
1248 host, ads->config.realm));
1249 return status;
1253 status = ads_add_machine_acct(ads, host, org_unit);
1254 if (!ADS_ERR_OK(status)) {
1255 DEBUG(0, ("ads_add_machine_acct: %s\n", ads_errstr(status)));
1256 return status;
1259 status = ads_find_machine_acct(ads, (void **)&res, host);
1260 if (!ADS_ERR_OK(status)) {
1261 DEBUG(0, ("Host account test failed\n"));
1262 return status;
1265 free(host);
1267 return status;
1271 * Delete a machine from the realm
1272 * @param ads connection to ads server
1273 * @param hostname Machine to remove
1274 * @return status of delete
1276 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
1278 ADS_STATUS status;
1279 void *res;
1280 char *hostnameDN, *host;
1281 int rc;
1283 /* hostname must be lowercase */
1284 host = strdup(hostname);
1285 strlower(host);
1287 status = ads_find_machine_acct(ads, &res, host);
1288 if (!ADS_ERR_OK(status)) {
1289 DEBUG(0, ("Host account for %s does not exist.\n", host));
1290 return status;
1293 hostnameDN = ads_get_dn(ads, (LDAPMessage *)res);
1294 rc = ldap_delete_s(ads->ld, hostnameDN);
1295 ads_memfree(ads, hostnameDN);
1296 if (rc != LDAP_SUCCESS) {
1297 return ADS_ERROR(rc);
1300 status = ads_find_machine_acct(ads, &res, host);
1301 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1302 DEBUG(0, ("Failed to remove host account.\n"));
1303 return status;
1306 free(host);
1308 return status;
1312 * add machine account to existing security descriptor
1313 * @param ads connection to ads server
1314 * @param hostname machine to add
1315 * @param dn DN of security descriptor
1316 * @return status
1318 ADS_STATUS ads_set_machine_sd(ADS_STRUCT *ads, const char *hostname, char *dn)
1320 const char *attrs[] = {"ntSecurityDescriptor", "objectSid", 0};
1321 char *exp = 0;
1322 size_t sd_size = 0;
1323 struct berval **bvals = 0;
1324 struct berval bval = {0, NULL};
1325 prs_struct ps;
1326 prs_struct ps_wire;
1328 LDAPMessage *res = 0;
1329 LDAPMessage *msg = 0;
1330 ADS_MODLIST mods = 0;
1332 NTSTATUS status;
1333 ADS_STATUS ret;
1334 DOM_SID sid;
1335 SEC_DESC *psd = 0;
1336 TALLOC_CTX *ctx = 0;
1338 if (!ads) return ADS_ERROR(LDAP_SERVER_DOWN);
1340 ret = ADS_ERROR(LDAP_SUCCESS);
1342 asprintf(&exp, "(samAccountName=%s$)", hostname);
1343 ret = ads_search(ads, (void *) &res, exp, attrs);
1345 if (!ADS_ERR_OK(ret)) return ret;
1347 msg = ads_first_entry(ads, res);
1348 bvals = ldap_get_values_len(ads->ld, msg, attrs[0]);
1349 ads_pull_sid(ads, msg, attrs[1], &sid);
1350 ads_msgfree(ads, res);
1351 #if 0
1352 file_save("/tmp/sec_desc.old", bvals[0]->bv_val, bvals[0]->bv_len);
1353 #endif
1354 if (!(ctx = talloc_init_named("sec_io_desc")))
1355 return ADS_ERROR(LDAP_NO_MEMORY);
1357 prs_init(&ps, bvals[0]->bv_len, ctx, UNMARSHALL);
1358 prs_append_data(&ps, bvals[0]->bv_val, bvals[0]->bv_len);
1359 ps.data_offset = 0;
1360 ldap_value_free_len(bvals);
1362 if (!sec_io_desc("sd", &psd, &ps, 1))
1363 goto ads_set_sd_error;
1365 status = sec_desc_add_sid(ctx, &psd, &sid, SEC_RIGHTS_FULL_CTRL, &sd_size);
1367 if (!NT_STATUS_IS_OK(status))
1368 goto ads_set_sd_error;
1370 prs_init(&ps_wire, sd_size, ctx, MARSHALL);
1371 if (!sec_io_desc("sd_wire", &psd, &ps_wire, 1))
1372 goto ads_set_sd_error;
1374 #if 0
1375 file_save("/tmp/sec_desc.new", ps_wire.data_p, sd_size);
1376 #endif
1377 if (!(mods = ads_init_mods(ctx))) return ADS_ERROR(LDAP_NO_MEMORY);
1379 bval.bv_len = sd_size;
1380 bval.bv_val = prs_data_p(&ps_wire);
1381 ads_mod_ber(ctx, &mods, attrs[0], &bval);
1382 ret = ads_gen_mod(ads, dn, mods);
1384 prs_mem_free(&ps);
1385 prs_mem_free(&ps_wire);
1386 talloc_destroy(ctx);
1387 return ret;
1389 ads_set_sd_error:
1390 prs_mem_free(&ps);
1391 prs_mem_free(&ps_wire);
1392 talloc_destroy(ctx);
1393 return ADS_ERROR(LDAP_NO_MEMORY);
1397 * Set the machine account password
1398 * @param ads connection to ads server
1399 * @param hostname machine whose password is being set
1400 * @param password new password
1401 * @return status of password change
1403 ADS_STATUS ads_set_machine_password(ADS_STRUCT *ads,
1404 const char *hostname,
1405 const char *password)
1407 ADS_STATUS status;
1408 char *host = strdup(hostname);
1409 char *principal;
1411 strlower(host);
1414 we need to use the '$' form of the name here, as otherwise the
1415 server might end up setting the password for a user instead
1417 asprintf(&principal, "%s$@%s", host, ads->auth.realm);
1419 status = krb5_set_password(ads->auth.kdc_server, principal, password, ads->auth.time_offset);
1421 free(host);
1422 free(principal);
1424 return status;
1428 * pull the first entry from a ADS result
1429 * @param ads connection to ads server
1430 * @param res Results of search
1431 * @return first entry from result
1433 void *ads_first_entry(ADS_STRUCT *ads, void *res)
1435 return (void *)ldap_first_entry(ads->ld, (LDAPMessage *)res);
1439 * pull the next entry from a ADS result
1440 * @param ads connection to ads server
1441 * @param res Results of search
1442 * @return next entry from result
1444 void *ads_next_entry(ADS_STRUCT *ads, void *res)
1446 return (void *)ldap_next_entry(ads->ld, (LDAPMessage *)res);
1450 * pull a single string from a ADS result
1451 * @param ads connection to ads server
1452 * @param mem_ctx TALLOC_CTX to use for allocating result string
1453 * @param msg Results of search
1454 * @param field Attribute to retrieve
1455 * @return Result string in talloc context
1457 char *ads_pull_string(ADS_STRUCT *ads,
1458 TALLOC_CTX *mem_ctx, void *msg, const char *field)
1460 char **values;
1461 char *ret = NULL;
1462 char *ux_string;
1463 int rc;
1465 values = ldap_get_values(ads->ld, msg, field);
1466 if (!values) return NULL;
1468 if (values[0]) {
1469 rc = pull_utf8_talloc(mem_ctx, &ux_string,
1470 values[0]);
1471 if (rc != -1)
1472 ret = ux_string;
1475 ldap_value_free(values);
1476 return ret;
1480 * pull an array of strings from a ADS result
1481 * @param ads connection to ads server
1482 * @param mem_ctx TALLOC_CTX to use for allocating result string
1483 * @param msg Results of search
1484 * @param field Attribute to retrieve
1485 * @return Result strings in talloc context
1487 char **ads_pull_strings(ADS_STRUCT *ads,
1488 TALLOC_CTX *mem_ctx, void *msg, const char *field)
1490 char **values;
1491 char **ret = NULL;
1492 int i, n;
1494 values = ldap_get_values(ads->ld, msg, field);
1495 if (!values) return NULL;
1497 for (i=0;values[i];i++) /* noop */ ;
1498 n = i;
1500 ret = talloc(mem_ctx, sizeof(char *) * (n+1));
1502 for (i=0;i<n;i++) {
1503 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
1504 return NULL;
1507 ret[i] = NULL;
1509 ldap_value_free(values);
1510 return ret;
1515 * pull a single uint32 from a ADS result
1516 * @param ads connection to ads server
1517 * @param msg Results of search
1518 * @param field Attribute to retrieve
1519 * @param v Pointer to int to store result
1520 * @return boolean inidicating success
1522 BOOL ads_pull_uint32(ADS_STRUCT *ads,
1523 void *msg, const char *field, uint32 *v)
1525 char **values;
1527 values = ldap_get_values(ads->ld, msg, field);
1528 if (!values) return False;
1529 if (!values[0]) {
1530 ldap_value_free(values);
1531 return False;
1534 *v = atoi(values[0]);
1535 ldap_value_free(values);
1536 return True;
1540 * pull a single DOM_SID from a ADS result
1541 * @param ads connection to ads server
1542 * @param msg Results of search
1543 * @param field Attribute to retrieve
1544 * @param sid Pointer to sid to store result
1545 * @return boolean inidicating success
1547 BOOL ads_pull_sid(ADS_STRUCT *ads,
1548 void *msg, const char *field, DOM_SID *sid)
1550 struct berval **values;
1551 BOOL ret = False;
1553 values = ldap_get_values_len(ads->ld, msg, field);
1555 if (!values) return False;
1557 if (values[0]) {
1558 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
1561 ldap_value_free_len(values);
1562 return ret;
1566 * pull an array of DOM_SIDs from a ADS result
1567 * @param ads connection to ads server
1568 * @param mem_ctx TALLOC_CTX for allocating sid array
1569 * @param msg Results of search
1570 * @param field Attribute to retrieve
1571 * @param sids pointer to sid array to allocate
1572 * @return the count of SIDs pulled
1574 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
1575 void *msg, const char *field, DOM_SID **sids)
1577 struct berval **values;
1578 BOOL ret;
1579 int count, i;
1581 values = ldap_get_values_len(ads->ld, msg, field);
1583 if (!values) return 0;
1585 for (i=0; values[i]; i++) /* nop */ ;
1587 (*sids) = talloc(mem_ctx, sizeof(DOM_SID) * i);
1589 count = 0;
1590 for (i=0; values[i]; i++) {
1591 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
1592 if (ret) count++;
1595 ldap_value_free_len(values);
1596 return count;
1601 * find the update serial number - this is the core of the ldap cache
1602 * @param ads connection to ads server
1603 * @param ads connection to ADS server
1604 * @param usn Pointer to retrieved update serial number
1605 * @return status of search
1607 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
1609 const char *attrs[] = {"highestCommittedUSN", NULL};
1610 ADS_STATUS status;
1611 void *res;
1613 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
1614 if (!ADS_ERR_OK(status)) return status;
1616 if (ads_count_replies(ads, res) != 1) {
1617 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1620 ads_pull_uint32(ads, res, "highestCommittedUSN", usn);
1621 ads_msgfree(ads, res);
1622 return ADS_SUCCESS;
1625 /* parse a ADS timestring - typical string is
1626 '20020917091222.0Z0' which means 09:12.22 17th September
1627 2002, timezone 0 */
1628 static time_t ads_parse_time(const char *str)
1630 struct tm tm;
1632 ZERO_STRUCT(tm);
1634 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
1635 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
1636 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
1637 return 0;
1639 tm.tm_year -= 1900;
1640 tm.tm_mon -= 1;
1642 return timegm(&tm);
1647 * Find the servers name and realm - this can be done before authentication
1648 * The ldapServiceName field on w2k looks like this:
1649 * vnet3.home.samba.org:win2000-vnet3$@VNET3.HOME.SAMBA.ORG
1650 * @param ads connection to ads server
1651 * @return status of search
1653 ADS_STATUS ads_server_info(ADS_STRUCT *ads)
1655 const char *attrs[] = {"ldapServiceName", "currentTime", NULL};
1656 ADS_STATUS status;
1657 void *res;
1658 char *value;
1659 char *p;
1660 char *timestr;
1661 TALLOC_CTX *ctx;
1663 if (!(ctx = talloc_init())) {
1664 return ADS_ERROR(LDAP_NO_MEMORY);
1667 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
1668 if (!ADS_ERR_OK(status)) return status;
1670 value = ads_pull_string(ads, ctx, res, "ldapServiceName");
1671 if (!value) {
1672 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1675 timestr = ads_pull_string(ads, ctx, res, "currentTime");
1676 if (!timestr) {
1677 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1680 ldap_msgfree(res);
1682 p = strchr(value, ':');
1683 if (!p) {
1684 talloc_destroy(ctx);
1685 DEBUG(1, ("ads_server_info: returned ldap server name did not contain a ':' so was deemed invalid\n"));
1686 return ADS_ERROR(LDAP_DECODING_ERROR);
1689 SAFE_FREE(ads->config.ldap_server_name);
1691 ads->config.ldap_server_name = strdup(p+1);
1692 p = strchr(ads->config.ldap_server_name, '$');
1693 if (!p || p[1] != '@') {
1694 talloc_destroy(ctx);
1695 SAFE_FREE(ads->config.ldap_server_name);
1696 DEBUG(1, ("ads_server_info: returned ldap server name did not contain '$@' so was deemed invalid\n"));
1697 return ADS_ERROR(LDAP_DECODING_ERROR);
1700 *p = 0;
1702 SAFE_FREE(ads->config.realm);
1703 SAFE_FREE(ads->config.bind_path);
1705 ads->config.realm = strdup(p+2);
1706 ads->config.bind_path = ads_build_dn(ads->config.realm);
1708 DEBUG(3,("got ldap server name %s@%s\n",
1709 ads->config.ldap_server_name, ads->config.realm));
1711 ads->config.current_time = ads_parse_time(timestr);
1713 if (ads->config.current_time != 0) {
1714 ads->auth.time_offset = ads->config.current_time - time(NULL);
1715 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
1718 talloc_destroy(ctx);
1720 return ADS_SUCCESS;
1725 * find the list of trusted domains
1726 * @param ads connection to ads server
1727 * @param mem_ctx TALLOC_CTX for allocating results
1728 * @param num_trusts pointer to number of trusts
1729 * @param names pointer to trusted domain name list
1730 * @param sids pointer to list of sids of trusted domains
1731 * @return the count of SIDs pulled
1733 ADS_STATUS ads_trusted_domains(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
1734 int *num_trusts,
1735 char ***names,
1736 char ***alt_names,
1737 DOM_SID **sids)
1739 const char *attrs[] = {"name", "flatname", "securityIdentifier",
1740 "trustDirection", NULL};
1741 ADS_STATUS status;
1742 void *res, *msg;
1743 int count, i;
1745 *num_trusts = 0;
1747 status = ads_search(ads, &res, "(objectcategory=trustedDomain)", attrs);
1748 if (!ADS_ERR_OK(status)) return status;
1750 count = ads_count_replies(ads, res);
1751 if (count == 0) {
1752 ads_msgfree(ads, res);
1753 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1756 (*names) = talloc(mem_ctx, sizeof(char *) * count);
1757 (*alt_names) = talloc(mem_ctx, sizeof(char *) * count);
1758 (*sids) = talloc(mem_ctx, sizeof(DOM_SID) * count);
1759 if (! *names || ! *sids) return ADS_ERROR(LDAP_NO_MEMORY);
1761 for (i=0, msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
1762 uint32 direction;
1764 /* direction is a 2 bit bitfield, 1 means they trust us
1765 but we don't trust them, so we should not list them
1766 as users from that domain can't login */
1767 if (ads_pull_uint32(ads, msg, "trustDirection", &direction) &&
1768 direction == 1) {
1769 continue;
1772 (*names)[i] = ads_pull_string(ads, mem_ctx, msg, "name");
1773 (*alt_names)[i] = ads_pull_string(ads, mem_ctx, msg, "flatname");
1775 if ((*alt_names)[i] && (*alt_names)[i][0]) {
1776 /* we prefer the flatname as the primary name
1777 for consistency with RPC */
1778 char *name = (*alt_names)[i];
1779 (*alt_names)[i] = (*names)[i];
1780 (*names)[i] = name;
1782 if (ads_pull_sid(ads, msg, "securityIdentifier", &(*sids)[i])) {
1783 i++;
1787 ads_msgfree(ads, res);
1789 *num_trusts = i;
1791 return ADS_SUCCESS;
1795 * find the domain sid for our domain
1796 * @param ads connection to ads server
1797 * @param sid Pointer to domain sid
1798 * @return status of search
1800 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
1802 const char *attrs[] = {"objectSid", NULL};
1803 void *res;
1804 ADS_STATUS rc;
1806 rc = ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
1807 attrs, &res);
1808 if (!ADS_ERR_OK(rc)) return rc;
1809 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
1810 return ADS_ERROR_SYSTEM(ENOENT);
1812 ads_msgfree(ads, res);
1814 return ADS_SUCCESS;
1817 /* this is rather complex - we need to find the allternate (netbios) name
1818 for the domain, but there isn't a simple query to do this. Instead
1819 we look for the principle names on the DCs account and find one that has
1820 the right form, then extract the netbios name of the domain from that
1822 ADS_STATUS ads_workgroup_name(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char **workgroup)
1824 char *exp;
1825 ADS_STATUS rc;
1826 char **principles;
1827 char *prefix;
1828 int prefix_length;
1829 int i;
1830 void *res;
1831 const char *attrs[] = {"servicePrincipalName", NULL};
1833 (*workgroup) = NULL;
1835 asprintf(&exp, "(&(objectclass=computer)(dnshostname=%s.%s))",
1836 ads->config.ldap_server_name, ads->config.realm);
1837 rc = ads_search(ads, &res, exp, attrs);
1838 free(exp);
1840 if (!ADS_ERR_OK(rc)) {
1841 return rc;
1844 principles = ads_pull_strings(ads, mem_ctx, res, "servicePrincipalName");
1846 ads_msgfree(ads, res);
1848 if (!principles) {
1849 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1852 asprintf(&prefix, "HOST/%s.%s/",
1853 ads->config.ldap_server_name,
1854 ads->config.realm);
1856 prefix_length = strlen(prefix);
1858 for (i=0;principles[i]; i++) {
1859 if (strncasecmp(principles[i], prefix, prefix_length) == 0 &&
1860 strcasecmp(ads->config.realm, principles[i]+prefix_length) != 0 &&
1861 !strchr(principles[i]+prefix_length, '.')) {
1862 /* found an alternate (short) name for the domain. */
1863 DEBUG(3,("Found alternate name '%s' for realm '%s'\n",
1864 principles[i]+prefix_length,
1865 ads->config.realm));
1866 (*workgroup) = talloc_strdup(mem_ctx, principles[i]+prefix_length);
1867 break;
1870 free(prefix);
1872 if (!*workgroup) {
1873 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1876 return ADS_SUCCESS;
1879 #endif