we need to call ads_first_entry() before using a ldap result,
[Samba/gebeck_regimport.git] / source / libads / ldap.c
blob0a59c4eb8f4800198acd38828bc2f71f6f736bda
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 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;
71 try a connection to a given ldap server, based on URL, returning True if successful
73 static BOOL ads_try_connect_uri(ADS_STRUCT *ads)
75 #if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
76 DEBUG(5,("ads_try_connect: trying ldap server at URI '%s'\n",
77 ads->server.ldap_uri));
80 if (ldap_initialize((LDAP**)&(ads->ld), ads->server.ldap_uri) == LDAP_SUCCESS) {
81 return True;
83 DEBUG(0, ("ldap_initialize: %s\n", strerror(errno)));
85 #else
87 DEBUG(1, ("no URL support in LDAP libs!\n"));
88 #endif
90 return False;
93 /* used by the IP comparison function */
94 struct ldap_ip {
95 struct in_addr ip;
96 unsigned port;
99 /* compare 2 ldap IPs by nearness to our interfaces - used in qsort */
100 static int ldap_ip_compare(struct ldap_ip *ip1, struct ldap_ip *ip2)
102 return ip_compare(&ip1->ip, &ip2->ip);
105 /* try connecting to a ldap server via DNS */
106 static BOOL ads_try_dns(ADS_STRUCT *ads)
108 const char *c_realm;
109 const char *ptr;
110 char *realm;
111 char *list = NULL;
112 pstring tok;
113 struct ldap_ip *ip_list;
114 int count, i=0;
116 c_realm = ads->server.realm;
117 if (!c_realm || !*c_realm) {
118 c_realm = lp_realm();
120 if (!c_realm || !*c_realm) {
121 c_realm = ads->server.workgroup;
123 if (!c_realm || !*c_realm) {
124 c_realm = lp_workgroup();
126 if (!c_realm) {
127 return False;
129 realm = smb_xstrdup(c_realm);
131 DEBUG(6,("ads_try_dns: looking for realm '%s'\n", realm));
132 if (ldap_domain2hostlist(realm, &list) != LDAP_SUCCESS) {
133 SAFE_FREE(realm);
134 return False;
137 DEBUG(6,("ads_try_dns: ldap realm '%s' host list '%s'\n", realm, list));
138 SAFE_FREE(realm);
140 count = count_chars(list, ' ') + 1;
141 ip_list = malloc(count * sizeof(struct ldap_ip));
142 if (!ip_list) {
143 return False;
146 ptr = list;
147 while (next_token(&ptr, tok, " ", sizeof(tok))) {
148 unsigned port = LDAP_PORT;
149 char *p = strchr(tok, ':');
150 if (p) {
151 *p = 0;
152 port = atoi(p+1);
154 ip_list[i].ip = *interpret_addr2(tok);
155 ip_list[i].port = port;
156 if (!is_zero_ip(ip_list[i].ip)) {
157 i++;
160 free(list);
162 count = i;
164 /* we sort the list of addresses by closeness to our interfaces. This
165 tries to prevent us using a DC on the other side of the country */
166 if (count > 1) {
167 qsort(ip_list, count, sizeof(struct ldap_ip),
168 QSORT_CAST ldap_ip_compare);
171 for (i=0;i<count;i++) {
172 if (ads_try_connect(ads, inet_ntoa(ip_list[i].ip), ip_list[i].port)) {
173 free(ip_list);
174 return True;
178 SAFE_FREE(ip_list);
179 return False;
182 /* try connecting to a ldap server via netbios */
183 static BOOL ads_try_netbios(ADS_STRUCT *ads)
185 struct in_addr *ip_list, pdc_ip;
186 int count;
187 int i;
188 const char *workgroup = ads->server.workgroup;
189 BOOL list_ordered;
191 if (!workgroup) {
192 workgroup = lp_workgroup();
195 DEBUG(6,("ads_try_netbios: looking for workgroup '%s'\n", workgroup));
197 /* try the PDC first */
198 if (get_pdc_ip(workgroup, &pdc_ip)) {
199 DEBUG(6,("ads_try_netbios: trying server '%s'\n",
200 inet_ntoa(pdc_ip)));
201 if (ads_try_connect(ads, inet_ntoa(pdc_ip), LDAP_PORT))
202 return True;
205 /* now any DC, including backups */
206 if (get_dc_list(workgroup, &ip_list, &count, &list_ordered)) {
207 for (i=0;i<count;i++) {
208 DEBUG(6,("ads_try_netbios: trying server '%s'\n",
209 inet_ntoa(ip_list[i])));
210 if (ads_try_connect(ads, inet_ntoa(ip_list[i]), LDAP_PORT)) {
211 free(ip_list);
212 return True;
215 free(ip_list);
218 return False;
222 * Connect to the LDAP server
223 * @param ads Pointer to an existing ADS_STRUCT
224 * @return status of connection
226 ADS_STATUS ads_connect(ADS_STRUCT *ads)
228 int version = LDAP_VERSION3;
229 ADS_STATUS status;
231 ads->last_attempt = time(NULL);
232 ads->ld = NULL;
234 /* try with a URL based server */
236 if (ads->server.ldap_uri &&
237 ads_try_connect_uri(ads)) {
238 goto got_connection;
241 /* try with a user specified server */
242 if (ads->server.ldap_server &&
243 ads_try_connect(ads, ads->server.ldap_server, LDAP_PORT)) {
244 goto got_connection;
247 /* try with a smb.conf ads server setting if we are connecting
248 to the primary workgroup or realm */
249 if (!ads->server.foreign &&
250 ads_try_connect(ads, lp_ads_server(), LDAP_PORT)) {
251 goto got_connection;
254 /* try via DNS */
255 if (ads_try_dns(ads)) {
256 goto got_connection;
259 /* try via netbios lookups */
260 if (!lp_disable_netbios() && ads_try_netbios(ads)) {
261 goto got_connection;
264 return ADS_ERROR_SYSTEM(errno?errno:ENOENT);
266 got_connection:
267 DEBUG(3,("Connected to LDAP server %s\n", inet_ntoa(ads->ldap_ip)));
269 status = ads_server_info(ads);
270 if (!ADS_ERR_OK(status)) {
271 DEBUG(1,("Failed to get ldap server info\n"));
272 return status;
275 ldap_set_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
277 if (!ads->auth.user_name) {
278 /* by default use the machine account */
279 fstring myname;
280 fstrcpy(myname, global_myname());
281 strlower(myname);
282 asprintf(&ads->auth.user_name, "HOST/%s", myname);
285 if (!ads->auth.realm) {
286 ads->auth.realm = strdup(ads->config.realm);
289 if (!ads->auth.kdc_server) {
290 ads->auth.kdc_server = strdup(inet_ntoa(ads->ldap_ip));
293 #if KRB5_DNS_HACK
294 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
295 to MIT kerberos to work (tridge) */
297 char *env;
298 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
299 setenv(env, ads->auth.kdc_server, 1);
300 free(env);
302 #endif
304 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
305 return ADS_SUCCESS;
308 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
309 return ADS_ERROR(ldap_simple_bind_s( ads->ld, NULL, NULL));
312 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
313 return ADS_ERROR(ldap_simple_bind_s( ads->ld, ads->auth.user_name, ads->auth.password));
316 return ads_sasl_bind(ads);
320 Duplicate a struct berval into talloc'ed memory
322 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
324 struct berval *value;
326 if (!in_val) return NULL;
328 value = talloc_zero(ctx, sizeof(struct berval));
329 if (value == NULL)
330 return NULL;
331 if (in_val->bv_len == 0) return value;
333 value->bv_len = in_val->bv_len;
334 value->bv_val = talloc_memdup(ctx, in_val->bv_val, in_val->bv_len);
335 return value;
339 Make a values list out of an array of (struct berval *)
341 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
342 const struct berval **in_vals)
344 struct berval **values;
345 int i;
347 if (!in_vals) return NULL;
348 for (i=0; in_vals[i]; i++); /* count values */
349 values = (struct berval **) talloc_zero(ctx,
350 (i+1)*sizeof(struct berval *));
351 if (!values) return NULL;
353 for (i=0; in_vals[i]; i++) {
354 values[i] = dup_berval(ctx, in_vals[i]);
356 return values;
360 UTF8-encode a values list out of an array of (char *)
362 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
364 char **values;
365 int i;
367 if (!in_vals) return NULL;
368 for (i=0; in_vals[i]; i++); /* count values */
369 values = (char ** ) talloc_zero(ctx, (i+1)*sizeof(char *));
370 if (!values) return NULL;
372 for (i=0; in_vals[i]; i++) {
373 push_utf8_talloc(ctx, &values[i], in_vals[i]);
375 return values;
379 Pull a (char *) array out of a UTF8-encoded values list
381 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
383 char **values;
384 int i;
386 if (!in_vals) return NULL;
387 for (i=0; in_vals[i]; i++); /* count values */
388 values = (char **) talloc_zero(ctx, (i+1)*sizeof(char *));
389 if (!values) return NULL;
391 for (i=0; in_vals[i]; i++) {
392 pull_utf8_talloc(ctx, &values[i], in_vals[i]);
394 return values;
398 * Do a search with paged results. cookie must be null on the first
399 * call, and then returned on each subsequent call. It will be null
400 * again when the entire search is complete
401 * @param ads connection to ads server
402 * @param bind_path Base dn for the search
403 * @param scope Scope of search (LDAP_BASE | LDAP_ONE | LDAP_SUBTREE)
404 * @param expr Search expression - specified in local charset
405 * @param attrs Attributes to retrieve - specified in utf8 or ascii
406 * @param res ** which will contain results - free res* with ads_msgfree()
407 * @param count Number of entries retrieved on this page
408 * @param cookie The paged results cookie to be returned on subsequent calls
409 * @return status of search
411 ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
412 int scope, const char *expr,
413 const char **attrs, void **res,
414 int *count, void **cookie)
416 int rc, i, version;
417 char *utf8_expr, *utf8_path, **search_attrs;
418 LDAPControl PagedResults, NoReferrals, *controls[3], **rcontrols;
419 BerElement *cookie_be = NULL;
420 struct berval *cookie_bv= NULL;
421 TALLOC_CTX *ctx;
423 *res = NULL;
425 if (!(ctx = talloc_init("ads_do_paged_search")))
426 return ADS_ERROR(LDAP_NO_MEMORY);
428 /* 0 means the conversion worked but the result was empty
429 so we only fail if it's -1. In any case, it always
430 at least nulls out the dest */
431 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
432 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
433 rc = LDAP_NO_MEMORY;
434 goto done;
437 if (!attrs || !(*attrs))
438 search_attrs = NULL;
439 else {
440 /* This would be the utf8-encoded version...*/
441 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
442 if (!(str_list_copy(&search_attrs, attrs))) {
443 rc = LDAP_NO_MEMORY;
444 goto done;
449 /* Paged results only available on ldap v3 or later */
450 ldap_get_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
451 if (version < LDAP_VERSION3) {
452 rc = LDAP_NOT_SUPPORTED;
453 goto done;
456 cookie_be = ber_alloc_t(LBER_USE_DER);
457 if (cookie && *cookie) {
458 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
459 ber_bvfree(*cookie); /* don't need it from last time */
460 *cookie = NULL;
461 } else {
462 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
464 ber_flatten(cookie_be, &cookie_bv);
465 PagedResults.ldctl_oid = ADS_PAGE_CTL_OID;
466 PagedResults.ldctl_iscritical = (char) 1;
467 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
468 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
470 NoReferrals.ldctl_oid = ADS_NO_REFERRALS_OID;
471 NoReferrals.ldctl_iscritical = (char) 0;
472 NoReferrals.ldctl_value.bv_len = 0;
473 NoReferrals.ldctl_value.bv_val = "";
476 controls[0] = &NoReferrals;
477 controls[1] = &PagedResults;
478 controls[2] = NULL;
480 *res = NULL;
482 /* we need to disable referrals as the openldap libs don't
483 handle them and paged results at the same time. Using them
484 together results in the result record containing the server
485 page control being removed from the result list (tridge/jmcd)
487 leaving this in despite the control that says don't generate
488 referrals, in case the server doesn't support it (jmcd)
490 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
492 rc = ldap_search_ext_s(ads->ld, utf8_path, scope, utf8_expr,
493 search_attrs, 0, controls,
494 NULL, NULL, LDAP_NO_LIMIT, (LDAPMessage **)res);
496 ber_free(cookie_be, 1);
497 ber_bvfree(cookie_bv);
499 if (rc) {
500 DEBUG(3,("ldap_search_ext_s(%s) -> %s\n", expr, ldap_err2string(rc)));
501 goto done;
504 rc = ldap_parse_result(ads->ld, *res, NULL, NULL, NULL,
505 NULL, &rcontrols, 0);
507 if (!rcontrols) {
508 goto done;
511 for (i=0; rcontrols[i]; i++) {
512 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
513 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
514 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
515 &cookie_bv);
516 /* the berval is the cookie, but must be freed when
517 it is all done */
518 if (cookie_bv->bv_len) /* still more to do */
519 *cookie=ber_bvdup(cookie_bv);
520 else
521 *cookie=NULL;
522 ber_bvfree(cookie_bv);
523 ber_free(cookie_be, 1);
524 break;
527 ldap_controls_free(rcontrols);
529 done:
530 talloc_destroy(ctx);
531 /* if/when we decide to utf8-encode attrs, take out this next line */
532 str_list_free(&search_attrs);
534 return ADS_ERROR(rc);
539 * Get all results for a search. This uses ads_do_paged_search() to return
540 * all entries in a large search.
541 * @param ads connection to ads server
542 * @param bind_path Base dn for the search
543 * @param scope Scope of search (LDAP_BASE | LDAP_ONE | LDAP_SUBTREE)
544 * @param expr Search expression
545 * @param attrs Attributes to retrieve
546 * @param res ** which will contain results - free res* with ads_msgfree()
547 * @return status of search
549 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
550 int scope, const char *expr,
551 const char **attrs, void **res)
553 void *cookie = NULL;
554 int count = 0;
555 ADS_STATUS status;
557 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, res,
558 &count, &cookie);
560 if (!ADS_ERR_OK(status)) return status;
562 while (cookie) {
563 void *res2 = NULL;
564 ADS_STATUS status2;
565 LDAPMessage *msg, *next;
567 status2 = ads_do_paged_search(ads, bind_path, scope, expr,
568 attrs, &res2, &count, &cookie);
570 if (!ADS_ERR_OK(status2)) break;
572 /* this relies on the way that ldap_add_result_entry() works internally. I hope
573 that this works on all ldap libs, but I have only tested with openldap */
574 for (msg = ads_first_entry(ads, res2); msg; msg = next) {
575 next = ads_next_entry(ads, msg);
576 ldap_add_result_entry((LDAPMessage **)res, msg);
578 /* note that we do not free res2, as the memory is now
579 part of the main returned list */
582 return status;
586 * Run a function on all results for a search. Uses ads_do_paged_search() and
587 * runs the function as each page is returned, using ads_process_results()
588 * @param ads connection to ads server
589 * @param bind_path Base dn for the search
590 * @param scope Scope of search (LDAP_BASE | LDAP_ONE | LDAP_SUBTREE)
591 * @param expr Search expression - specified in local charset
592 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
593 * @param fn Function which takes attr name, values list, and data_area
594 * @param data_area Pointer which is passed to function on each call
595 * @return status of search
597 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
598 int scope, const char *expr, const char **attrs,
599 BOOL(*fn)(char *, void **, void *),
600 void *data_area)
602 void *cookie = NULL;
603 int count = 0;
604 ADS_STATUS status;
605 void *res;
607 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
608 &count, &cookie);
610 if (!ADS_ERR_OK(status)) return status;
612 ads_process_results(ads, res, fn, data_area);
613 ads_msgfree(ads, res);
615 while (cookie) {
616 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
617 &res, &count, &cookie);
619 if (!ADS_ERR_OK(status)) break;
621 ads_process_results(ads, res, fn, data_area);
622 ads_msgfree(ads, res);
625 return status;
629 * Do a search with a timeout.
630 * @param ads connection to ads server
631 * @param bind_path Base dn for the search
632 * @param scope Scope of search (LDAP_BASE | LDAP_ONE | LDAP_SUBTREE)
633 * @param expr Search expression
634 * @param attrs Attributes to retrieve
635 * @param res ** which will contain results - free res* with ads_msgfree()
636 * @return status of search
638 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
639 const char *expr,
640 const char **attrs, void **res)
642 struct timeval timeout;
643 int rc;
644 char *utf8_expr, *utf8_path, **search_attrs = NULL;
645 TALLOC_CTX *ctx;
647 if (!(ctx = talloc_init("ads_do_search"))) {
648 DEBUG(1,("ads_do_search: talloc_init() failed!"));
649 return ADS_ERROR(LDAP_NO_MEMORY);
652 /* 0 means the conversion worked but the result was empty
653 so we only fail if it's negative. In any case, it always
654 at least nulls out the dest */
655 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
656 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
657 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
658 rc = LDAP_NO_MEMORY;
659 goto done;
662 if (!attrs || !(*attrs))
663 search_attrs = NULL;
664 else {
665 /* This would be the utf8-encoded version...*/
666 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
667 if (!(str_list_copy(&search_attrs, attrs)))
669 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
670 rc = LDAP_NO_MEMORY;
671 goto done;
675 timeout.tv_sec = ADS_SEARCH_TIMEOUT;
676 timeout.tv_usec = 0;
677 *res = NULL;
679 /* see the note in ads_do_paged_search - we *must* disable referrals */
680 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
682 rc = ldap_search_ext_s(ads->ld, utf8_path, scope, utf8_expr,
683 search_attrs, 0, NULL, NULL,
684 &timeout, LDAP_NO_LIMIT, (LDAPMessage **)res);
686 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
687 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
688 rc = 0;
691 done:
692 talloc_destroy(ctx);
693 /* if/when we decide to utf8-encode attrs, take out this next line */
694 str_list_free(&search_attrs);
695 return ADS_ERROR(rc);
698 * Do a general ADS search
699 * @param ads connection to ads server
700 * @param res ** which will contain results - free res* with ads_msgfree()
701 * @param expr Search expression
702 * @param attrs Attributes to retrieve
703 * @return status of search
705 ADS_STATUS ads_search(ADS_STRUCT *ads, void **res,
706 const char *expr,
707 const char **attrs)
709 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
710 expr, attrs, res);
714 * Do a search on a specific DistinguishedName
715 * @param ads connection to ads server
716 * @param res ** which will contain results - free res* with ads_msgfree()
717 * @param dn DistinguishName to search
718 * @param attrs Attributes to retrieve
719 * @return status of search
721 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, void **res,
722 const char *dn,
723 const char **attrs)
725 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)", attrs, res);
729 * Free up memory from a ads_search
730 * @param ads connection to ads server
731 * @param msg Search results to free
733 void ads_msgfree(ADS_STRUCT *ads, void *msg)
735 if (!msg) return;
736 ldap_msgfree(msg);
740 * Free up memory from various ads requests
741 * @param ads connection to ads server
742 * @param mem Area to free
744 void ads_memfree(ADS_STRUCT *ads, void *mem)
746 SAFE_FREE(mem);
750 * Get a dn from search results
751 * @param ads connection to ads server
752 * @param msg Search result
753 * @return dn string
755 char *ads_get_dn(ADS_STRUCT *ads, void *msg)
757 char *utf8_dn, *unix_dn;
759 utf8_dn = ldap_get_dn(ads->ld, msg);
761 pull_utf8_allocate((void **) &unix_dn, utf8_dn);
762 ldap_memfree(utf8_dn);
763 return unix_dn;
767 * Find a machine account given a hostname
768 * @param ads connection to ads server
769 * @param res ** which will contain results - free res* with ads_msgfree()
770 * @param host Hostname to search for
771 * @return status of search
773 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, void **res, const char *host)
775 ADS_STATUS status;
776 char *expr;
777 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
779 /* the easiest way to find a machine account anywhere in the tree
780 is to look for hostname$ */
781 if (asprintf(&expr, "(samAccountName=%s$)", host) == -1) {
782 DEBUG(1, ("asprintf failed!\n"));
783 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
786 status = ads_search(ads, res, expr, attrs);
787 free(expr);
788 return status;
792 * Initialize a list of mods to be used in a modify request
793 * @param ctx An initialized TALLOC_CTX
794 * @return allocated ADS_MODLIST
796 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
798 #define ADS_MODLIST_ALLOC_SIZE 10
799 LDAPMod **mods;
801 if ((mods = (LDAPMod **) talloc_zero(ctx, sizeof(LDAPMod *) *
802 (ADS_MODLIST_ALLOC_SIZE + 1))))
803 /* -1 is safety to make sure we don't go over the end.
804 need to reset it to NULL before doing ldap modify */
805 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
807 return mods;
812 add an attribute to the list, with values list already constructed
814 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
815 int mod_op, const char *name,
816 const void **invals)
818 int curmod;
819 LDAPMod **modlist = (LDAPMod **) *mods;
820 struct berval **ber_values = NULL;
821 char **char_values = NULL;
823 if (!invals) {
824 mod_op = LDAP_MOD_DELETE;
825 } else {
826 if (mod_op & LDAP_MOD_BVALUES)
827 ber_values = ads_dup_values(ctx,
828 (const struct berval **)invals);
829 else
830 char_values = ads_push_strvals(ctx,
831 (const char **) invals);
834 /* find the first empty slot */
835 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
836 curmod++);
837 if (modlist[curmod] == (LDAPMod *) -1) {
838 if (!(modlist = talloc_realloc(ctx, modlist,
839 (curmod+ADS_MODLIST_ALLOC_SIZE+1)*sizeof(LDAPMod *))))
840 return ADS_ERROR(LDAP_NO_MEMORY);
841 memset(&modlist[curmod], 0,
842 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
843 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
844 *mods = modlist;
847 if (!(modlist[curmod] = talloc_zero(ctx, sizeof(LDAPMod))))
848 return ADS_ERROR(LDAP_NO_MEMORY);
849 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
850 if (mod_op & LDAP_MOD_BVALUES) {
851 modlist[curmod]->mod_bvalues = ber_values;
852 } else if (mod_op & LDAP_MOD_DELETE) {
853 modlist[curmod]->mod_values = NULL;
854 } else {
855 modlist[curmod]->mod_values = char_values;
858 modlist[curmod]->mod_op = mod_op;
859 return ADS_ERROR(LDAP_SUCCESS);
863 * Add a single string value to a mod list
864 * @param ctx An initialized TALLOC_CTX
865 * @param mods An initialized ADS_MODLIST
866 * @param name The attribute name to add
867 * @param val The value to add - NULL means DELETE
868 * @return ADS STATUS indicating success of add
870 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
871 const char *name, const char *val)
873 const char *values[2];
875 values[0] = val;
876 values[1] = NULL;
878 if (!val)
879 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
880 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name,
881 (const void **) values);
885 * Add an array of string values to a mod list
886 * @param ctx An initialized TALLOC_CTX
887 * @param mods An initialized ADS_MODLIST
888 * @param name The attribute name to add
889 * @param vals The array of string values to add - NULL means DELETE
890 * @return ADS STATUS indicating success of add
892 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
893 const char *name, const char **vals)
895 if (!vals)
896 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
897 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
898 name, (const void **) vals);
902 * Add a single ber-encoded value to a mod list
903 * @param ctx An initialized TALLOC_CTX
904 * @param mods An initialized ADS_MODLIST
905 * @param name The attribute name to add
906 * @param val The value to add - NULL means DELETE
907 * @return ADS STATUS indicating success of add
909 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
910 const char *name, const struct berval *val)
912 const struct berval *values[2];
914 values[0] = val;
915 values[1] = NULL;
916 if (!val)
917 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
918 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
919 name, (const void **) values);
923 * Perform an ldap modify
924 * @param ads connection to ads server
925 * @param mod_dn DistinguishedName to modify
926 * @param mods list of modifications to perform
927 * @return status of modify
929 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
931 int ret,i;
932 char *utf8_dn = NULL;
934 this control is needed to modify that contains a currently
935 non-existent attribute (but allowable for the object) to run
937 LDAPControl PermitModify = {
938 ADS_PERMIT_MODIFY_OID,
939 {0, NULL},
940 (char) 1};
941 LDAPControl *controls[2];
943 controls[0] = &PermitModify;
944 controls[1] = NULL;
946 if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) {
947 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
950 /* find the end of the list, marked by NULL or -1 */
951 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
952 /* make sure the end of the list is NULL */
953 mods[i] = NULL;
954 ret = ldap_modify_ext_s(ads->ld, utf8_dn,
955 (LDAPMod **) mods, controls, NULL);
956 SAFE_FREE(utf8_dn);
957 return ADS_ERROR(ret);
961 * Perform an ldap add
962 * @param ads connection to ads server
963 * @param new_dn DistinguishedName to add
964 * @param mods list of attributes and values for DN
965 * @return status of add
967 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
969 int ret, i;
970 char *utf8_dn = NULL;
972 if (push_utf8_allocate(&utf8_dn, new_dn) == -1) {
973 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
974 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
977 /* find the end of the list, marked by NULL or -1 */
978 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
979 /* make sure the end of the list is NULL */
980 mods[i] = NULL;
982 ret = ldap_add_s(ads->ld, utf8_dn, mods);
983 SAFE_FREE(utf8_dn);
984 return ADS_ERROR(ret);
988 * Delete a DistinguishedName
989 * @param ads connection to ads server
990 * @param new_dn DistinguishedName to delete
991 * @return status of delete
993 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
995 int ret;
996 char *utf8_dn = NULL;
997 if (push_utf8_allocate(&utf8_dn, del_dn) == -1) {
998 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
999 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1002 ret = ldap_delete(ads->ld, utf8_dn);
1003 return ADS_ERROR(ret);
1007 * Build an org unit string
1008 * if org unit is Computers or blank then assume a container, otherwise
1009 * assume a \ separated list of organisational units
1010 * @param org_unit Organizational unit
1011 * @return org unit string - caller must free
1013 char *ads_ou_string(const char *org_unit)
1015 if (!org_unit || !*org_unit || strcasecmp(org_unit, "Computers") == 0) {
1016 return strdup("cn=Computers");
1019 return ads_build_path(org_unit, "\\/", "ou=", 1);
1025 add a machine account to the ADS server
1027 static ADS_STATUS ads_add_machine_acct(ADS_STRUCT *ads, const char *hostname,
1028 uint32 account_type,
1029 const char *org_unit)
1031 ADS_STATUS ret, status;
1032 char *host_spn, *host_upn, *new_dn, *samAccountName, *controlstr;
1033 char *ou_str;
1034 TALLOC_CTX *ctx;
1035 ADS_MODLIST mods;
1036 const char *objectClass[] = {"top", "person", "organizationalPerson",
1037 "user", "computer", NULL};
1038 const char *servicePrincipalName[5] = {NULL, NULL, NULL, NULL, NULL};
1039 char *psp, *psp2;
1040 unsigned acct_control;
1042 if (!(ctx = talloc_init("machine_account")))
1043 return ADS_ERROR(LDAP_NO_MEMORY);
1045 ret = ADS_ERROR(LDAP_NO_MEMORY);
1047 if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", hostname)))
1048 goto done;
1049 if (!(host_upn = talloc_asprintf(ctx, "%s@%s", host_spn, ads->config.realm)))
1050 goto done;
1051 ou_str = ads_ou_string(org_unit);
1052 if (!ou_str) {
1053 DEBUG(1, ("ads_ou_string returned NULL (malloc failure?)\n"));
1054 goto done;
1056 new_dn = talloc_asprintf(ctx, "cn=%s,%s,%s", hostname, ou_str,
1057 ads->config.bind_path);
1058 servicePrincipalName[0] = talloc_asprintf(ctx, "HOST/%s", hostname);
1059 psp = talloc_asprintf(ctx, "HOST/%s.%s",
1060 hostname,
1061 ads->config.realm);
1062 strlower(&psp[5]);
1063 servicePrincipalName[1] = psp;
1064 servicePrincipalName[2] = talloc_asprintf(ctx, "CIFS/%s", hostname);
1065 psp2 = talloc_asprintf(ctx, "CIFS/%s.%s",
1066 hostname,
1067 ads->config.realm);
1068 strlower(&psp2[5]);
1069 servicePrincipalName[3] = psp2;
1071 free(ou_str);
1072 if (!new_dn)
1073 goto done;
1075 if (!(samAccountName = talloc_asprintf(ctx, "%s$", hostname)))
1076 goto done;
1078 acct_control = account_type | UF_DONT_EXPIRE_PASSWD;
1079 #ifndef ENCTYPE_ARCFOUR_HMAC
1080 acct_control |= UF_USE_DES_KEY_ONLY;
1081 #endif
1083 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control)))
1084 goto done;
1086 if (!(mods = ads_init_mods(ctx)))
1087 goto done;
1089 ads_mod_str(ctx, &mods, "cn", hostname);
1090 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1091 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1092 ads_mod_str(ctx, &mods, "userPrincipalName", host_upn);
1093 ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1094 ads_mod_str(ctx, &mods, "dNSHostName", hostname);
1095 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1096 ads_mod_str(ctx, &mods, "operatingSystem", "Samba");
1097 ads_mod_str(ctx, &mods, "operatingSystemVersion", VERSION);
1099 ret = ads_gen_add(ads, new_dn, mods);
1101 if (!ADS_ERR_OK(ret))
1102 goto done;
1104 /* Do not fail if we can't set security descriptor
1105 * it shouldn't be mandatory and probably we just
1106 * don't have enough rights to do it.
1108 status = ads_set_machine_sd(ads, hostname, new_dn);
1110 if (!ADS_ERR_OK(status)) {
1111 DEBUG(0, ("Warning: ads_set_machine_sd: %s\n",
1112 ads_errstr(status)));
1114 done:
1115 talloc_destroy(ctx);
1116 return ret;
1120 dump a binary result from ldap
1122 static void dump_binary(const char *field, struct berval **values)
1124 int i, j;
1125 for (i=0; values[i]; i++) {
1126 printf("%s: ", field);
1127 for (j=0; j<values[i]->bv_len; j++) {
1128 printf("%02X", (unsigned char)values[i]->bv_val[j]);
1130 printf("\n");
1134 struct uuid {
1135 uint32 i1;
1136 uint16 i2;
1137 uint16 i3;
1138 uint8 s[8];
1141 static void dump_guid(const char *field, struct berval **values)
1143 int i;
1144 GUID guid;
1145 for (i=0; values[i]; i++) {
1146 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1147 printf("%s: %s\n", field, smb_uuid_string_static(guid));
1152 dump a sid result from ldap
1154 static void dump_sid(const char *field, struct berval **values)
1156 int i;
1157 for (i=0; values[i]; i++) {
1158 DOM_SID sid;
1159 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1160 printf("%s: %s\n", field, sid_string_static(&sid));
1165 dump ntSecurityDescriptor
1167 static void dump_sd(const char *filed, struct berval **values)
1169 prs_struct ps;
1171 SEC_DESC *psd = 0;
1172 TALLOC_CTX *ctx = 0;
1174 if (!(ctx = talloc_init("sec_io_desc")))
1175 return;
1177 /* prepare data */
1178 prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
1179 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
1180 prs_set_offset(&ps,0);
1182 /* parse secdesc */
1183 if (!sec_io_desc("sd", &psd, &ps, 1)) {
1184 prs_mem_free(&ps);
1185 talloc_destroy(ctx);
1186 return;
1188 if (psd) ads_disp_sd(psd);
1190 prs_mem_free(&ps);
1191 talloc_destroy(ctx);
1195 dump a string result from ldap
1197 static void dump_string(const char *field, char **values)
1199 int i;
1200 for (i=0; values[i]; i++) {
1201 printf("%s: %s\n", field, values[i]);
1206 dump a field from LDAP on stdout
1207 used for debugging
1210 static BOOL ads_dump_field(char *field, void **values, void *data_area)
1212 const struct {
1213 const char *name;
1214 BOOL string;
1215 void (*handler)(const char *, struct berval **);
1216 } handlers[] = {
1217 {"objectGUID", False, dump_guid},
1218 {"nTSecurityDescriptor", False, dump_sd},
1219 {"dnsRecord", False, dump_binary},
1220 {"objectSid", False, dump_sid},
1221 {"tokenGroups", False, dump_sid},
1222 {NULL, True, NULL}
1224 int i;
1226 if (!field) { /* must be end of an entry */
1227 printf("\n");
1228 return False;
1231 for (i=0; handlers[i].name; i++) {
1232 if (StrCaseCmp(handlers[i].name, field) == 0) {
1233 if (!values) /* first time, indicate string or not */
1234 return handlers[i].string;
1235 handlers[i].handler(field, (struct berval **) values);
1236 break;
1239 if (!handlers[i].name) {
1240 if (!values) /* first time, indicate string conversion */
1241 return True;
1242 dump_string(field, (char **)values);
1244 return False;
1248 * Dump a result from LDAP on stdout
1249 * used for debugging
1250 * @param ads connection to ads server
1251 * @param res Results to dump
1254 void ads_dump(ADS_STRUCT *ads, void *res)
1256 ads_process_results(ads, res, ads_dump_field, NULL);
1260 * Walk through results, calling a function for each entry found.
1261 * The function receives a field name, a berval * array of values,
1262 * and a data area passed through from the start. The function is
1263 * called once with null for field and values at the end of each
1264 * entry.
1265 * @param ads connection to ads server
1266 * @param res Results to process
1267 * @param fn Function for processing each result
1268 * @param data_area user-defined area to pass to function
1270 void ads_process_results(ADS_STRUCT *ads, void *res,
1271 BOOL(*fn)(char *, void **, void *),
1272 void *data_area)
1274 void *msg;
1275 TALLOC_CTX *ctx;
1277 if (!(ctx = talloc_init("ads_process_results")))
1278 return;
1280 for (msg = ads_first_entry(ads, res); msg;
1281 msg = ads_next_entry(ads, msg)) {
1282 char *utf8_field;
1283 BerElement *b;
1285 for (utf8_field=ldap_first_attribute(ads->ld,
1286 (LDAPMessage *)msg,&b);
1287 utf8_field;
1288 utf8_field=ldap_next_attribute(ads->ld,
1289 (LDAPMessage *)msg,b)) {
1290 struct berval **ber_vals;
1291 char **str_vals, **utf8_vals;
1292 char *field;
1293 BOOL string;
1295 pull_utf8_talloc(ctx, &field, utf8_field);
1296 string = fn(field, NULL, data_area);
1298 if (string) {
1299 utf8_vals = ldap_get_values(ads->ld,
1300 (LDAPMessage *)msg, field);
1301 str_vals = ads_pull_strvals(ctx,
1302 (const char **) utf8_vals);
1303 fn(field, (void **) str_vals, data_area);
1304 ldap_value_free(utf8_vals);
1305 } else {
1306 ber_vals = ldap_get_values_len(ads->ld,
1307 (LDAPMessage *)msg, field);
1308 fn(field, (void **) ber_vals, data_area);
1310 ldap_value_free_len(ber_vals);
1312 ldap_memfree(utf8_field);
1314 ber_free(b, 0);
1315 talloc_destroy_pool(ctx);
1316 fn(NULL, NULL, data_area); /* completed an entry */
1319 talloc_destroy(ctx);
1323 * count how many replies are in a LDAPMessage
1324 * @param ads connection to ads server
1325 * @param res Results to count
1326 * @return number of replies
1328 int ads_count_replies(ADS_STRUCT *ads, void *res)
1330 return ldap_count_entries(ads->ld, (LDAPMessage *)res);
1334 * Join a machine to a realm
1335 * Creates the machine account and sets the machine password
1336 * @param ads connection to ads server
1337 * @param hostname name of host to add
1338 * @param org_unit Organizational unit to place machine in
1339 * @return status of join
1341 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *hostname,
1342 uint32 account_type, const char *org_unit)
1344 ADS_STATUS status;
1345 LDAPMessage *res;
1346 char *host;
1348 /* hostname must be lowercase */
1349 host = strdup(hostname);
1350 strlower(host);
1352 status = ads_find_machine_acct(ads, (void **)&res, host);
1353 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1354 DEBUG(0, ("Host account for %s already exists - deleting old account\n", host));
1355 status = ads_leave_realm(ads, host);
1356 if (!ADS_ERR_OK(status)) {
1357 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
1358 host, ads->config.realm));
1359 return status;
1363 status = ads_add_machine_acct(ads, host, account_type, org_unit);
1364 if (!ADS_ERR_OK(status)) {
1365 DEBUG(0, ("ads_add_machine_acct: %s\n", ads_errstr(status)));
1366 return status;
1369 status = ads_find_machine_acct(ads, (void **)&res, host);
1370 if (!ADS_ERR_OK(status)) {
1371 DEBUG(0, ("Host account test failed\n"));
1372 return status;
1375 free(host);
1377 return status;
1381 * Delete a machine from the realm
1382 * @param ads connection to ads server
1383 * @param hostname Machine to remove
1384 * @return status of delete
1386 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
1388 ADS_STATUS status;
1389 void *res, *msg;
1390 char *hostnameDN, *host;
1391 int rc;
1393 /* hostname must be lowercase */
1394 host = strdup(hostname);
1395 strlower(host);
1397 status = ads_find_machine_acct(ads, &res, host);
1398 if (!ADS_ERR_OK(status)) {
1399 DEBUG(0, ("Host account for %s does not exist.\n", host));
1400 return status;
1403 msg = ads_first_entry(ads, res);
1404 if (!msg) {
1405 return ADS_ERROR_SYSTEM(ENOENT);
1408 hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
1409 rc = ldap_delete_s(ads->ld, hostnameDN);
1410 ads_memfree(ads, hostnameDN);
1411 if (rc != LDAP_SUCCESS) {
1412 return ADS_ERROR(rc);
1415 status = ads_find_machine_acct(ads, &res, host);
1416 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1417 DEBUG(0, ("Failed to remove host account.\n"));
1418 return status;
1421 free(host);
1423 return status;
1427 * add machine account to existing security descriptor
1428 * @param ads connection to ads server
1429 * @param hostname machine to add
1430 * @param dn DN of security descriptor
1431 * @return status
1433 ADS_STATUS ads_set_machine_sd(ADS_STRUCT *ads, const char *hostname, char *dn)
1435 const char *attrs[] = {"nTSecurityDescriptor", "objectSid", 0};
1436 char *expr = 0;
1437 size_t sd_size = 0;
1438 struct berval bval = {0, NULL};
1439 prs_struct ps_wire;
1440 char *escaped_hostname = escape_ldap_string_alloc(hostname);
1442 LDAPMessage *res = 0;
1443 LDAPMessage *msg = 0;
1444 ADS_MODLIST mods = 0;
1446 NTSTATUS status;
1447 ADS_STATUS ret;
1448 DOM_SID sid;
1449 SEC_DESC *psd = NULL;
1450 TALLOC_CTX *ctx = NULL;
1452 /* Avoid segmentation fault in prs_mem_free if
1453 * we have to bail out before prs_init */
1454 ps_wire.is_dynamic = False;
1456 if (!ads) return ADS_ERROR(LDAP_SERVER_DOWN);
1458 ret = ADS_ERROR(LDAP_SUCCESS);
1460 if (!escaped_hostname) {
1461 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1464 if (asprintf(&expr, "(samAccountName=%s$)", escaped_hostname) == -1) {
1465 DEBUG(1, ("ads_set_machine_sd: asprintf failed!\n"));
1466 SAFE_FREE(escaped_hostname);
1467 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1470 SAFE_FREE(escaped_hostname);
1472 ret = ads_search(ads, (void *) &res, expr, attrs);
1474 if (!ADS_ERR_OK(ret)) return ret;
1476 if ( !(msg = ads_first_entry(ads, res) )) {
1477 ret = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1478 goto ads_set_sd_error;
1481 if (!ads_pull_sid(ads, msg, attrs[1], &sid)) {
1482 ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
1483 goto ads_set_sd_error;
1486 if (!(ctx = talloc_init("sec_io_desc"))) {
1487 ret = ADS_ERROR(LDAP_NO_MEMORY);
1488 goto ads_set_sd_error;
1491 if (!ads_pull_sd(ads, ctx, msg, attrs[0], &psd)) {
1492 ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
1493 goto ads_set_sd_error;
1496 status = sec_desc_add_sid(ctx, &psd, &sid, SEC_RIGHTS_FULL_CTRL, &sd_size);
1498 if (!NT_STATUS_IS_OK(status)) {
1499 ret = ADS_ERROR_NT(status);
1500 goto ads_set_sd_error;
1503 if (!prs_init(&ps_wire, sd_size, ctx, MARSHALL)) {
1504 ret = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1507 if (!sec_io_desc("sd_wire", &psd, &ps_wire, 1)) {
1508 ret = ADS_ERROR(LDAP_NO_MEMORY);
1509 goto ads_set_sd_error;
1512 #if 0
1513 file_save("/tmp/sec_desc.new", ps_wire.data_p, sd_size);
1514 #endif
1515 if (!(mods = ads_init_mods(ctx))) return ADS_ERROR(LDAP_NO_MEMORY);
1517 bval.bv_len = prs_offset(&ps_wire);
1518 bval.bv_val = talloc(ctx, bval.bv_len);
1519 if (!bval.bv_val) {
1520 ret = ADS_ERROR(LDAP_NO_MEMORY);
1521 goto ads_set_sd_error;
1524 prs_set_offset(&ps_wire, 0);
1526 if (!prs_copy_data_out(bval.bv_val, &ps_wire, bval.bv_len)) {
1527 ret = ADS_ERROR(LDAP_NO_MEMORY);
1528 goto ads_set_sd_error;
1531 ret = ads_mod_ber(ctx, &mods, attrs[0], &bval);
1532 if (ADS_ERR_OK(ret)) {
1533 ret = ads_gen_mod(ads, dn, mods);
1536 ads_set_sd_error:
1537 ads_msgfree(ads, res);
1538 prs_mem_free(&ps_wire);
1539 talloc_destroy(ctx);
1540 return ret;
1544 * pull the first entry from a ADS result
1545 * @param ads connection to ads server
1546 * @param res Results of search
1547 * @return first entry from result
1549 void *ads_first_entry(ADS_STRUCT *ads, void *res)
1551 return (void *)ldap_first_entry(ads->ld, (LDAPMessage *)res);
1555 * pull the next entry from a ADS result
1556 * @param ads connection to ads server
1557 * @param res Results of search
1558 * @return next entry from result
1560 void *ads_next_entry(ADS_STRUCT *ads, void *res)
1562 return (void *)ldap_next_entry(ads->ld, (LDAPMessage *)res);
1566 * pull a single string from a ADS result
1567 * @param ads connection to ads server
1568 * @param mem_ctx TALLOC_CTX to use for allocating result string
1569 * @param msg Results of search
1570 * @param field Attribute to retrieve
1571 * @return Result string in talloc context
1573 char *ads_pull_string(ADS_STRUCT *ads,
1574 TALLOC_CTX *mem_ctx, void *msg, const char *field)
1576 char **values;
1577 char *ret = NULL;
1578 char *ux_string;
1579 size_t rc;
1581 values = ldap_get_values(ads->ld, msg, field);
1582 if (!values)
1583 return NULL;
1585 if (values[0]) {
1586 rc = pull_utf8_talloc(mem_ctx, &ux_string,
1587 values[0]);
1588 if (rc != (size_t)-1)
1589 ret = ux_string;
1592 ldap_value_free(values);
1593 return ret;
1597 * pull an array of strings from a ADS result
1598 * @param ads connection to ads server
1599 * @param mem_ctx TALLOC_CTX to use for allocating result string
1600 * @param msg Results of search
1601 * @param field Attribute to retrieve
1602 * @return Result strings in talloc context
1604 char **ads_pull_strings(ADS_STRUCT *ads,
1605 TALLOC_CTX *mem_ctx, void *msg, const char *field)
1607 char **values;
1608 char **ret = NULL;
1609 int i, n;
1611 values = ldap_get_values(ads->ld, msg, field);
1612 if (!values)
1613 return NULL;
1615 for (i=0;values[i];i++)
1616 /* noop */ ;
1617 n = i;
1619 ret = talloc(mem_ctx, sizeof(char *) * (n+1));
1620 if (!ret) {
1621 ldap_value_free(values);
1622 return NULL;
1625 for (i=0;i<n;i++) {
1626 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
1627 ldap_value_free(values);
1628 return NULL;
1631 ret[i] = NULL;
1633 ldap_value_free(values);
1634 return ret;
1639 * pull a single uint32 from a ADS result
1640 * @param ads connection to ads server
1641 * @param msg Results of search
1642 * @param field Attribute to retrieve
1643 * @param v Pointer to int to store result
1644 * @return boolean inidicating success
1646 BOOL ads_pull_uint32(ADS_STRUCT *ads,
1647 void *msg, const char *field, uint32 *v)
1649 char **values;
1651 values = ldap_get_values(ads->ld, msg, field);
1652 if (!values)
1653 return False;
1654 if (!values[0]) {
1655 ldap_value_free(values);
1656 return False;
1659 *v = atoi(values[0]);
1660 ldap_value_free(values);
1661 return True;
1665 * pull a single objectGUID from an ADS result
1666 * @param ads connection to ADS server
1667 * @param msg results of search
1668 * @param guid 37-byte area to receive text guid
1669 * @return boolean indicating success
1671 BOOL ads_pull_guid(ADS_STRUCT *ads,
1672 void *msg, GUID *guid)
1674 char **values;
1676 values = ldap_get_values(ads->ld, msg, "objectGUID");
1677 if (!values)
1678 return False;
1680 if (values[0]) {
1681 memcpy(guid, values[0], sizeof(GUID));
1682 ldap_value_free(values);
1683 return True;
1685 ldap_value_free(values);
1686 return False;
1692 * pull a single DOM_SID from a ADS result
1693 * @param ads connection to ads server
1694 * @param msg Results of search
1695 * @param field Attribute to retrieve
1696 * @param sid Pointer to sid to store result
1697 * @return boolean inidicating success
1699 BOOL ads_pull_sid(ADS_STRUCT *ads,
1700 void *msg, const char *field, DOM_SID *sid)
1702 struct berval **values;
1703 BOOL ret = False;
1705 values = ldap_get_values_len(ads->ld, msg, field);
1707 if (!values)
1708 return False;
1710 if (values[0])
1711 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
1713 ldap_value_free_len(values);
1714 return ret;
1718 * pull an array of DOM_SIDs from a ADS result
1719 * @param ads connection to ads server
1720 * @param mem_ctx TALLOC_CTX for allocating sid array
1721 * @param msg Results of search
1722 * @param field Attribute to retrieve
1723 * @param sids pointer to sid array to allocate
1724 * @return the count of SIDs pulled
1726 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
1727 void *msg, const char *field, DOM_SID **sids)
1729 struct berval **values;
1730 BOOL ret;
1731 int count, i;
1733 values = ldap_get_values_len(ads->ld, msg, field);
1735 if (!values)
1736 return 0;
1738 for (i=0; values[i]; i++)
1739 /* nop */ ;
1741 (*sids) = talloc(mem_ctx, sizeof(DOM_SID) * i);
1742 if (!(*sids)) {
1743 ldap_value_free_len(values);
1744 return 0;
1747 count = 0;
1748 for (i=0; values[i]; i++) {
1749 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
1750 if (ret) {
1751 fstring sid;
1752 DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid, &(*sids)[count])));
1753 count++;
1757 ldap_value_free_len(values);
1758 return count;
1762 * pull a SEC_DESC from a ADS result
1763 * @param ads connection to ads server
1764 * @param mem_ctx TALLOC_CTX for allocating sid array
1765 * @param msg Results of search
1766 * @param field Attribute to retrieve
1767 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
1768 * @return boolean inidicating success
1770 BOOL ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
1771 void *msg, const char *field, SEC_DESC **sd)
1773 struct berval **values;
1774 prs_struct ps;
1775 BOOL ret = False;
1777 values = ldap_get_values_len(ads->ld, msg, field);
1779 if (!values) return False;
1781 if (values[0]) {
1782 prs_init(&ps, values[0]->bv_len, mem_ctx, UNMARSHALL);
1783 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
1784 prs_set_offset(&ps,0);
1786 ret = sec_io_desc("sd", sd, &ps, 1);
1789 ldap_value_free_len(values);
1790 return ret;
1794 * in order to support usernames longer than 21 characters we need to
1795 * use both the sAMAccountName and the userPrincipalName attributes
1796 * It seems that not all users have the userPrincipalName attribute set
1798 * @param ads connection to ads server
1799 * @param mem_ctx TALLOC_CTX for allocating sid array
1800 * @param msg Results of search
1801 * @return the username
1803 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, void *msg)
1805 char *ret, *p;
1807 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
1808 if (ret && (p = strchr(ret, '@'))) {
1809 *p = 0;
1810 return ret;
1812 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
1817 * find the update serial number - this is the core of the ldap cache
1818 * @param ads connection to ads server
1819 * @param ads connection to ADS server
1820 * @param usn Pointer to retrieved update serial number
1821 * @return status of search
1823 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
1825 const char *attrs[] = {"highestCommittedUSN", NULL};
1826 ADS_STATUS status;
1827 void *res;
1829 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
1830 if (!ADS_ERR_OK(status)) return status;
1832 if (ads_count_replies(ads, res) != 1) {
1833 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1836 ads_pull_uint32(ads, res, "highestCommittedUSN", usn);
1837 ads_msgfree(ads, res);
1838 return ADS_SUCCESS;
1841 /* parse a ADS timestring - typical string is
1842 '20020917091222.0Z0' which means 09:12.22 17th September
1843 2002, timezone 0 */
1844 static time_t ads_parse_time(const char *str)
1846 struct tm tm;
1848 ZERO_STRUCT(tm);
1850 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
1851 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
1852 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
1853 return 0;
1855 tm.tm_year -= 1900;
1856 tm.tm_mon -= 1;
1858 return timegm(&tm);
1863 * Find the servers name and realm - this can be done before authentication
1864 * The ldapServiceName field on w2k looks like this:
1865 * vnet3.home.samba.org:win2000-vnet3$@VNET3.HOME.SAMBA.ORG
1866 * @param ads connection to ads server
1867 * @return status of search
1869 ADS_STATUS ads_server_info(ADS_STRUCT *ads)
1871 const char *attrs[] = {"ldapServiceName", "currentTime", NULL};
1872 ADS_STATUS status;
1873 void *res;
1874 char *value;
1875 char *p;
1876 char *timestr;
1877 TALLOC_CTX *ctx;
1879 if (!(ctx = talloc_init("ads_server_info"))) {
1880 return ADS_ERROR(LDAP_NO_MEMORY);
1883 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
1884 if (!ADS_ERR_OK(status)) return status;
1886 value = ads_pull_string(ads, ctx, res, "ldapServiceName");
1887 if (!value) {
1888 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1891 timestr = ads_pull_string(ads, ctx, res, "currentTime");
1892 if (!timestr) {
1893 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1896 ldap_msgfree(res);
1898 p = strchr(value, ':');
1899 if (!p) {
1900 talloc_destroy(ctx);
1901 DEBUG(1, ("ads_server_info: returned ldap server name did not contain a ':' so was deemed invalid\n"));
1902 return ADS_ERROR(LDAP_DECODING_ERROR);
1905 SAFE_FREE(ads->config.ldap_server_name);
1907 ads->config.ldap_server_name = strdup(p+1);
1908 p = strchr(ads->config.ldap_server_name, '$');
1909 if (!p || p[1] != '@') {
1910 talloc_destroy(ctx);
1911 DEBUG(1, ("ads_server_info: returned ldap server name (%s) does not contain '$@' so was deemed invalid\n", ads->config.ldap_server_name));
1912 SAFE_FREE(ads->config.ldap_server_name);
1913 return ADS_ERROR(LDAP_DECODING_ERROR);
1916 *p = 0;
1918 SAFE_FREE(ads->config.realm);
1919 SAFE_FREE(ads->config.bind_path);
1921 ads->config.realm = strdup(p+2);
1922 ads->config.bind_path = ads_build_dn(ads->config.realm);
1924 DEBUG(3,("got ldap server name %s@%s, using bind path: %s\n",
1925 ads->config.ldap_server_name, ads->config.realm,
1926 ads->config.bind_path));
1928 ads->config.current_time = ads_parse_time(timestr);
1930 if (ads->config.current_time != 0) {
1931 ads->auth.time_offset = ads->config.current_time - time(NULL);
1932 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
1935 talloc_destroy(ctx);
1937 return ADS_SUCCESS;
1942 * find the list of trusted domains
1943 * @param ads connection to ads server
1944 * @param mem_ctx TALLOC_CTX for allocating results
1945 * @param num_trusts pointer to number of trusts
1946 * @param names pointer to trusted domain name list
1947 * @param sids pointer to list of sids of trusted domains
1948 * @return the count of SIDs pulled
1950 ADS_STATUS ads_trusted_domains(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
1951 int *num_trusts,
1952 char ***names,
1953 char ***alt_names,
1954 DOM_SID **sids)
1956 const char *attrs[] = {"name", "flatname", "securityIdentifier",
1957 "trustDirection", NULL};
1958 ADS_STATUS status;
1959 void *res, *msg;
1960 int count, i;
1962 *num_trusts = 0;
1964 status = ads_search(ads, &res, "(objectcategory=trustedDomain)", attrs);
1965 if (!ADS_ERR_OK(status)) return status;
1967 count = ads_count_replies(ads, res);
1968 if (count == 0) {
1969 ads_msgfree(ads, res);
1970 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1973 (*names) = talloc(mem_ctx, sizeof(char *) * count);
1974 (*alt_names) = talloc(mem_ctx, sizeof(char *) * count);
1975 (*sids) = talloc(mem_ctx, sizeof(DOM_SID) * count);
1976 if (! *names || ! *sids) return ADS_ERROR(LDAP_NO_MEMORY);
1978 for (i=0, msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
1979 uint32 direction;
1981 /* direction is a 2 bit bitfield, 1 means they trust us
1982 but we don't trust them, so we should not list them
1983 as users from that domain can't login */
1984 if (ads_pull_uint32(ads, msg, "trustDirection", &direction) &&
1985 direction == 1) {
1986 continue;
1989 (*names)[i] = ads_pull_string(ads, mem_ctx, msg, "name");
1990 (*alt_names)[i] = ads_pull_string(ads, mem_ctx, msg, "flatname");
1992 if ((*alt_names)[i] && (*alt_names)[i][0]) {
1993 /* we prefer the flatname as the primary name
1994 for consistency with RPC */
1995 char *name = (*alt_names)[i];
1996 (*alt_names)[i] = (*names)[i];
1997 (*names)[i] = name;
1999 if (ads_pull_sid(ads, msg, "securityIdentifier", &(*sids)[i])) {
2000 i++;
2004 ads_msgfree(ads, res);
2006 *num_trusts = i;
2008 return ADS_SUCCESS;
2012 * find the domain sid for our domain
2013 * @param ads connection to ads server
2014 * @param sid Pointer to domain sid
2015 * @return status of search
2017 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2019 const char *attrs[] = {"objectSid", NULL};
2020 void *res;
2021 ADS_STATUS rc;
2023 rc = ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2024 attrs, &res);
2025 if (!ADS_ERR_OK(rc)) return rc;
2026 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2027 return ADS_ERROR_SYSTEM(ENOENT);
2029 ads_msgfree(ads, res);
2031 return ADS_SUCCESS;
2034 /* this is rather complex - we need to find the allternate (netbios) name
2035 for the domain, but there isn't a simple query to do this. Instead
2036 we look for the principle names on the DCs account and find one that has
2037 the right form, then extract the netbios name of the domain from that
2039 NOTE! better method is this:
2041 bin/net -Uadministrator%XXXXX ads search '(&(objectclass=crossref)(dnsroot=VNET3.HOME.SAMBA.ORG))' nETBIOSName
2043 but you need to force the bind path to match the configurationNamingContext from the rootDSE
2046 ADS_STATUS ads_workgroup_name(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char **workgroup)
2048 char *expr;
2049 ADS_STATUS rc;
2050 char **principles;
2051 char *prefix;
2052 int prefix_length;
2053 int i;
2054 void *res;
2055 const char *attrs[] = {"servicePrincipalName", NULL};
2057 (*workgroup) = NULL;
2059 asprintf(&expr, "(&(objectclass=computer)(dnshostname=%s.%s))",
2060 ads->config.ldap_server_name, ads->config.realm);
2061 rc = ads_search(ads, &res, expr, attrs);
2062 free(expr);
2064 if (!ADS_ERR_OK(rc)) {
2065 return rc;
2068 principles = ads_pull_strings(ads, mem_ctx, res, "servicePrincipalName");
2070 ads_msgfree(ads, res);
2072 if (!principles) {
2073 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2076 asprintf(&prefix, "HOST/%s.%s/",
2077 ads->config.ldap_server_name,
2078 ads->config.realm);
2080 prefix_length = strlen(prefix);
2082 for (i=0;principles[i]; i++) {
2083 if (strncasecmp(principles[i], prefix, prefix_length) == 0 &&
2084 strcasecmp(ads->config.realm, principles[i]+prefix_length) != 0 &&
2085 !strchr(principles[i]+prefix_length, '.')) {
2086 /* found an alternate (short) name for the domain. */
2087 DEBUG(3,("Found alternate name '%s' for realm '%s'\n",
2088 principles[i]+prefix_length,
2089 ads->config.realm));
2090 (*workgroup) = talloc_strdup(mem_ctx, principles[i]+prefix_length);
2091 break;
2094 free(prefix);
2096 if (!*workgroup) {
2097 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2100 return ADS_SUCCESS;
2103 #endif