r15822: Add suggestion made by Ralf Haferkamp.
[Samba.git] / source / libads / ldap.c
blobbac85f3222b38f19af986eb59762e4666ed1a53b
1 /*
2 Unix SMB/CIFS implementation.
3 ads (active directory) utility library
4 Copyright (C) Andrew Tridgell 2001
5 Copyright (C) Remus Koos 2001
6 Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002
7 Copyright (C) Guenther Deschner 2005
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 #include "includes.h"
26 #ifdef HAVE_LDAP
28 /**
29 * @file ldap.c
30 * @brief basic ldap client-side routines for ads server communications
32 * The routines contained here should do the necessary ldap calls for
33 * ads setups.
35 * Important note: attribute names passed into ads_ routines must
36 * already be in UTF-8 format. We do not convert them because in almost
37 * all cases, they are just ascii (which is represented with the same
38 * codepoints in UTF-8). This may have to change at some point
39 **/
42 #define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"
44 static SIG_ATOMIC_T gotalarm;
46 /***************************************************************
47 Signal function to tell us we timed out.
48 ****************************************************************/
50 static void gotalarm_sig(void)
52 gotalarm = 1;
55 LDAP *ldap_open_with_timeout(const char *server, int port, unsigned int to)
57 LDAP *ldp = NULL;
59 /* Setup timeout */
60 gotalarm = 0;
61 CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
62 alarm(to);
63 /* End setup timeout. */
65 ldp = ldap_open(server, port);
67 /* Teardown timeout. */
68 CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
69 alarm(0);
71 return ldp;
74 static int ldap_search_with_timeout(LDAP *ld,
75 LDAP_CONST char *base,
76 int scope,
77 LDAP_CONST char *filter,
78 char **attrs,
79 int attrsonly,
80 LDAPControl **sctrls,
81 LDAPControl **cctrls,
82 int sizelimit,
83 LDAPMessage **res )
85 struct timeval timeout;
86 int result;
88 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
89 timeout.tv_sec = lp_ldap_timeout();
90 timeout.tv_usec = 0;
92 /* Setup alarm timeout.... Do we need both of these ? JRA. */
93 gotalarm = 0;
94 CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
95 alarm(lp_ldap_timeout());
96 /* End setup timeout. */
98 result = ldap_search_ext_s(ld, base, scope, filter, attrs,
99 attrsonly, sctrls, cctrls, &timeout,
100 sizelimit, res);
102 /* Teardown timeout. */
103 CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
104 alarm(0);
106 if (gotalarm != 0)
107 return LDAP_TIMELIMIT_EXCEEDED;
109 return result;
113 try a connection to a given ldap server, returning True and setting the servers IP
114 in the ads struct if successful
116 BOOL ads_try_connect(ADS_STRUCT *ads, const char *server )
118 char *srv;
119 struct cldap_netlogon_reply cldap_reply;
121 if (!server || !*server) {
122 return False;
125 DEBUG(5,("ads_try_connect: sending CLDAP request to %s\n", server));
127 /* this copes with inet_ntoa brokenness */
129 srv = SMB_STRDUP(server);
131 ZERO_STRUCT( cldap_reply );
133 if ( !ads_cldap_netlogon( srv, ads->server.realm, &cldap_reply ) ) {
134 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", srv));
135 return False;
138 /* Check the CLDAP reply flags */
140 if ( !(cldap_reply.flags & ADS_LDAP) ) {
141 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
142 srv));
143 SAFE_FREE( srv );
144 return False;
147 /* Fill in the ads->config values */
149 SAFE_FREE(ads->config.realm);
150 SAFE_FREE(ads->config.bind_path);
151 SAFE_FREE(ads->config.ldap_server_name);
153 ads->config.ldap_server_name = SMB_STRDUP(cldap_reply.hostname);
154 strupper_m(cldap_reply.domain);
155 ads->config.realm = SMB_STRDUP(cldap_reply.domain);
156 ads->config.bind_path = ads_build_dn(ads->config.realm);
158 ads->ldap_port = LDAP_PORT;
159 ads->ldap_ip = *interpret_addr2(srv);
160 SAFE_FREE(srv);
162 /* cache the successful connection */
164 saf_store( ads->server.workgroup, server );
166 return True;
169 /**********************************************************************
170 Try to find an AD dc using our internal name resolution routines
171 Try the realm first and then then workgroup name if netbios is not
172 disabled
173 **********************************************************************/
175 static BOOL ads_find_dc(ADS_STRUCT *ads)
177 const char *c_realm;
178 int count, i=0;
179 struct ip_service *ip_list;
180 pstring realm;
181 BOOL got_realm = False;
182 BOOL use_own_domain = False;
184 /* if the realm and workgroup are both empty, assume they are ours */
186 /* realm */
187 c_realm = ads->server.realm;
189 if ( !c_realm || !*c_realm ) {
190 /* special case where no realm and no workgroup means our own */
191 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
192 use_own_domain = True;
193 c_realm = lp_realm();
197 if (c_realm && *c_realm)
198 got_realm = True;
200 again:
201 /* we need to try once with the realm name and fallback to the
202 netbios domain name if we fail (if netbios has not been disabled */
204 if ( !got_realm && !lp_disable_netbios() ) {
205 c_realm = ads->server.workgroup;
206 if (!c_realm || !*c_realm) {
207 if ( use_own_domain )
208 c_realm = lp_workgroup();
211 if ( !c_realm || !*c_realm ) {
212 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
213 return False;
217 pstrcpy( realm, c_realm );
219 DEBUG(6,("ads_find_dc: looking for %s '%s'\n",
220 (got_realm ? "realm" : "domain"), realm));
222 if ( !get_sorted_dc_list(realm, &ip_list, &count, got_realm) ) {
223 /* fall back to netbios if we can */
224 if ( got_realm && !lp_disable_netbios() ) {
225 got_realm = False;
226 goto again;
229 return False;
232 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
233 for ( i=0; i<count; i++ ) {
234 fstring server;
236 fstrcpy( server, inet_ntoa(ip_list[i].ip) );
238 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
239 continue;
241 if ( ads_try_connect(ads, server) ) {
242 SAFE_FREE(ip_list);
243 return True;
246 /* keep track of failures */
247 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
250 SAFE_FREE(ip_list);
252 return False;
257 * Connect to the LDAP server
258 * @param ads Pointer to an existing ADS_STRUCT
259 * @return status of connection
261 ADS_STATUS ads_connect(ADS_STRUCT *ads)
263 int version = LDAP_VERSION3;
264 ADS_STATUS status;
266 ads->last_attempt = time(NULL);
267 ads->ld = NULL;
269 /* try with a user specified server */
271 if (ads->server.ldap_server &&
272 ads_try_connect(ads, ads->server.ldap_server)) {
273 goto got_connection;
276 if (ads_find_dc(ads)) {
277 goto got_connection;
280 return ADS_ERROR_SYSTEM(errno?errno:ENOENT);
282 got_connection:
283 DEBUG(3,("Connected to LDAP server %s\n", inet_ntoa(ads->ldap_ip)));
285 if (!ads->auth.user_name) {
286 /* have to use the userPrincipalName value here and
287 not servicePrincipalName; found by Guenther Deschner @ Sernet.
289 Is this still correct? The comment does not match
290 the code. --jerry */
292 asprintf(&ads->auth.user_name, "host/%s", global_myname() );
295 if (!ads->auth.realm) {
296 ads->auth.realm = SMB_STRDUP(ads->config.realm);
299 if (!ads->auth.kdc_server) {
300 ads->auth.kdc_server = SMB_STRDUP(inet_ntoa(ads->ldap_ip));
303 #if KRB5_DNS_HACK
304 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
305 to MIT kerberos to work (tridge) */
307 char *env;
308 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
309 setenv(env, ads->auth.kdc_server, 1);
310 free(env);
312 #endif
314 /* If the caller() requested no LDAP bind, then we are done */
316 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
317 return ADS_SUCCESS;
320 /* Otherwise setup the TCP LDAP session */
322 if ( (ads->ld = ldap_open_with_timeout(ads->config.ldap_server_name,
323 LDAP_PORT, lp_ldap_timeout())) == NULL )
325 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
327 ldap_set_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
329 status = ADS_ERROR(smb_ldap_start_tls(ads->ld, version));
330 if (!ADS_ERR_OK(status)) {
331 return status;
334 /* fill in the current time and offsets */
336 status = ads_current_time( ads );
337 if ( !ADS_ERR_OK(status) ) {
338 return status;
341 /* Now do the bind */
343 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
344 return ADS_ERROR(ldap_simple_bind_s( ads->ld, NULL, NULL));
347 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
348 return ADS_ERROR(ldap_simple_bind_s( ads->ld, ads->auth.user_name, ads->auth.password));
351 return ads_sasl_bind(ads);
355 Duplicate a struct berval into talloc'ed memory
357 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
359 struct berval *value;
361 if (!in_val) return NULL;
363 value = TALLOC_ZERO_P(ctx, struct berval);
364 if (value == NULL)
365 return NULL;
366 if (in_val->bv_len == 0) return value;
368 value->bv_len = in_val->bv_len;
369 value->bv_val = TALLOC_MEMDUP(ctx, in_val->bv_val, in_val->bv_len);
370 return value;
374 Make a values list out of an array of (struct berval *)
376 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
377 const struct berval **in_vals)
379 struct berval **values;
380 int i;
382 if (!in_vals) return NULL;
383 for (i=0; in_vals[i]; i++)
384 ; /* count values */
385 values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
386 if (!values) return NULL;
388 for (i=0; in_vals[i]; i++) {
389 values[i] = dup_berval(ctx, in_vals[i]);
391 return values;
395 UTF8-encode a values list out of an array of (char *)
397 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
399 char **values;
400 int i;
402 if (!in_vals) return NULL;
403 for (i=0; in_vals[i]; i++)
404 ; /* count values */
405 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
406 if (!values) return NULL;
408 for (i=0; in_vals[i]; i++) {
409 push_utf8_talloc(ctx, &values[i], in_vals[i]);
411 return values;
415 Pull a (char *) array out of a UTF8-encoded values list
417 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
419 char **values;
420 int i;
422 if (!in_vals) return NULL;
423 for (i=0; in_vals[i]; i++)
424 ; /* count values */
425 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
426 if (!values) return NULL;
428 for (i=0; in_vals[i]; i++) {
429 pull_utf8_talloc(ctx, &values[i], in_vals[i]);
431 return values;
435 * Do a search with paged results. cookie must be null on the first
436 * call, and then returned on each subsequent call. It will be null
437 * again when the entire search is complete
438 * @param ads connection to ads server
439 * @param bind_path Base dn for the search
440 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
441 * @param expr Search expression - specified in local charset
442 * @param attrs Attributes to retrieve - specified in utf8 or ascii
443 * @param res ** which will contain results - free res* with ads_msgfree()
444 * @param count Number of entries retrieved on this page
445 * @param cookie The paged results cookie to be returned on subsequent calls
446 * @return status of search
448 ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads, const char *bind_path,
449 int scope, const char *expr,
450 const char **attrs, void *args, void **res,
451 int *count, void **cookie)
453 int rc, i, version;
454 char *utf8_expr, *utf8_path, **search_attrs;
455 LDAPControl PagedResults, NoReferrals, ExtendedDn, *controls[4], **rcontrols;
456 BerElement *cookie_be = NULL;
457 struct berval *cookie_bv= NULL;
458 BerElement *extdn_be = NULL;
459 struct berval *extdn_bv= NULL;
461 TALLOC_CTX *ctx;
462 ads_control *external_control = (ads_control *) args;
464 *res = NULL;
466 if (!(ctx = talloc_init("ads_do_paged_search_args")))
467 return ADS_ERROR(LDAP_NO_MEMORY);
469 /* 0 means the conversion worked but the result was empty
470 so we only fail if it's -1. In any case, it always
471 at least nulls out the dest */
472 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
473 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
474 rc = LDAP_NO_MEMORY;
475 goto done;
478 if (!attrs || !(*attrs))
479 search_attrs = NULL;
480 else {
481 /* This would be the utf8-encoded version...*/
482 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
483 if (!(str_list_copy(&search_attrs, attrs))) {
484 rc = LDAP_NO_MEMORY;
485 goto done;
490 /* Paged results only available on ldap v3 or later */
491 ldap_get_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
492 if (version < LDAP_VERSION3) {
493 rc = LDAP_NOT_SUPPORTED;
494 goto done;
497 cookie_be = ber_alloc_t(LBER_USE_DER);
498 if (cookie && *cookie) {
499 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
500 ber_bvfree(*cookie); /* don't need it from last time */
501 *cookie = NULL;
502 } else {
503 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
505 ber_flatten(cookie_be, &cookie_bv);
506 PagedResults.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID);
507 PagedResults.ldctl_iscritical = (char) 1;
508 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
509 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
511 NoReferrals.ldctl_oid = CONST_DISCARD(char *, ADS_NO_REFERRALS_OID);
512 NoReferrals.ldctl_iscritical = (char) 0;
513 NoReferrals.ldctl_value.bv_len = 0;
514 NoReferrals.ldctl_value.bv_val = CONST_DISCARD(char *, "");
516 if (external_control && strequal(external_control->control, ADS_EXTENDED_DN_OID)) {
518 ExtendedDn.ldctl_oid = CONST_DISCARD(char *, external_control->control);
519 ExtendedDn.ldctl_iscritical = (char) external_control->critical;
521 /* win2k does not accept a ldctl_value beeing passed in */
523 if (external_control->val != 0) {
525 if ((extdn_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
526 rc = LDAP_NO_MEMORY;
527 goto done;
530 if ((ber_printf(extdn_be, "{i}", (ber_int_t) external_control->val)) == -1) {
531 rc = LDAP_NO_MEMORY;
532 goto done;
534 if ((ber_flatten(extdn_be, &extdn_bv)) == -1) {
535 rc = LDAP_NO_MEMORY;
536 goto done;
539 ExtendedDn.ldctl_value.bv_len = extdn_bv->bv_len;
540 ExtendedDn.ldctl_value.bv_val = extdn_bv->bv_val;
542 } else {
543 ExtendedDn.ldctl_value.bv_len = 0;
544 ExtendedDn.ldctl_value.bv_val = NULL;
547 controls[0] = &NoReferrals;
548 controls[1] = &PagedResults;
549 controls[2] = &ExtendedDn;
550 controls[3] = NULL;
552 } else {
553 controls[0] = &NoReferrals;
554 controls[1] = &PagedResults;
555 controls[2] = NULL;
558 /* we need to disable referrals as the openldap libs don't
559 handle them and paged results at the same time. Using them
560 together results in the result record containing the server
561 page control being removed from the result list (tridge/jmcd)
563 leaving this in despite the control that says don't generate
564 referrals, in case the server doesn't support it (jmcd)
566 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
568 rc = ldap_search_with_timeout(ads->ld, utf8_path, scope, utf8_expr,
569 search_attrs, 0, controls,
570 NULL, LDAP_NO_LIMIT,
571 (LDAPMessage **)res);
573 ber_free(cookie_be, 1);
574 ber_bvfree(cookie_bv);
576 if (rc) {
577 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
578 ldap_err2string(rc)));
579 goto done;
582 rc = ldap_parse_result(ads->ld, *res, NULL, NULL, NULL,
583 NULL, &rcontrols, 0);
585 if (!rcontrols) {
586 goto done;
589 for (i=0; rcontrols[i]; i++) {
590 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
591 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
592 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
593 &cookie_bv);
594 /* the berval is the cookie, but must be freed when
595 it is all done */
596 if (cookie_bv->bv_len) /* still more to do */
597 *cookie=ber_bvdup(cookie_bv);
598 else
599 *cookie=NULL;
600 ber_bvfree(cookie_bv);
601 ber_free(cookie_be, 1);
602 break;
605 ldap_controls_free(rcontrols);
607 done:
608 talloc_destroy(ctx);
610 if (extdn_be) {
611 ber_free(extdn_be, 1);
614 if (extdn_bv) {
615 ber_bvfree(extdn_bv);
618 /* if/when we decide to utf8-encode attrs, take out this next line */
619 str_list_free(&search_attrs);
621 return ADS_ERROR(rc);
624 ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
625 int scope, const char *expr,
626 const char **attrs, void **res,
627 int *count, void **cookie)
629 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
634 * Get all results for a search. This uses ads_do_paged_search() to return
635 * all entries in a large search.
636 * @param ads connection to ads server
637 * @param bind_path Base dn for the search
638 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
639 * @param expr Search expression
640 * @param attrs Attributes to retrieve
641 * @param res ** which will contain results - free res* with ads_msgfree()
642 * @return status of search
644 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
645 int scope, const char *expr,
646 const char **attrs, void *args, void **res)
648 void *cookie = NULL;
649 int count = 0;
650 ADS_STATUS status;
652 *res = NULL;
653 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
654 &count, &cookie);
656 if (!ADS_ERR_OK(status))
657 return status;
659 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
660 while (cookie) {
661 void *res2 = NULL;
662 ADS_STATUS status2;
663 LDAPMessage *msg, *next;
665 status2 = ads_do_paged_search_args(ads, bind_path, scope, expr,
666 attrs, args, &res2, &count, &cookie);
668 if (!ADS_ERR_OK(status2)) break;
670 /* this relies on the way that ldap_add_result_entry() works internally. I hope
671 that this works on all ldap libs, but I have only tested with openldap */
672 for (msg = ads_first_entry(ads, res2); msg; msg = next) {
673 next = ads_next_entry(ads, msg);
674 ldap_add_result_entry((LDAPMessage **)res, msg);
676 /* note that we do not free res2, as the memory is now
677 part of the main returned list */
679 #else
680 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
681 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
682 #endif
684 return status;
687 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
688 int scope, const char *expr,
689 const char **attrs, void **res)
691 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
695 * Run a function on all results for a search. Uses ads_do_paged_search() and
696 * runs the function as each page is returned, using ads_process_results()
697 * @param ads connection to ads server
698 * @param bind_path Base dn for the search
699 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
700 * @param expr Search expression - specified in local charset
701 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
702 * @param fn Function which takes attr name, values list, and data_area
703 * @param data_area Pointer which is passed to function on each call
704 * @return status of search
706 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
707 int scope, const char *expr, const char **attrs,
708 BOOL(*fn)(char *, void **, void *),
709 void *data_area)
711 void *cookie = NULL;
712 int count = 0;
713 ADS_STATUS status;
714 void *res;
716 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
717 &count, &cookie);
719 if (!ADS_ERR_OK(status)) return status;
721 ads_process_results(ads, res, fn, data_area);
722 ads_msgfree(ads, res);
724 while (cookie) {
725 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
726 &res, &count, &cookie);
728 if (!ADS_ERR_OK(status)) break;
730 ads_process_results(ads, res, fn, data_area);
731 ads_msgfree(ads, res);
734 return status;
738 * Do a search with a timeout.
739 * @param ads connection to ads server
740 * @param bind_path Base dn for the search
741 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
742 * @param expr Search expression
743 * @param attrs Attributes to retrieve
744 * @param res ** which will contain results - free res* with ads_msgfree()
745 * @return status of search
747 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
748 const char *expr,
749 const char **attrs, void **res)
751 int rc;
752 char *utf8_expr, *utf8_path, **search_attrs = NULL;
753 TALLOC_CTX *ctx;
755 *res = NULL;
756 if (!(ctx = talloc_init("ads_do_search"))) {
757 DEBUG(1,("ads_do_search: talloc_init() failed!"));
758 return ADS_ERROR(LDAP_NO_MEMORY);
761 /* 0 means the conversion worked but the result was empty
762 so we only fail if it's negative. In any case, it always
763 at least nulls out the dest */
764 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
765 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
766 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
767 rc = LDAP_NO_MEMORY;
768 goto done;
771 if (!attrs || !(*attrs))
772 search_attrs = NULL;
773 else {
774 /* This would be the utf8-encoded version...*/
775 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
776 if (!(str_list_copy(&search_attrs, attrs)))
778 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
779 rc = LDAP_NO_MEMORY;
780 goto done;
784 /* see the note in ads_do_paged_search - we *must* disable referrals */
785 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
787 rc = ldap_search_with_timeout(ads->ld, utf8_path, scope, utf8_expr,
788 search_attrs, 0, NULL, NULL,
789 LDAP_NO_LIMIT,
790 (LDAPMessage **)res);
792 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
793 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
794 rc = 0;
797 done:
798 talloc_destroy(ctx);
799 /* if/when we decide to utf8-encode attrs, take out this next line */
800 str_list_free(&search_attrs);
801 return ADS_ERROR(rc);
804 * Do a general ADS search
805 * @param ads connection to ads server
806 * @param res ** which will contain results - free res* with ads_msgfree()
807 * @param expr Search expression
808 * @param attrs Attributes to retrieve
809 * @return status of search
811 ADS_STATUS ads_search(ADS_STRUCT *ads, void **res,
812 const char *expr,
813 const char **attrs)
815 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
816 expr, attrs, res);
820 * Do a search on a specific DistinguishedName
821 * @param ads connection to ads server
822 * @param res ** which will contain results - free res* with ads_msgfree()
823 * @param dn DistinguishName to search
824 * @param attrs Attributes to retrieve
825 * @return status of search
827 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, void **res,
828 const char *dn,
829 const char **attrs)
831 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)", attrs, res);
835 * Free up memory from a ads_search
836 * @param ads connection to ads server
837 * @param msg Search results to free
839 void ads_msgfree(ADS_STRUCT *ads, void *msg)
841 if (!msg) return;
842 ldap_msgfree(msg);
846 * Free up memory from various ads requests
847 * @param ads connection to ads server
848 * @param mem Area to free
850 void ads_memfree(ADS_STRUCT *ads, void *mem)
852 SAFE_FREE(mem);
856 * Get a dn from search results
857 * @param ads connection to ads server
858 * @param msg Search result
859 * @return dn string
861 char *ads_get_dn(ADS_STRUCT *ads, void *msg)
863 char *utf8_dn, *unix_dn;
865 utf8_dn = ldap_get_dn(ads->ld, msg);
867 if (!utf8_dn) {
868 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
869 return NULL;
872 if (pull_utf8_allocate(&unix_dn, utf8_dn) == (size_t)-1) {
873 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
874 utf8_dn ));
875 return NULL;
877 ldap_memfree(utf8_dn);
878 return unix_dn;
882 * Get a canonical dn from search results
883 * @param ads connection to ads server
884 * @param msg Search result
885 * @return dn string
887 char *ads_get_dn_canonical(ADS_STRUCT *ads, void *msg)
889 #ifdef HAVE_LDAP_DN2AD_CANONICAL
890 return ldap_dn2ad_canonical(ads_get_dn(ads, msg));
891 #else
892 return NULL;
893 #endif
897 * Get the parent from a dn
898 * @param dn the dn to return the parent from
899 * @return parent dn string
901 char *ads_parent_dn(const char *dn)
903 char *p = strchr(dn, ',');
905 if (p == NULL) {
906 return NULL;
909 return p+1;
913 * Find a machine account given a hostname
914 * @param ads connection to ads server
915 * @param res ** which will contain results - free res* with ads_msgfree()
916 * @param host Hostname to search for
917 * @return status of search
919 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, void **res, const char *machine)
921 ADS_STATUS status;
922 char *expr;
923 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
925 *res = NULL;
927 /* the easiest way to find a machine account anywhere in the tree
928 is to look for hostname$ */
929 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
930 DEBUG(1, ("asprintf failed!\n"));
931 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
934 status = ads_search(ads, res, expr, attrs);
935 SAFE_FREE(expr);
936 return status;
940 * Initialize a list of mods to be used in a modify request
941 * @param ctx An initialized TALLOC_CTX
942 * @return allocated ADS_MODLIST
944 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
946 #define ADS_MODLIST_ALLOC_SIZE 10
947 LDAPMod **mods;
949 if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
950 /* -1 is safety to make sure we don't go over the end.
951 need to reset it to NULL before doing ldap modify */
952 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
954 return (ADS_MODLIST)mods;
959 add an attribute to the list, with values list already constructed
961 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
962 int mod_op, const char *name,
963 const void **invals)
965 int curmod;
966 LDAPMod **modlist = (LDAPMod **) *mods;
967 struct berval **ber_values = NULL;
968 char **char_values = NULL;
970 if (!invals) {
971 mod_op = LDAP_MOD_DELETE;
972 } else {
973 if (mod_op & LDAP_MOD_BVALUES)
974 ber_values = ads_dup_values(ctx,
975 (const struct berval **)invals);
976 else
977 char_values = ads_push_strvals(ctx,
978 (const char **) invals);
981 /* find the first empty slot */
982 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
983 curmod++);
984 if (modlist[curmod] == (LDAPMod *) -1) {
985 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
986 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
987 return ADS_ERROR(LDAP_NO_MEMORY);
988 memset(&modlist[curmod], 0,
989 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
990 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
991 *mods = (ADS_MODLIST)modlist;
994 if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
995 return ADS_ERROR(LDAP_NO_MEMORY);
996 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
997 if (mod_op & LDAP_MOD_BVALUES) {
998 modlist[curmod]->mod_bvalues = ber_values;
999 } else if (mod_op & LDAP_MOD_DELETE) {
1000 modlist[curmod]->mod_values = NULL;
1001 } else {
1002 modlist[curmod]->mod_values = char_values;
1005 modlist[curmod]->mod_op = mod_op;
1006 return ADS_ERROR(LDAP_SUCCESS);
1010 * Add a single string value to a mod list
1011 * @param ctx An initialized TALLOC_CTX
1012 * @param mods An initialized ADS_MODLIST
1013 * @param name The attribute name to add
1014 * @param val The value to add - NULL means DELETE
1015 * @return ADS STATUS indicating success of add
1017 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1018 const char *name, const char *val)
1020 const char *values[2];
1022 values[0] = val;
1023 values[1] = NULL;
1025 if (!val)
1026 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1027 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name,
1028 (const void **) values);
1032 * Add an array of string values to a mod list
1033 * @param ctx An initialized TALLOC_CTX
1034 * @param mods An initialized ADS_MODLIST
1035 * @param name The attribute name to add
1036 * @param vals The array of string values to add - NULL means DELETE
1037 * @return ADS STATUS indicating success of add
1039 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1040 const char *name, const char **vals)
1042 if (!vals)
1043 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1044 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1045 name, (const void **) vals);
1048 #if 0
1050 * Add a single ber-encoded value to a mod list
1051 * @param ctx An initialized TALLOC_CTX
1052 * @param mods An initialized ADS_MODLIST
1053 * @param name The attribute name to add
1054 * @param val The value to add - NULL means DELETE
1055 * @return ADS STATUS indicating success of add
1057 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1058 const char *name, const struct berval *val)
1060 const struct berval *values[2];
1062 values[0] = val;
1063 values[1] = NULL;
1064 if (!val)
1065 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1066 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1067 name, (const void **) values);
1069 #endif
1072 * Perform an ldap modify
1073 * @param ads connection to ads server
1074 * @param mod_dn DistinguishedName to modify
1075 * @param mods list of modifications to perform
1076 * @return status of modify
1078 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1080 int ret,i;
1081 char *utf8_dn = NULL;
1083 this control is needed to modify that contains a currently
1084 non-existent attribute (but allowable for the object) to run
1086 LDAPControl PermitModify = {
1087 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
1088 {0, NULL},
1089 (char) 1};
1090 LDAPControl *controls[2];
1092 controls[0] = &PermitModify;
1093 controls[1] = NULL;
1095 if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) {
1096 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1099 /* find the end of the list, marked by NULL or -1 */
1100 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1101 /* make sure the end of the list is NULL */
1102 mods[i] = NULL;
1103 ret = ldap_modify_ext_s(ads->ld, utf8_dn,
1104 (LDAPMod **) mods, controls, NULL);
1105 SAFE_FREE(utf8_dn);
1106 return ADS_ERROR(ret);
1110 * Perform an ldap add
1111 * @param ads connection to ads server
1112 * @param new_dn DistinguishedName to add
1113 * @param mods list of attributes and values for DN
1114 * @return status of add
1116 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1118 int ret, i;
1119 char *utf8_dn = NULL;
1121 if (push_utf8_allocate(&utf8_dn, new_dn) == -1) {
1122 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1123 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1126 /* find the end of the list, marked by NULL or -1 */
1127 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1128 /* make sure the end of the list is NULL */
1129 mods[i] = NULL;
1131 ret = ldap_add_s(ads->ld, utf8_dn, (LDAPMod**)mods);
1132 SAFE_FREE(utf8_dn);
1133 return ADS_ERROR(ret);
1137 * Delete a DistinguishedName
1138 * @param ads connection to ads server
1139 * @param new_dn DistinguishedName to delete
1140 * @return status of delete
1142 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1144 int ret;
1145 char *utf8_dn = NULL;
1146 if (push_utf8_allocate(&utf8_dn, del_dn) == -1) {
1147 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1148 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1151 ret = ldap_delete_s(ads->ld, utf8_dn);
1152 return ADS_ERROR(ret);
1156 * Build an org unit string
1157 * if org unit is Computers or blank then assume a container, otherwise
1158 * assume a / separated list of organisational units.
1159 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1160 * @param ads connection to ads server
1161 * @param org_unit Organizational unit
1162 * @return org unit string - caller must free
1164 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1166 char *ret = NULL;
1168 if (!org_unit || !*org_unit) {
1170 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1172 /* samba4 might not yet respond to a wellknownobject-query */
1173 return ret ? ret : SMB_STRDUP("cn=Computers");
1176 if (strequal(org_unit, "Computers")) {
1177 return SMB_STRDUP("cn=Computers");
1180 /* jmcd: removed "\\" from the separation chars, because it is
1181 needed as an escape for chars like '#' which are valid in an
1182 OU name */
1183 return ads_build_path(org_unit, "/", "ou=", 1);
1187 * Get a org unit string for a well-known GUID
1188 * @param ads connection to ads server
1189 * @param wknguid Well known GUID
1190 * @return org unit string - caller must free
1192 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1194 ADS_STATUS status;
1195 void *res;
1196 char *base, *wkn_dn, *ret, **wkn_dn_exp, **bind_dn_exp;
1197 const char *attrs[] = {"distinguishedName", NULL};
1198 int new_ln, wkn_ln, bind_ln, i;
1200 if (wknguid == NULL) {
1201 return NULL;
1204 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1205 DEBUG(1, ("asprintf failed!\n"));
1206 return NULL;
1209 status = ads_search_dn(ads, &res, base, attrs);
1210 if (!ADS_ERR_OK(status)) {
1211 DEBUG(1,("Failed while searching for: %s\n", base));
1212 return NULL;
1214 free(base);
1216 if (ads_count_replies(ads, res) != 1) {
1217 return NULL;
1220 /* substitute the bind-path from the well-known-guid-search result */
1221 wkn_dn = ads_get_dn(ads, res);
1222 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1223 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1225 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1227 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1230 new_ln = wkn_ln - bind_ln;
1232 ret = wkn_dn_exp[0];
1234 for (i=1; i < new_ln; i++) {
1235 char *s;
1236 asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]);
1237 ret = SMB_STRDUP(s);
1238 free(s);
1241 return ret;
1245 * Adds (appends) an item to an attribute array, rather then
1246 * replacing the whole list
1247 * @param ctx An initialized TALLOC_CTX
1248 * @param mods An initialized ADS_MODLIST
1249 * @param name name of the ldap attribute to append to
1250 * @param vals an array of values to add
1251 * @return status of addition
1254 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1255 const char *name, const char **vals)
1257 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name, (const void **) vals);
1261 * Determines the computer account's current KVNO via an LDAP lookup
1262 * @param ads An initialized ADS_STRUCT
1263 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1264 * @return the kvno for the computer account, or -1 in case of a failure.
1267 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *machine_name)
1269 LDAPMessage *res = NULL;
1270 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1271 char *filter;
1272 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1273 char *dn_string = NULL;
1274 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1276 DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name));
1277 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1278 return kvno;
1280 ret = ads_search(ads, (void**)(void *)&res, filter, attrs);
1281 SAFE_FREE(filter);
1282 if (!ADS_ERR_OK(ret) && ads_count_replies(ads, res)) {
1283 DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name));
1284 ads_msgfree(ads, res);
1285 return kvno;
1288 dn_string = ads_get_dn(ads, res);
1289 if (!dn_string) {
1290 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1291 ads_msgfree(ads, res);
1292 return kvno;
1294 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1295 ads_memfree(ads, dn_string);
1297 /* ---------------------------------------------------------
1298 * 0 is returned as a default KVNO from this point on...
1299 * This is done because Windows 2000 does not support key
1300 * version numbers. Chances are that a failure in the next
1301 * step is simply due to Windows 2000 being used for a
1302 * domain controller. */
1303 kvno = 0;
1305 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1306 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1307 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1308 ads_msgfree(ads, res);
1309 return kvno;
1312 /* Success */
1313 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1314 ads_msgfree(ads, res);
1315 return kvno;
1319 * This clears out all registered spn's for a given hostname
1320 * @param ads An initilaized ADS_STRUCT
1321 * @param machine_name the NetBIOS name of the computer.
1322 * @return 0 upon success, non-zero otherwise.
1325 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1327 TALLOC_CTX *ctx;
1328 LDAPMessage *res = NULL;
1329 ADS_MODLIST mods;
1330 const char *servicePrincipalName[1] = {NULL};
1331 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1332 char *dn_string = NULL;
1334 ret = ads_find_machine_acct(ads, (void **)(void *)&res, machine_name);
1335 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1336 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1337 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1338 ads_msgfree(ads, res);
1339 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1342 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1343 ctx = talloc_init("ads_clear_service_principal_names");
1344 if (!ctx) {
1345 ads_msgfree(ads, res);
1346 return ADS_ERROR(LDAP_NO_MEMORY);
1349 if (!(mods = ads_init_mods(ctx))) {
1350 talloc_destroy(ctx);
1351 ads_msgfree(ads, res);
1352 return ADS_ERROR(LDAP_NO_MEMORY);
1354 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1355 if (!ADS_ERR_OK(ret)) {
1356 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1357 ads_msgfree(ads, res);
1358 talloc_destroy(ctx);
1359 return ret;
1361 dn_string = ads_get_dn(ads, res);
1362 if (!dn_string) {
1363 talloc_destroy(ctx);
1364 ads_msgfree(ads, res);
1365 return ADS_ERROR(LDAP_NO_MEMORY);
1367 ret = ads_gen_mod(ads, dn_string, mods);
1368 ads_memfree(ads,dn_string);
1369 if (!ADS_ERR_OK(ret)) {
1370 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1371 machine_name));
1372 ads_msgfree(ads, res);
1373 talloc_destroy(ctx);
1374 return ret;
1377 ads_msgfree(ads, res);
1378 talloc_destroy(ctx);
1379 return ret;
1383 * This adds a service principal name to an existing computer account
1384 * (found by hostname) in AD.
1385 * @param ads An initialized ADS_STRUCT
1386 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1387 * @param spn A string of the service principal to add, i.e. 'host'
1388 * @return 0 upon sucess, or non-zero if a failure occurs
1391 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name, const char *spn)
1393 ADS_STATUS ret;
1394 TALLOC_CTX *ctx;
1395 LDAPMessage *res = NULL;
1396 char *host_spn, *psp1, *psp2, *psp3;
1397 ADS_MODLIST mods;
1398 fstring my_fqdn;
1399 char *dn_string = NULL;
1400 const char *servicePrincipalName[4] = {NULL, NULL, NULL, NULL};
1402 ret = ads_find_machine_acct(ads, (void **)(void *)&res, machine_name);
1403 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1404 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1405 machine_name));
1406 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1407 spn, machine_name, ads->config.realm));
1408 ads_msgfree(ads, res);
1409 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1412 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1413 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1414 ads_msgfree(ads, res);
1415 return ADS_ERROR(LDAP_NO_MEMORY);
1418 name_to_fqdn(my_fqdn, machine_name);
1419 strlower_m(my_fqdn);
1421 if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", my_fqdn))) {
1422 talloc_destroy(ctx);
1423 ads_msgfree(ads, res);
1424 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1427 /* Add the extra principal */
1428 psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name);
1429 strupper_m(psp1);
1430 strlower_m(&psp1[strlen(spn)]);
1431 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp1, machine_name));
1432 servicePrincipalName[0] = psp1;
1433 psp2 = talloc_asprintf(ctx, "%s/%s.%s", spn, machine_name, ads->config.realm);
1434 strupper_m(psp2);
1435 strlower_m(&psp2[strlen(spn)]);
1436 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp2, machine_name));
1437 servicePrincipalName[1] = psp2;
1439 /* Add another principal in case the realm != the DNS domain, so that
1440 * the KDC doesn't send "server principal unknown" errors to clients
1441 * which use the DNS name in determining service principal names. */
1442 psp3 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn);
1443 strupper_m(psp3);
1444 strlower_m(&psp3[strlen(spn)]);
1445 if (strcmp(psp2, psp3) != 0) {
1446 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp3, machine_name));
1447 servicePrincipalName[2] = psp3;
1450 if (!(mods = ads_init_mods(ctx))) {
1451 talloc_destroy(ctx);
1452 ads_msgfree(ads, res);
1453 return ADS_ERROR(LDAP_NO_MEMORY);
1455 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1456 if (!ADS_ERR_OK(ret)) {
1457 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1458 talloc_destroy(ctx);
1459 ads_msgfree(ads, res);
1460 return ret;
1462 dn_string = ads_get_dn(ads, res);
1463 if (!dn_string) {
1464 talloc_destroy(ctx);
1465 ads_msgfree(ads, res);
1466 return ADS_ERROR(LDAP_NO_MEMORY);
1468 ret = ads_gen_mod(ads, dn_string, mods);
1469 ads_memfree(ads,dn_string);
1470 if (!ADS_ERR_OK(ret)) {
1471 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1472 talloc_destroy(ctx);
1473 ads_msgfree(ads, res);
1474 return ret;
1477 talloc_destroy(ctx);
1478 ads_msgfree(ads, res);
1479 return ret;
1483 * adds a machine account to the ADS server
1484 * @param ads An intialized ADS_STRUCT
1485 * @param machine_name - the NetBIOS machine name of this account.
1486 * @param account_type A number indicating the type of account to create
1487 * @param org_unit The LDAP path in which to place this account
1488 * @return 0 upon success, or non-zero otherwise
1491 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1492 const char *org_unit)
1494 ADS_STATUS ret;
1495 char *samAccountName, *controlstr;
1496 TALLOC_CTX *ctx;
1497 ADS_MODLIST mods;
1498 char *new_dn;
1499 const char *objectClass[] = {"top", "person", "organizationalPerson",
1500 "user", "computer", NULL};
1501 LDAPMessage *res = NULL;
1502 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
1503 UF_DONT_EXPIRE_PASSWD |\
1504 UF_ACCOUNTDISABLE );
1506 if (!(ctx = talloc_init("ads_add_machine_acct")))
1507 return ADS_ERROR(LDAP_NO_MEMORY);
1509 ret = ADS_ERROR(LDAP_NO_MEMORY);
1511 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_name, org_unit);
1512 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
1514 if ( !new_dn || !samAccountName ) {
1515 goto done;
1518 #ifndef ENCTYPE_ARCFOUR_HMAC
1519 acct_control |= UF_USE_DES_KEY_ONLY;
1520 #endif
1522 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
1523 goto done;
1526 if (!(mods = ads_init_mods(ctx))) {
1527 goto done;
1530 ads_mod_str(ctx, &mods, "cn", machine_name);
1531 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1532 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1533 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1535 ret = ads_gen_add(ads, new_dn, mods);
1537 done:
1538 ads_msgfree(ads, res);
1539 talloc_destroy(ctx);
1541 return ret;
1545 dump a binary result from ldap
1547 static void dump_binary(const char *field, struct berval **values)
1549 int i, j;
1550 for (i=0; values[i]; i++) {
1551 printf("%s: ", field);
1552 for (j=0; j<values[i]->bv_len; j++) {
1553 printf("%02X", (unsigned char)values[i]->bv_val[j]);
1555 printf("\n");
1559 static void dump_guid(const char *field, struct berval **values)
1561 int i;
1562 UUID_FLAT guid;
1563 for (i=0; values[i]; i++) {
1564 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1565 printf("%s: %s\n", field,
1566 smb_uuid_string_static(smb_uuid_unpack_static(guid)));
1571 dump a sid result from ldap
1573 static void dump_sid(const char *field, struct berval **values)
1575 int i;
1576 for (i=0; values[i]; i++) {
1577 DOM_SID sid;
1578 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1579 printf("%s: %s\n", field, sid_string_static(&sid));
1584 dump ntSecurityDescriptor
1586 static void dump_sd(const char *filed, struct berval **values)
1588 prs_struct ps;
1590 SEC_DESC *psd = 0;
1591 TALLOC_CTX *ctx = 0;
1593 if (!(ctx = talloc_init("sec_io_desc")))
1594 return;
1596 /* prepare data */
1597 prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
1598 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
1599 prs_set_offset(&ps,0);
1601 /* parse secdesc */
1602 if (!sec_io_desc("sd", &psd, &ps, 1)) {
1603 prs_mem_free(&ps);
1604 talloc_destroy(ctx);
1605 return;
1607 if (psd) ads_disp_sd(psd);
1609 prs_mem_free(&ps);
1610 talloc_destroy(ctx);
1614 dump a string result from ldap
1616 static void dump_string(const char *field, char **values)
1618 int i;
1619 for (i=0; values[i]; i++) {
1620 printf("%s: %s\n", field, values[i]);
1625 dump a field from LDAP on stdout
1626 used for debugging
1629 static BOOL ads_dump_field(char *field, void **values, void *data_area)
1631 const struct {
1632 const char *name;
1633 BOOL string;
1634 void (*handler)(const char *, struct berval **);
1635 } handlers[] = {
1636 {"objectGUID", False, dump_guid},
1637 {"netbootGUID", False, dump_guid},
1638 {"nTSecurityDescriptor", False, dump_sd},
1639 {"dnsRecord", False, dump_binary},
1640 {"objectSid", False, dump_sid},
1641 {"tokenGroups", False, dump_sid},
1642 {"tokenGroupsNoGCAcceptable", False, dump_sid},
1643 {"tokengroupsGlobalandUniversal", False, dump_sid},
1644 {NULL, True, NULL}
1646 int i;
1648 if (!field) { /* must be end of an entry */
1649 printf("\n");
1650 return False;
1653 for (i=0; handlers[i].name; i++) {
1654 if (StrCaseCmp(handlers[i].name, field) == 0) {
1655 if (!values) /* first time, indicate string or not */
1656 return handlers[i].string;
1657 handlers[i].handler(field, (struct berval **) values);
1658 break;
1661 if (!handlers[i].name) {
1662 if (!values) /* first time, indicate string conversion */
1663 return True;
1664 dump_string(field, (char **)values);
1666 return False;
1670 * Dump a result from LDAP on stdout
1671 * used for debugging
1672 * @param ads connection to ads server
1673 * @param res Results to dump
1676 void ads_dump(ADS_STRUCT *ads, void *res)
1678 ads_process_results(ads, res, ads_dump_field, NULL);
1682 * Walk through results, calling a function for each entry found.
1683 * The function receives a field name, a berval * array of values,
1684 * and a data area passed through from the start. The function is
1685 * called once with null for field and values at the end of each
1686 * entry.
1687 * @param ads connection to ads server
1688 * @param res Results to process
1689 * @param fn Function for processing each result
1690 * @param data_area user-defined area to pass to function
1692 void ads_process_results(ADS_STRUCT *ads, void *res,
1693 BOOL(*fn)(char *, void **, void *),
1694 void *data_area)
1696 void *msg;
1697 TALLOC_CTX *ctx;
1699 if (!(ctx = talloc_init("ads_process_results")))
1700 return;
1702 for (msg = ads_first_entry(ads, res); msg;
1703 msg = ads_next_entry(ads, msg)) {
1704 char *utf8_field;
1705 BerElement *b;
1707 for (utf8_field=ldap_first_attribute(ads->ld,
1708 (LDAPMessage *)msg,&b);
1709 utf8_field;
1710 utf8_field=ldap_next_attribute(ads->ld,
1711 (LDAPMessage *)msg,b)) {
1712 struct berval **ber_vals;
1713 char **str_vals, **utf8_vals;
1714 char *field;
1715 BOOL string;
1717 pull_utf8_talloc(ctx, &field, utf8_field);
1718 string = fn(field, NULL, data_area);
1720 if (string) {
1721 utf8_vals = ldap_get_values(ads->ld,
1722 (LDAPMessage *)msg, field);
1723 str_vals = ads_pull_strvals(ctx,
1724 (const char **) utf8_vals);
1725 fn(field, (void **) str_vals, data_area);
1726 ldap_value_free(utf8_vals);
1727 } else {
1728 ber_vals = ldap_get_values_len(ads->ld,
1729 (LDAPMessage *)msg, field);
1730 fn(field, (void **) ber_vals, data_area);
1732 ldap_value_free_len(ber_vals);
1734 ldap_memfree(utf8_field);
1736 ber_free(b, 0);
1737 talloc_free_children(ctx);
1738 fn(NULL, NULL, data_area); /* completed an entry */
1741 talloc_destroy(ctx);
1745 * count how many replies are in a LDAPMessage
1746 * @param ads connection to ads server
1747 * @param res Results to count
1748 * @return number of replies
1750 int ads_count_replies(ADS_STRUCT *ads, void *res)
1752 return ldap_count_entries(ads->ld, (LDAPMessage *)res);
1756 * pull the first entry from a ADS result
1757 * @param ads connection to ads server
1758 * @param res Results of search
1759 * @return first entry from result
1761 void *ads_first_entry(ADS_STRUCT *ads, void *res)
1763 return (void *)ldap_first_entry(ads->ld, (LDAPMessage *)res);
1767 * pull the next entry from a ADS result
1768 * @param ads connection to ads server
1769 * @param res Results of search
1770 * @return next entry from result
1772 void *ads_next_entry(ADS_STRUCT *ads, void *res)
1774 return (void *)ldap_next_entry(ads->ld, (LDAPMessage *)res);
1778 * pull a single string from a ADS result
1779 * @param ads connection to ads server
1780 * @param mem_ctx TALLOC_CTX to use for allocating result string
1781 * @param msg Results of search
1782 * @param field Attribute to retrieve
1783 * @return Result string in talloc context
1785 char *ads_pull_string(ADS_STRUCT *ads,
1786 TALLOC_CTX *mem_ctx, void *msg, const char *field)
1788 char **values;
1789 char *ret = NULL;
1790 char *ux_string;
1791 size_t rc;
1793 values = ldap_get_values(ads->ld, msg, field);
1794 if (!values)
1795 return NULL;
1797 if (values[0]) {
1798 rc = pull_utf8_talloc(mem_ctx, &ux_string,
1799 values[0]);
1800 if (rc != (size_t)-1)
1801 ret = ux_string;
1804 ldap_value_free(values);
1805 return ret;
1809 * pull an array of strings from a ADS result
1810 * @param ads connection to ads server
1811 * @param mem_ctx TALLOC_CTX to use for allocating result string
1812 * @param msg Results of search
1813 * @param field Attribute to retrieve
1814 * @return Result strings in talloc context
1816 char **ads_pull_strings(ADS_STRUCT *ads,
1817 TALLOC_CTX *mem_ctx, void *msg, const char *field,
1818 size_t *num_values)
1820 char **values;
1821 char **ret = NULL;
1822 int i;
1824 values = ldap_get_values(ads->ld, msg, field);
1825 if (!values)
1826 return NULL;
1828 *num_values = ldap_count_values(values);
1830 ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
1831 if (!ret) {
1832 ldap_value_free(values);
1833 return NULL;
1836 for (i=0;i<*num_values;i++) {
1837 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
1838 ldap_value_free(values);
1839 return NULL;
1842 ret[i] = NULL;
1844 ldap_value_free(values);
1845 return ret;
1849 * pull an array of strings from a ADS result
1850 * (handle large multivalue attributes with range retrieval)
1851 * @param ads connection to ads server
1852 * @param mem_ctx TALLOC_CTX to use for allocating result string
1853 * @param msg Results of search
1854 * @param field Attribute to retrieve
1855 * @param current_strings strings returned by a previous call to this function
1856 * @param next_attribute The next query should ask for this attribute
1857 * @param num_values How many values did we get this time?
1858 * @param more_values Are there more values to get?
1859 * @return Result strings in talloc context
1861 char **ads_pull_strings_range(ADS_STRUCT *ads,
1862 TALLOC_CTX *mem_ctx,
1863 void *msg, const char *field,
1864 char **current_strings,
1865 const char **next_attribute,
1866 size_t *num_strings,
1867 BOOL *more_strings)
1869 char *attr;
1870 char *expected_range_attrib, *range_attr;
1871 BerElement *ptr = NULL;
1872 char **strings;
1873 char **new_strings;
1874 size_t num_new_strings;
1875 unsigned long int range_start;
1876 unsigned long int range_end;
1878 /* we might have been given the whole lot anyway */
1879 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
1880 *more_strings = False;
1881 return strings;
1884 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
1886 /* look for Range result */
1887 for (attr = ldap_first_attribute(ads->ld, (LDAPMessage *)msg, &ptr);
1888 attr;
1889 attr = ldap_next_attribute(ads->ld, (LDAPMessage *)msg, ptr)) {
1890 /* we ignore the fact that this is utf8, as all attributes are ascii... */
1891 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
1892 range_attr = attr;
1893 break;
1895 ldap_memfree(attr);
1897 if (!attr) {
1898 ber_free(ptr, 0);
1899 /* nothing here - this field is just empty */
1900 *more_strings = False;
1901 return NULL;
1904 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
1905 &range_start, &range_end) == 2) {
1906 *more_strings = True;
1907 } else {
1908 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
1909 &range_start) == 1) {
1910 *more_strings = False;
1911 } else {
1912 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
1913 range_attr));
1914 ldap_memfree(range_attr);
1915 *more_strings = False;
1916 return NULL;
1920 if ((*num_strings) != range_start) {
1921 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
1922 " - aborting range retreival\n",
1923 range_attr, (unsigned int)(*num_strings) + 1, range_start));
1924 ldap_memfree(range_attr);
1925 *more_strings = False;
1926 return NULL;
1929 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
1931 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
1932 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
1933 "strings in this bunch, but we only got %lu - aborting range retreival\n",
1934 range_attr, (unsigned long int)range_end - range_start + 1,
1935 (unsigned long int)num_new_strings));
1936 ldap_memfree(range_attr);
1937 *more_strings = False;
1938 return NULL;
1941 strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
1942 *num_strings + num_new_strings);
1944 if (strings == NULL) {
1945 ldap_memfree(range_attr);
1946 *more_strings = False;
1947 return NULL;
1950 memcpy(&strings[*num_strings], new_strings,
1951 sizeof(*new_strings) * num_new_strings);
1953 (*num_strings) += num_new_strings;
1955 if (*more_strings) {
1956 *next_attribute = talloc_asprintf(mem_ctx,
1957 "%s;range=%d-*",
1958 field,
1959 (int)*num_strings);
1961 if (!*next_attribute) {
1962 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
1963 ldap_memfree(range_attr);
1964 *more_strings = False;
1965 return NULL;
1969 ldap_memfree(range_attr);
1971 return strings;
1975 * pull a single uint32 from a ADS result
1976 * @param ads connection to ads server
1977 * @param msg Results of search
1978 * @param field Attribute to retrieve
1979 * @param v Pointer to int to store result
1980 * @return boolean inidicating success
1982 BOOL ads_pull_uint32(ADS_STRUCT *ads,
1983 void *msg, const char *field, uint32 *v)
1985 char **values;
1987 values = ldap_get_values(ads->ld, msg, field);
1988 if (!values)
1989 return False;
1990 if (!values[0]) {
1991 ldap_value_free(values);
1992 return False;
1995 *v = atoi(values[0]);
1996 ldap_value_free(values);
1997 return True;
2001 * pull a single objectGUID from an ADS result
2002 * @param ads connection to ADS server
2003 * @param msg results of search
2004 * @param guid 37-byte area to receive text guid
2005 * @return boolean indicating success
2007 BOOL ads_pull_guid(ADS_STRUCT *ads,
2008 void *msg, struct uuid *guid)
2010 char **values;
2011 UUID_FLAT flat_guid;
2013 values = ldap_get_values(ads->ld, msg, "objectGUID");
2014 if (!values)
2015 return False;
2017 if (values[0]) {
2018 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2019 smb_uuid_unpack(flat_guid, guid);
2020 ldap_value_free(values);
2021 return True;
2023 ldap_value_free(values);
2024 return False;
2030 * pull a single DOM_SID from a ADS result
2031 * @param ads connection to ads server
2032 * @param msg Results of search
2033 * @param field Attribute to retrieve
2034 * @param sid Pointer to sid to store result
2035 * @return boolean inidicating success
2037 BOOL ads_pull_sid(ADS_STRUCT *ads,
2038 void *msg, const char *field, DOM_SID *sid)
2040 struct berval **values;
2041 BOOL ret = False;
2043 values = ldap_get_values_len(ads->ld, msg, field);
2045 if (!values)
2046 return False;
2048 if (values[0])
2049 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2051 ldap_value_free_len(values);
2052 return ret;
2056 * pull an array of DOM_SIDs from a ADS result
2057 * @param ads connection to ads server
2058 * @param mem_ctx TALLOC_CTX for allocating sid array
2059 * @param msg Results of search
2060 * @param field Attribute to retrieve
2061 * @param sids pointer to sid array to allocate
2062 * @return the count of SIDs pulled
2064 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2065 void *msg, const char *field, DOM_SID **sids)
2067 struct berval **values;
2068 BOOL ret;
2069 int count, i;
2071 values = ldap_get_values_len(ads->ld, msg, field);
2073 if (!values)
2074 return 0;
2076 for (i=0; values[i]; i++)
2077 /* nop */ ;
2079 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2080 if (!(*sids)) {
2081 ldap_value_free_len(values);
2082 return 0;
2085 count = 0;
2086 for (i=0; values[i]; i++) {
2087 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2088 if (ret) {
2089 fstring sid;
2090 DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid, &(*sids)[count])));
2091 count++;
2095 ldap_value_free_len(values);
2096 return count;
2100 * pull a SEC_DESC from a ADS result
2101 * @param ads connection to ads server
2102 * @param mem_ctx TALLOC_CTX for allocating sid array
2103 * @param msg Results of search
2104 * @param field Attribute to retrieve
2105 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2106 * @return boolean inidicating success
2108 BOOL ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2109 void *msg, const char *field, SEC_DESC **sd)
2111 struct berval **values;
2112 prs_struct ps;
2113 BOOL ret = False;
2115 values = ldap_get_values_len(ads->ld, msg, field);
2117 if (!values) return False;
2119 if (values[0]) {
2120 prs_init(&ps, values[0]->bv_len, mem_ctx, UNMARSHALL);
2121 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
2122 prs_set_offset(&ps,0);
2124 ret = sec_io_desc("sd", sd, &ps, 1);
2127 ldap_value_free_len(values);
2128 return ret;
2132 * in order to support usernames longer than 21 characters we need to
2133 * use both the sAMAccountName and the userPrincipalName attributes
2134 * It seems that not all users have the userPrincipalName attribute set
2136 * @param ads connection to ads server
2137 * @param mem_ctx TALLOC_CTX for allocating sid array
2138 * @param msg Results of search
2139 * @return the username
2141 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, void *msg)
2143 #if 0 /* JERRY */
2144 char *ret, *p;
2146 /* lookup_name() only works on the sAMAccountName to
2147 returning the username portion of userPrincipalName
2148 breaks winbindd_getpwnam() */
2150 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2151 if (ret && (p = strchr_m(ret, '@'))) {
2152 *p = 0;
2153 return ret;
2155 #endif
2156 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2161 * find the update serial number - this is the core of the ldap cache
2162 * @param ads connection to ads server
2163 * @param ads connection to ADS server
2164 * @param usn Pointer to retrieved update serial number
2165 * @return status of search
2167 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2169 const char *attrs[] = {"highestCommittedUSN", NULL};
2170 ADS_STATUS status;
2171 void *res;
2173 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2174 if (!ADS_ERR_OK(status))
2175 return status;
2177 if (ads_count_replies(ads, res) != 1) {
2178 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2181 ads_pull_uint32(ads, res, "highestCommittedUSN", usn);
2182 ads_msgfree(ads, res);
2183 return ADS_SUCCESS;
2186 /* parse a ADS timestring - typical string is
2187 '20020917091222.0Z0' which means 09:12.22 17th September
2188 2002, timezone 0 */
2189 static time_t ads_parse_time(const char *str)
2191 struct tm tm;
2193 ZERO_STRUCT(tm);
2195 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2196 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2197 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2198 return 0;
2200 tm.tm_year -= 1900;
2201 tm.tm_mon -= 1;
2203 return timegm(&tm);
2207 * Find the servers name and realm - this can be done before authentication
2208 * The ldapServiceName field on w2k looks like this:
2209 * vnet3.home.samba.org:win2000-vnet3$@VNET3.HOME.SAMBA.ORG
2210 * @param ads connection to ads server
2211 * @return status of search
2213 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2215 const char *attrs[] = {"currentTime", NULL};
2216 ADS_STATUS status;
2217 void *res;
2218 char *timestr;
2219 TALLOC_CTX *ctx;
2220 ADS_STRUCT *ads_s = ads;
2222 if (!(ctx = talloc_init("ads_server_info"))) {
2223 return ADS_ERROR(LDAP_NO_MEMORY);
2226 /* establish a new ldap tcp session if necessary */
2228 if ( !ads->ld ) {
2229 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2230 ads->server.ldap_server )) == NULL )
2232 goto done;
2234 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2235 status = ads_connect( ads_s );
2236 if ( !ADS_ERR_OK(status))
2237 goto done;
2240 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2241 if (!ADS_ERR_OK(status)) {
2242 talloc_destroy(ctx);
2243 goto done;
2246 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2247 if (!timestr) {
2248 ads_msgfree(ads, res);
2249 talloc_destroy(ctx);
2250 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2251 goto done;
2254 /* but save the time and offset in the original ADS_STRUCT */
2256 ads->config.current_time = ads_parse_time(timestr);
2258 if (ads->config.current_time != 0) {
2259 ads->auth.time_offset = ads->config.current_time - time(NULL);
2260 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2263 ads_msgfree(ads, res);
2265 status = ADS_SUCCESS;
2267 done:
2268 /* free any temporary ads connections */
2269 if ( ads_s != ads ) {
2270 ads_destroy( &ads_s );
2272 talloc_destroy(ctx);
2274 return status;
2278 * find the domain sid for our domain
2279 * @param ads connection to ads server
2280 * @param sid Pointer to domain sid
2281 * @return status of search
2283 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2285 const char *attrs[] = {"objectSid", NULL};
2286 void *res;
2287 ADS_STATUS rc;
2289 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2290 attrs, &res);
2291 if (!ADS_ERR_OK(rc)) return rc;
2292 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2293 ads_msgfree(ads, res);
2294 return ADS_ERROR_SYSTEM(ENOENT);
2296 ads_msgfree(ads, res);
2298 return ADS_SUCCESS;
2302 * find our site name
2303 * @param ads connection to ads server
2304 * @param mem_ctx Pointer to talloc context
2305 * @param site_name Pointer to the sitename
2306 * @return status of search
2308 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2310 ADS_STATUS status;
2311 void *res;
2312 const char *dn, *service_name;
2313 const char *attrs[] = { "dsServiceName", NULL };
2315 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2316 if (!ADS_ERR_OK(status)) {
2317 return status;
2320 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2321 if (service_name == NULL) {
2322 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2325 /* go up three levels */
2326 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2327 if (dn == NULL) {
2328 return ADS_ERROR(LDAP_NO_MEMORY);
2331 *site_name = talloc_strdup(mem_ctx, dn);
2332 if (*site_name == NULL) {
2333 return ADS_ERROR(LDAP_NO_MEMORY);
2336 ads_msgfree(ads, res);
2338 return status;
2340 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2345 * find the site dn where a machine resides
2346 * @param ads connection to ads server
2347 * @param mem_ctx Pointer to talloc context
2348 * @param computer_name name of the machine
2349 * @param site_name Pointer to the sitename
2350 * @return status of search
2352 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
2354 ADS_STATUS status;
2355 void *res;
2356 const char *parent, *config_context, *filter;
2357 const char *attrs[] = { "configurationNamingContext", NULL };
2358 char *dn;
2360 /* shortcut a query */
2361 if (strequal(computer_name, ads->config.ldap_server_name)) {
2362 return ads_site_dn(ads, mem_ctx, site_dn);
2365 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2366 if (!ADS_ERR_OK(status)) {
2367 return status;
2370 config_context = ads_pull_string(ads, mem_ctx, res, "configurationNamingContext");
2371 if (config_context == NULL) {
2372 return ADS_ERROR(LDAP_NO_MEMORY);
2375 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
2376 if (filter == NULL) {
2377 return ADS_ERROR(LDAP_NO_MEMORY);
2380 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE, filter, NULL, &res);
2381 if (!ADS_ERR_OK(status)) {
2382 return status;
2385 if (ads_count_replies(ads, res) != 1) {
2386 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2389 dn = ads_get_dn(ads, res);
2390 if (dn == NULL) {
2391 return ADS_ERROR(LDAP_NO_MEMORY);
2394 /* go up three levels */
2395 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
2396 if (parent == NULL) {
2397 ads_memfree(ads, dn);
2398 return ADS_ERROR(LDAP_NO_MEMORY);
2401 *site_dn = talloc_strdup(mem_ctx, parent);
2402 if (*site_dn == NULL) {
2403 ads_memfree(ads, dn);
2404 ADS_ERROR(LDAP_NO_MEMORY);
2407 ads_memfree(ads, dn);
2408 ads_msgfree(ads, res);
2410 return status;
2414 * get the upn suffixes for a domain
2415 * @param ads connection to ads server
2416 * @param mem_ctx Pointer to talloc context
2417 * @param suffixes Pointer to an array of suffixes
2418 * @param site_name Pointer to the number of suffixes
2419 * @return status of search
2421 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char **suffixes, size_t *num_suffixes)
2423 ADS_STATUS status;
2424 void *res;
2425 const char *config_context, *base;
2426 const char *attrs[] = { "configurationNamingContext", NULL };
2427 const char *attrs2[] = { "uPNSuffixes", NULL };
2429 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2430 if (!ADS_ERR_OK(status)) {
2431 return status;
2434 config_context = ads_pull_string(ads, mem_ctx, res, "configurationNamingContext");
2435 if (config_context == NULL) {
2436 return ADS_ERROR(LDAP_NO_MEMORY);
2439 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
2440 if (base == NULL) {
2441 return ADS_ERROR(LDAP_NO_MEMORY);
2444 status = ads_search_dn(ads, &res, base, attrs2);
2445 if (!ADS_ERR_OK(status)) {
2446 return status;
2449 if (ads_count_replies(ads, res) != 1) {
2450 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2453 suffixes = ads_pull_strings(ads, mem_ctx, &res, "uPNSuffixes", num_suffixes);
2454 if (suffixes == NULL) {
2455 ads_msgfree(ads, res);
2456 return ADS_ERROR(LDAP_NO_MEMORY);
2459 ads_msgfree(ads, res);
2461 return status;
2465 * pull a DOM_SID from an extended dn string
2466 * @param mem_ctx TALLOC_CTX
2467 * @param flags string type of extended_dn
2468 * @param sid pointer to a DOM_SID
2469 * @return boolean inidicating success
2471 BOOL ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
2472 const char *dn,
2473 enum ads_extended_dn_flags flags,
2474 DOM_SID *sid)
2476 char *p, *q;
2478 if (!dn) {
2479 return False;
2483 * ADS_EXTENDED_DN_HEX_STRING:
2484 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
2486 * ADS_EXTENDED_DN_STRING (only with w2k3):
2487 <GUID=63198e23-39cb-4b0f-b032-ba0105525a29>;<SID=S-1-5-21-4257769659-666132843-1169174103-1110>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
2490 p = strchr(dn, ';');
2491 if (!p) {
2492 return False;
2495 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
2496 return False;
2499 p += strlen(";<SID=");
2501 q = strchr(p, '>');
2502 if (!q) {
2503 return False;
2506 *q = '\0';
2508 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
2510 switch (flags) {
2512 case ADS_EXTENDED_DN_STRING:
2513 if (!string_to_sid(sid, p)) {
2514 return False;
2516 break;
2517 case ADS_EXTENDED_DN_HEX_STRING: {
2518 pstring buf;
2519 size_t buf_len;
2521 buf_len = strhex_to_str(buf, strlen(p), p);
2522 if (buf_len == 0) {
2523 return False;
2526 if (!sid_parse(buf, buf_len, sid)) {
2527 DEBUG(10,("failed to parse sid\n"));
2528 return False;
2530 break;
2532 default:
2533 DEBUG(10,("unknown extended dn format\n"));
2534 return False;
2537 return True;
2541 * pull an array of DOM_SIDs from a ADS result
2542 * @param ads connection to ads server
2543 * @param mem_ctx TALLOC_CTX for allocating sid array
2544 * @param msg Results of search
2545 * @param field Attribute to retrieve
2546 * @param flags string type of extended_dn
2547 * @param sids pointer to sid array to allocate
2548 * @return the count of SIDs pulled
2550 int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads,
2551 TALLOC_CTX *mem_ctx,
2552 void *msg,
2553 const char *field,
2554 enum ads_extended_dn_flags flags,
2555 DOM_SID **sids)
2557 int i;
2558 size_t dn_count;
2559 char **dn_strings;
2561 if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field,
2562 &dn_count)) == NULL) {
2563 return 0;
2566 (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, dn_count + 1);
2567 if (!(*sids)) {
2568 TALLOC_FREE(dn_strings);
2569 return 0;
2572 for (i=0; i<dn_count; i++) {
2574 if (!ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i],
2575 flags, &(*sids)[i])) {
2576 TALLOC_FREE(*sids);
2577 TALLOC_FREE(dn_strings);
2578 return 0;
2582 TALLOC_FREE(dn_strings);
2584 return dn_count;
2587 #endif