r16322: Klocwork #481., Don't deref null on malloc fail.
[Samba/gebeck_regimport.git] / source / libads / ldap.c
blobbe15643ba2f01ec8952bac1de55b283b73377c48
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 Yes it is :)
293 - Guenther
296 asprintf(&ads->auth.user_name, "%s$", global_myname() );
299 if (!ads->auth.realm) {
300 ads->auth.realm = SMB_STRDUP(ads->config.realm);
303 if (!ads->auth.kdc_server) {
304 ads->auth.kdc_server = SMB_STRDUP(inet_ntoa(ads->ldap_ip));
307 #if KRB5_DNS_HACK
308 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
309 to MIT kerberos to work (tridge) */
311 char *env;
312 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
313 setenv(env, ads->auth.kdc_server, 1);
314 free(env);
316 #endif
318 /* If the caller() requested no LDAP bind, then we are done */
320 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
321 return ADS_SUCCESS;
324 /* Otherwise setup the TCP LDAP session */
326 if ( (ads->ld = ldap_open_with_timeout(ads->config.ldap_server_name,
327 LDAP_PORT, lp_ldap_timeout())) == NULL )
329 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
331 ldap_set_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
333 status = ADS_ERROR(smb_ldap_start_tls(ads->ld, version));
334 if (!ADS_ERR_OK(status)) {
335 return status;
338 /* fill in the current time and offsets */
340 status = ads_current_time( ads );
341 if ( !ADS_ERR_OK(status) ) {
342 return status;
345 /* Now do the bind */
347 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
348 return ADS_ERROR(ldap_simple_bind_s( ads->ld, NULL, NULL));
351 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
352 return ADS_ERROR(ldap_simple_bind_s( ads->ld, ads->auth.user_name, ads->auth.password));
355 return ads_sasl_bind(ads);
359 Duplicate a struct berval into talloc'ed memory
361 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
363 struct berval *value;
365 if (!in_val) return NULL;
367 value = TALLOC_ZERO_P(ctx, struct berval);
368 if (value == NULL)
369 return NULL;
370 if (in_val->bv_len == 0) return value;
372 value->bv_len = in_val->bv_len;
373 value->bv_val = TALLOC_MEMDUP(ctx, in_val->bv_val, in_val->bv_len);
374 return value;
378 Make a values list out of an array of (struct berval *)
380 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
381 const struct berval **in_vals)
383 struct berval **values;
384 int i;
386 if (!in_vals) return NULL;
387 for (i=0; in_vals[i]; i++)
388 ; /* count values */
389 values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
390 if (!values) return NULL;
392 for (i=0; in_vals[i]; i++) {
393 values[i] = dup_berval(ctx, in_vals[i]);
395 return values;
399 UTF8-encode a values list out of an array of (char *)
401 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
403 char **values;
404 int i;
406 if (!in_vals) return NULL;
407 for (i=0; in_vals[i]; i++)
408 ; /* count values */
409 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
410 if (!values) return NULL;
412 for (i=0; in_vals[i]; i++) {
413 push_utf8_talloc(ctx, &values[i], in_vals[i]);
415 return values;
419 Pull a (char *) array out of a UTF8-encoded values list
421 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
423 char **values;
424 int i;
426 if (!in_vals) return NULL;
427 for (i=0; in_vals[i]; i++)
428 ; /* count values */
429 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
430 if (!values) return NULL;
432 for (i=0; in_vals[i]; i++) {
433 pull_utf8_talloc(ctx, &values[i], in_vals[i]);
435 return values;
439 * Do a search with paged results. cookie must be null on the first
440 * call, and then returned on each subsequent call. It will be null
441 * again when the entire search is complete
442 * @param ads connection to ads server
443 * @param bind_path Base dn for the search
444 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
445 * @param expr Search expression - specified in local charset
446 * @param attrs Attributes to retrieve - specified in utf8 or ascii
447 * @param res ** which will contain results - free res* with ads_msgfree()
448 * @param count Number of entries retrieved on this page
449 * @param cookie The paged results cookie to be returned on subsequent calls
450 * @return status of search
452 ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads, const char *bind_path,
453 int scope, const char *expr,
454 const char **attrs, void *args, void **res,
455 int *count, void **cookie)
457 int rc, i, version;
458 char *utf8_expr, *utf8_path, **search_attrs;
459 LDAPControl PagedResults, NoReferrals, ExtendedDn, *controls[4], **rcontrols;
460 BerElement *cookie_be = NULL;
461 struct berval *cookie_bv= NULL;
462 BerElement *extdn_be = NULL;
463 struct berval *extdn_bv= NULL;
465 TALLOC_CTX *ctx;
466 ads_control *external_control = (ads_control *) args;
468 *res = NULL;
470 if (!(ctx = talloc_init("ads_do_paged_search_args")))
471 return ADS_ERROR(LDAP_NO_MEMORY);
473 /* 0 means the conversion worked but the result was empty
474 so we only fail if it's -1. In any case, it always
475 at least nulls out the dest */
476 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
477 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
478 rc = LDAP_NO_MEMORY;
479 goto done;
482 if (!attrs || !(*attrs))
483 search_attrs = NULL;
484 else {
485 /* This would be the utf8-encoded version...*/
486 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
487 if (!(str_list_copy(&search_attrs, attrs))) {
488 rc = LDAP_NO_MEMORY;
489 goto done;
494 /* Paged results only available on ldap v3 or later */
495 ldap_get_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
496 if (version < LDAP_VERSION3) {
497 rc = LDAP_NOT_SUPPORTED;
498 goto done;
501 cookie_be = ber_alloc_t(LBER_USE_DER);
502 if (cookie && *cookie) {
503 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
504 ber_bvfree(*cookie); /* don't need it from last time */
505 *cookie = NULL;
506 } else {
507 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
509 ber_flatten(cookie_be, &cookie_bv);
510 PagedResults.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID);
511 PagedResults.ldctl_iscritical = (char) 1;
512 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
513 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
515 NoReferrals.ldctl_oid = CONST_DISCARD(char *, ADS_NO_REFERRALS_OID);
516 NoReferrals.ldctl_iscritical = (char) 0;
517 NoReferrals.ldctl_value.bv_len = 0;
518 NoReferrals.ldctl_value.bv_val = CONST_DISCARD(char *, "");
520 if (external_control && strequal(external_control->control, ADS_EXTENDED_DN_OID)) {
522 ExtendedDn.ldctl_oid = CONST_DISCARD(char *, external_control->control);
523 ExtendedDn.ldctl_iscritical = (char) external_control->critical;
525 /* win2k does not accept a ldctl_value beeing passed in */
527 if (external_control->val != 0) {
529 if ((extdn_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
530 rc = LDAP_NO_MEMORY;
531 goto done;
534 if ((ber_printf(extdn_be, "{i}", (ber_int_t) external_control->val)) == -1) {
535 rc = LDAP_NO_MEMORY;
536 goto done;
538 if ((ber_flatten(extdn_be, &extdn_bv)) == -1) {
539 rc = LDAP_NO_MEMORY;
540 goto done;
543 ExtendedDn.ldctl_value.bv_len = extdn_bv->bv_len;
544 ExtendedDn.ldctl_value.bv_val = extdn_bv->bv_val;
546 } else {
547 ExtendedDn.ldctl_value.bv_len = 0;
548 ExtendedDn.ldctl_value.bv_val = NULL;
551 controls[0] = &NoReferrals;
552 controls[1] = &PagedResults;
553 controls[2] = &ExtendedDn;
554 controls[3] = NULL;
556 } else {
557 controls[0] = &NoReferrals;
558 controls[1] = &PagedResults;
559 controls[2] = NULL;
562 /* we need to disable referrals as the openldap libs don't
563 handle them and paged results at the same time. Using them
564 together results in the result record containing the server
565 page control being removed from the result list (tridge/jmcd)
567 leaving this in despite the control that says don't generate
568 referrals, in case the server doesn't support it (jmcd)
570 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
572 rc = ldap_search_with_timeout(ads->ld, utf8_path, scope, utf8_expr,
573 search_attrs, 0, controls,
574 NULL, LDAP_NO_LIMIT,
575 (LDAPMessage **)res);
577 ber_free(cookie_be, 1);
578 ber_bvfree(cookie_bv);
580 if (rc) {
581 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
582 ldap_err2string(rc)));
583 goto done;
586 rc = ldap_parse_result(ads->ld, *res, NULL, NULL, NULL,
587 NULL, &rcontrols, 0);
589 if (!rcontrols) {
590 goto done;
593 for (i=0; rcontrols[i]; i++) {
594 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
595 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
596 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
597 &cookie_bv);
598 /* the berval is the cookie, but must be freed when
599 it is all done */
600 if (cookie_bv->bv_len) /* still more to do */
601 *cookie=ber_bvdup(cookie_bv);
602 else
603 *cookie=NULL;
604 ber_bvfree(cookie_bv);
605 ber_free(cookie_be, 1);
606 break;
609 ldap_controls_free(rcontrols);
611 done:
612 talloc_destroy(ctx);
614 if (extdn_be) {
615 ber_free(extdn_be, 1);
618 if (extdn_bv) {
619 ber_bvfree(extdn_bv);
622 /* if/when we decide to utf8-encode attrs, take out this next line */
623 str_list_free(&search_attrs);
625 return ADS_ERROR(rc);
628 ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
629 int scope, const char *expr,
630 const char **attrs, void **res,
631 int *count, void **cookie)
633 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
638 * Get all results for a search. This uses ads_do_paged_search() to return
639 * all entries in a large search.
640 * @param ads connection to ads server
641 * @param bind_path Base dn for the search
642 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
643 * @param expr Search expression
644 * @param attrs Attributes to retrieve
645 * @param res ** which will contain results - free res* with ads_msgfree()
646 * @return status of search
648 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
649 int scope, const char *expr,
650 const char **attrs, void *args, void **res)
652 void *cookie = NULL;
653 int count = 0;
654 ADS_STATUS status;
656 *res = NULL;
657 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
658 &count, &cookie);
660 if (!ADS_ERR_OK(status))
661 return status;
663 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
664 while (cookie) {
665 void *res2 = NULL;
666 ADS_STATUS status2;
667 LDAPMessage *msg, *next;
669 status2 = ads_do_paged_search_args(ads, bind_path, scope, expr,
670 attrs, args, &res2, &count, &cookie);
672 if (!ADS_ERR_OK(status2)) break;
674 /* this relies on the way that ldap_add_result_entry() works internally. I hope
675 that this works on all ldap libs, but I have only tested with openldap */
676 for (msg = ads_first_entry(ads, res2); msg; msg = next) {
677 next = ads_next_entry(ads, msg);
678 ldap_add_result_entry((LDAPMessage **)res, msg);
680 /* note that we do not free res2, as the memory is now
681 part of the main returned list */
683 #else
684 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
685 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
686 #endif
688 return status;
691 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
692 int scope, const char *expr,
693 const char **attrs, void **res)
695 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
699 * Run a function on all results for a search. Uses ads_do_paged_search() and
700 * runs the function as each page is returned, using ads_process_results()
701 * @param ads connection to ads server
702 * @param bind_path Base dn for the search
703 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
704 * @param expr Search expression - specified in local charset
705 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
706 * @param fn Function which takes attr name, values list, and data_area
707 * @param data_area Pointer which is passed to function on each call
708 * @return status of search
710 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
711 int scope, const char *expr, const char **attrs,
712 BOOL(*fn)(char *, void **, void *),
713 void *data_area)
715 void *cookie = NULL;
716 int count = 0;
717 ADS_STATUS status;
718 void *res;
720 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
721 &count, &cookie);
723 if (!ADS_ERR_OK(status)) return status;
725 ads_process_results(ads, res, fn, data_area);
726 ads_msgfree(ads, res);
728 while (cookie) {
729 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
730 &res, &count, &cookie);
732 if (!ADS_ERR_OK(status)) break;
734 ads_process_results(ads, res, fn, data_area);
735 ads_msgfree(ads, res);
738 return status;
742 * Do a search with a timeout.
743 * @param ads connection to ads server
744 * @param bind_path Base dn for the search
745 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
746 * @param expr Search expression
747 * @param attrs Attributes to retrieve
748 * @param res ** which will contain results - free res* with ads_msgfree()
749 * @return status of search
751 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
752 const char *expr,
753 const char **attrs, void **res)
755 int rc;
756 char *utf8_expr, *utf8_path, **search_attrs = NULL;
757 TALLOC_CTX *ctx;
759 *res = NULL;
760 if (!(ctx = talloc_init("ads_do_search"))) {
761 DEBUG(1,("ads_do_search: talloc_init() failed!"));
762 return ADS_ERROR(LDAP_NO_MEMORY);
765 /* 0 means the conversion worked but the result was empty
766 so we only fail if it's negative. In any case, it always
767 at least nulls out the dest */
768 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
769 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
770 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
771 rc = LDAP_NO_MEMORY;
772 goto done;
775 if (!attrs || !(*attrs))
776 search_attrs = NULL;
777 else {
778 /* This would be the utf8-encoded version...*/
779 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
780 if (!(str_list_copy(&search_attrs, attrs)))
782 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
783 rc = LDAP_NO_MEMORY;
784 goto done;
788 /* see the note in ads_do_paged_search - we *must* disable referrals */
789 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
791 rc = ldap_search_with_timeout(ads->ld, utf8_path, scope, utf8_expr,
792 search_attrs, 0, NULL, NULL,
793 LDAP_NO_LIMIT,
794 (LDAPMessage **)res);
796 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
797 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
798 rc = 0;
801 done:
802 talloc_destroy(ctx);
803 /* if/when we decide to utf8-encode attrs, take out this next line */
804 str_list_free(&search_attrs);
805 return ADS_ERROR(rc);
808 * Do a general ADS search
809 * @param ads connection to ads server
810 * @param res ** which will contain results - free res* with ads_msgfree()
811 * @param expr Search expression
812 * @param attrs Attributes to retrieve
813 * @return status of search
815 ADS_STATUS ads_search(ADS_STRUCT *ads, void **res,
816 const char *expr,
817 const char **attrs)
819 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
820 expr, attrs, res);
824 * Do a search on a specific DistinguishedName
825 * @param ads connection to ads server
826 * @param res ** which will contain results - free res* with ads_msgfree()
827 * @param dn DistinguishName to search
828 * @param attrs Attributes to retrieve
829 * @return status of search
831 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, void **res,
832 const char *dn,
833 const char **attrs)
835 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)", attrs, res);
839 * Free up memory from a ads_search
840 * @param ads connection to ads server
841 * @param msg Search results to free
843 void ads_msgfree(ADS_STRUCT *ads, void *msg)
845 if (!msg) return;
846 ldap_msgfree(msg);
850 * Free up memory from various ads requests
851 * @param ads connection to ads server
852 * @param mem Area to free
854 void ads_memfree(ADS_STRUCT *ads, void *mem)
856 SAFE_FREE(mem);
860 * Get a dn from search results
861 * @param ads connection to ads server
862 * @param msg Search result
863 * @return dn string
865 char *ads_get_dn(ADS_STRUCT *ads, void *msg)
867 char *utf8_dn, *unix_dn;
869 utf8_dn = ldap_get_dn(ads->ld, msg);
871 if (!utf8_dn) {
872 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
873 return NULL;
876 if (pull_utf8_allocate(&unix_dn, utf8_dn) == (size_t)-1) {
877 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
878 utf8_dn ));
879 return NULL;
881 ldap_memfree(utf8_dn);
882 return unix_dn;
886 * Get a canonical dn from search results
887 * @param ads connection to ads server
888 * @param msg Search result
889 * @return dn string
891 char *ads_get_dn_canonical(ADS_STRUCT *ads, void *msg)
893 #ifdef HAVE_LDAP_DN2AD_CANONICAL
894 return ldap_dn2ad_canonical(ads_get_dn(ads, msg));
895 #else
896 return NULL;
897 #endif
901 * Get the parent from a dn
902 * @param dn the dn to return the parent from
903 * @return parent dn string
905 char *ads_parent_dn(const char *dn)
907 char *p = strchr(dn, ',');
909 if (p == NULL) {
910 return NULL;
913 return p+1;
917 * Find a machine account given a hostname
918 * @param ads connection to ads server
919 * @param res ** which will contain results - free res* with ads_msgfree()
920 * @param host Hostname to search for
921 * @return status of search
923 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, void **res, const char *machine)
925 ADS_STATUS status;
926 char *expr;
927 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
929 *res = NULL;
931 /* the easiest way to find a machine account anywhere in the tree
932 is to look for hostname$ */
933 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
934 DEBUG(1, ("asprintf failed!\n"));
935 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
938 status = ads_search(ads, res, expr, attrs);
939 SAFE_FREE(expr);
940 return status;
944 * Initialize a list of mods to be used in a modify request
945 * @param ctx An initialized TALLOC_CTX
946 * @return allocated ADS_MODLIST
948 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
950 #define ADS_MODLIST_ALLOC_SIZE 10
951 LDAPMod **mods;
953 if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
954 /* -1 is safety to make sure we don't go over the end.
955 need to reset it to NULL before doing ldap modify */
956 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
958 return (ADS_MODLIST)mods;
963 add an attribute to the list, with values list already constructed
965 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
966 int mod_op, const char *name,
967 const void **invals)
969 int curmod;
970 LDAPMod **modlist = (LDAPMod **) *mods;
971 struct berval **ber_values = NULL;
972 char **char_values = NULL;
974 if (!invals) {
975 mod_op = LDAP_MOD_DELETE;
976 } else {
977 if (mod_op & LDAP_MOD_BVALUES)
978 ber_values = ads_dup_values(ctx,
979 (const struct berval **)invals);
980 else
981 char_values = ads_push_strvals(ctx,
982 (const char **) invals);
985 /* find the first empty slot */
986 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
987 curmod++);
988 if (modlist[curmod] == (LDAPMod *) -1) {
989 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
990 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
991 return ADS_ERROR(LDAP_NO_MEMORY);
992 memset(&modlist[curmod], 0,
993 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
994 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
995 *mods = (ADS_MODLIST)modlist;
998 if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
999 return ADS_ERROR(LDAP_NO_MEMORY);
1000 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1001 if (mod_op & LDAP_MOD_BVALUES) {
1002 modlist[curmod]->mod_bvalues = ber_values;
1003 } else if (mod_op & LDAP_MOD_DELETE) {
1004 modlist[curmod]->mod_values = NULL;
1005 } else {
1006 modlist[curmod]->mod_values = char_values;
1009 modlist[curmod]->mod_op = mod_op;
1010 return ADS_ERROR(LDAP_SUCCESS);
1014 * Add a single string value to a mod list
1015 * @param ctx An initialized TALLOC_CTX
1016 * @param mods An initialized ADS_MODLIST
1017 * @param name The attribute name to add
1018 * @param val The value to add - NULL means DELETE
1019 * @return ADS STATUS indicating success of add
1021 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1022 const char *name, const char *val)
1024 const char *values[2];
1026 values[0] = val;
1027 values[1] = NULL;
1029 if (!val)
1030 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1031 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name,
1032 (const void **) values);
1036 * Add an array of string values to a mod list
1037 * @param ctx An initialized TALLOC_CTX
1038 * @param mods An initialized ADS_MODLIST
1039 * @param name The attribute name to add
1040 * @param vals The array of string values to add - NULL means DELETE
1041 * @return ADS STATUS indicating success of add
1043 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1044 const char *name, const char **vals)
1046 if (!vals)
1047 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1048 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1049 name, (const void **) vals);
1052 #if 0
1054 * Add a single ber-encoded value to a mod list
1055 * @param ctx An initialized TALLOC_CTX
1056 * @param mods An initialized ADS_MODLIST
1057 * @param name The attribute name to add
1058 * @param val The value to add - NULL means DELETE
1059 * @return ADS STATUS indicating success of add
1061 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1062 const char *name, const struct berval *val)
1064 const struct berval *values[2];
1066 values[0] = val;
1067 values[1] = NULL;
1068 if (!val)
1069 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1070 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1071 name, (const void **) values);
1073 #endif
1076 * Perform an ldap modify
1077 * @param ads connection to ads server
1078 * @param mod_dn DistinguishedName to modify
1079 * @param mods list of modifications to perform
1080 * @return status of modify
1082 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1084 int ret,i;
1085 char *utf8_dn = NULL;
1087 this control is needed to modify that contains a currently
1088 non-existent attribute (but allowable for the object) to run
1090 LDAPControl PermitModify = {
1091 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
1092 {0, NULL},
1093 (char) 1};
1094 LDAPControl *controls[2];
1096 controls[0] = &PermitModify;
1097 controls[1] = NULL;
1099 if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) {
1100 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1103 /* find the end of the list, marked by NULL or -1 */
1104 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1105 /* make sure the end of the list is NULL */
1106 mods[i] = NULL;
1107 ret = ldap_modify_ext_s(ads->ld, utf8_dn,
1108 (LDAPMod **) mods, controls, NULL);
1109 SAFE_FREE(utf8_dn);
1110 return ADS_ERROR(ret);
1114 * Perform an ldap add
1115 * @param ads connection to ads server
1116 * @param new_dn DistinguishedName to add
1117 * @param mods list of attributes and values for DN
1118 * @return status of add
1120 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1122 int ret, i;
1123 char *utf8_dn = NULL;
1125 if (push_utf8_allocate(&utf8_dn, new_dn) == -1) {
1126 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1127 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1130 /* find the end of the list, marked by NULL or -1 */
1131 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1132 /* make sure the end of the list is NULL */
1133 mods[i] = NULL;
1135 ret = ldap_add_s(ads->ld, utf8_dn, (LDAPMod**)mods);
1136 SAFE_FREE(utf8_dn);
1137 return ADS_ERROR(ret);
1141 * Delete a DistinguishedName
1142 * @param ads connection to ads server
1143 * @param new_dn DistinguishedName to delete
1144 * @return status of delete
1146 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1148 int ret;
1149 char *utf8_dn = NULL;
1150 if (push_utf8_allocate(&utf8_dn, del_dn) == -1) {
1151 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1152 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1155 ret = ldap_delete_s(ads->ld, utf8_dn);
1156 return ADS_ERROR(ret);
1160 * Build an org unit string
1161 * if org unit is Computers or blank then assume a container, otherwise
1162 * assume a / separated list of organisational units.
1163 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1164 * @param ads connection to ads server
1165 * @param org_unit Organizational unit
1166 * @return org unit string - caller must free
1168 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1170 char *ret = NULL;
1172 if (!org_unit || !*org_unit) {
1174 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1176 /* samba4 might not yet respond to a wellknownobject-query */
1177 return ret ? ret : SMB_STRDUP("cn=Computers");
1180 if (strequal(org_unit, "Computers")) {
1181 return SMB_STRDUP("cn=Computers");
1184 /* jmcd: removed "\\" from the separation chars, because it is
1185 needed as an escape for chars like '#' which are valid in an
1186 OU name */
1187 return ads_build_path(org_unit, "/", "ou=", 1);
1191 * Get a org unit string for a well-known GUID
1192 * @param ads connection to ads server
1193 * @param wknguid Well known GUID
1194 * @return org unit string - caller must free
1196 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1198 ADS_STATUS status;
1199 void *res;
1200 char *base, *wkn_dn, *ret, **wkn_dn_exp, **bind_dn_exp;
1201 const char *attrs[] = {"distinguishedName", NULL};
1202 int new_ln, wkn_ln, bind_ln, i;
1204 if (wknguid == NULL) {
1205 return NULL;
1208 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1209 DEBUG(1, ("asprintf failed!\n"));
1210 return NULL;
1213 status = ads_search_dn(ads, &res, base, attrs);
1214 if (!ADS_ERR_OK(status)) {
1215 DEBUG(1,("Failed while searching for: %s\n", base));
1216 SAFE_FREE(base);
1217 return NULL;
1219 SAFE_FREE(base);
1221 if (ads_count_replies(ads, res) != 1) {
1222 return NULL;
1225 /* substitute the bind-path from the well-known-guid-search result */
1226 wkn_dn = ads_get_dn(ads, res);
1227 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1228 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1230 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1232 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1235 new_ln = wkn_ln - bind_ln;
1237 ret = wkn_dn_exp[0];
1239 for (i=1; i < new_ln; i++) {
1240 char *s;
1241 asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]);
1242 ret = SMB_STRDUP(s);
1243 free(s);
1246 ads_memfree(ads, wkn_dn);
1247 ldap_value_free(wkn_dn_exp);
1248 ldap_value_free(bind_dn_exp);
1250 return ret;
1254 * Adds (appends) an item to an attribute array, rather then
1255 * replacing the whole list
1256 * @param ctx An initialized TALLOC_CTX
1257 * @param mods An initialized ADS_MODLIST
1258 * @param name name of the ldap attribute to append to
1259 * @param vals an array of values to add
1260 * @return status of addition
1263 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1264 const char *name, const char **vals)
1266 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name, (const void **) vals);
1270 * Determines the computer account's current KVNO via an LDAP lookup
1271 * @param ads An initialized ADS_STRUCT
1272 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1273 * @return the kvno for the computer account, or -1 in case of a failure.
1276 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *machine_name)
1278 LDAPMessage *res = NULL;
1279 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1280 char *filter;
1281 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1282 char *dn_string = NULL;
1283 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1285 DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name));
1286 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1287 return kvno;
1289 ret = ads_search(ads, (void**)(void *)&res, filter, attrs);
1290 SAFE_FREE(filter);
1291 if (!ADS_ERR_OK(ret) && ads_count_replies(ads, res)) {
1292 DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name));
1293 ads_msgfree(ads, res);
1294 return kvno;
1297 dn_string = ads_get_dn(ads, res);
1298 if (!dn_string) {
1299 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1300 ads_msgfree(ads, res);
1301 return kvno;
1303 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1304 ads_memfree(ads, dn_string);
1306 /* ---------------------------------------------------------
1307 * 0 is returned as a default KVNO from this point on...
1308 * This is done because Windows 2000 does not support key
1309 * version numbers. Chances are that a failure in the next
1310 * step is simply due to Windows 2000 being used for a
1311 * domain controller. */
1312 kvno = 0;
1314 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1315 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1316 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1317 ads_msgfree(ads, res);
1318 return kvno;
1321 /* Success */
1322 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1323 ads_msgfree(ads, res);
1324 return kvno;
1328 * This clears out all registered spn's for a given hostname
1329 * @param ads An initilaized ADS_STRUCT
1330 * @param machine_name the NetBIOS name of the computer.
1331 * @return 0 upon success, non-zero otherwise.
1334 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1336 TALLOC_CTX *ctx;
1337 LDAPMessage *res = NULL;
1338 ADS_MODLIST mods;
1339 const char *servicePrincipalName[1] = {NULL};
1340 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1341 char *dn_string = NULL;
1343 ret = ads_find_machine_acct(ads, (void **)(void *)&res, machine_name);
1344 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1345 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1346 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1347 ads_msgfree(ads, res);
1348 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1351 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1352 ctx = talloc_init("ads_clear_service_principal_names");
1353 if (!ctx) {
1354 ads_msgfree(ads, res);
1355 return ADS_ERROR(LDAP_NO_MEMORY);
1358 if (!(mods = ads_init_mods(ctx))) {
1359 talloc_destroy(ctx);
1360 ads_msgfree(ads, res);
1361 return ADS_ERROR(LDAP_NO_MEMORY);
1363 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1364 if (!ADS_ERR_OK(ret)) {
1365 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1366 ads_msgfree(ads, res);
1367 talloc_destroy(ctx);
1368 return ret;
1370 dn_string = ads_get_dn(ads, res);
1371 if (!dn_string) {
1372 talloc_destroy(ctx);
1373 ads_msgfree(ads, res);
1374 return ADS_ERROR(LDAP_NO_MEMORY);
1376 ret = ads_gen_mod(ads, dn_string, mods);
1377 ads_memfree(ads,dn_string);
1378 if (!ADS_ERR_OK(ret)) {
1379 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1380 machine_name));
1381 ads_msgfree(ads, res);
1382 talloc_destroy(ctx);
1383 return ret;
1386 ads_msgfree(ads, res);
1387 talloc_destroy(ctx);
1388 return ret;
1392 * This adds a service principal name to an existing computer account
1393 * (found by hostname) in AD.
1394 * @param ads An initialized ADS_STRUCT
1395 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1396 * @param spn A string of the service principal to add, i.e. 'host'
1397 * @return 0 upon sucess, or non-zero if a failure occurs
1400 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name, const char *spn)
1402 ADS_STATUS ret;
1403 TALLOC_CTX *ctx;
1404 LDAPMessage *res = NULL;
1405 char *host_spn, *psp1, *psp2, *psp3;
1406 ADS_MODLIST mods;
1407 fstring my_fqdn;
1408 char *dn_string = NULL;
1409 const char *servicePrincipalName[4] = {NULL, NULL, NULL, NULL};
1411 ret = ads_find_machine_acct(ads, (void **)(void *)&res, machine_name);
1412 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1413 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1414 machine_name));
1415 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1416 spn, machine_name, ads->config.realm));
1417 ads_msgfree(ads, res);
1418 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1421 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1422 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1423 ads_msgfree(ads, res);
1424 return ADS_ERROR(LDAP_NO_MEMORY);
1427 name_to_fqdn(my_fqdn, machine_name);
1428 strlower_m(my_fqdn);
1430 if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", my_fqdn))) {
1431 talloc_destroy(ctx);
1432 ads_msgfree(ads, res);
1433 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1436 /* Add the extra principal */
1437 psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name);
1438 strupper_m(psp1);
1439 strlower_m(&psp1[strlen(spn)]);
1440 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp1, machine_name));
1441 servicePrincipalName[0] = psp1;
1442 psp2 = talloc_asprintf(ctx, "%s/%s.%s", spn, machine_name, ads->config.realm);
1443 strupper_m(psp2);
1444 strlower_m(&psp2[strlen(spn)]);
1445 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp2, machine_name));
1446 servicePrincipalName[1] = psp2;
1448 /* Add another principal in case the realm != the DNS domain, so that
1449 * the KDC doesn't send "server principal unknown" errors to clients
1450 * which use the DNS name in determining service principal names. */
1451 psp3 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn);
1452 strupper_m(psp3);
1453 strlower_m(&psp3[strlen(spn)]);
1454 if (strcmp(psp2, psp3) != 0) {
1455 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp3, machine_name));
1456 servicePrincipalName[2] = psp3;
1459 if (!(mods = ads_init_mods(ctx))) {
1460 talloc_destroy(ctx);
1461 ads_msgfree(ads, res);
1462 return ADS_ERROR(LDAP_NO_MEMORY);
1464 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1465 if (!ADS_ERR_OK(ret)) {
1466 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1467 talloc_destroy(ctx);
1468 ads_msgfree(ads, res);
1469 return ret;
1471 dn_string = ads_get_dn(ads, res);
1472 if (!dn_string) {
1473 talloc_destroy(ctx);
1474 ads_msgfree(ads, res);
1475 return ADS_ERROR(LDAP_NO_MEMORY);
1477 ret = ads_gen_mod(ads, dn_string, mods);
1478 ads_memfree(ads,dn_string);
1479 if (!ADS_ERR_OK(ret)) {
1480 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1481 talloc_destroy(ctx);
1482 ads_msgfree(ads, res);
1483 return ret;
1486 talloc_destroy(ctx);
1487 ads_msgfree(ads, res);
1488 return ret;
1492 * adds a machine account to the ADS server
1493 * @param ads An intialized ADS_STRUCT
1494 * @param machine_name - the NetBIOS machine name of this account.
1495 * @param account_type A number indicating the type of account to create
1496 * @param org_unit The LDAP path in which to place this account
1497 * @return 0 upon success, or non-zero otherwise
1500 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1501 const char *org_unit)
1503 ADS_STATUS ret;
1504 char *samAccountName, *controlstr;
1505 TALLOC_CTX *ctx;
1506 ADS_MODLIST mods;
1507 char *new_dn;
1508 const char *objectClass[] = {"top", "person", "organizationalPerson",
1509 "user", "computer", NULL};
1510 LDAPMessage *res = NULL;
1511 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
1512 UF_DONT_EXPIRE_PASSWD |\
1513 UF_ACCOUNTDISABLE );
1515 if (!(ctx = talloc_init("ads_add_machine_acct")))
1516 return ADS_ERROR(LDAP_NO_MEMORY);
1518 ret = ADS_ERROR(LDAP_NO_MEMORY);
1520 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_name, org_unit);
1521 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
1523 if ( !new_dn || !samAccountName ) {
1524 goto done;
1527 #ifndef ENCTYPE_ARCFOUR_HMAC
1528 acct_control |= UF_USE_DES_KEY_ONLY;
1529 #endif
1531 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
1532 goto done;
1535 if (!(mods = ads_init_mods(ctx))) {
1536 goto done;
1539 ads_mod_str(ctx, &mods, "cn", machine_name);
1540 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1541 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1542 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1544 ret = ads_gen_add(ads, new_dn, mods);
1546 done:
1547 ads_msgfree(ads, res);
1548 talloc_destroy(ctx);
1550 return ret;
1554 dump a binary result from ldap
1556 static void dump_binary(const char *field, struct berval **values)
1558 int i, j;
1559 for (i=0; values[i]; i++) {
1560 printf("%s: ", field);
1561 for (j=0; j<values[i]->bv_len; j++) {
1562 printf("%02X", (unsigned char)values[i]->bv_val[j]);
1564 printf("\n");
1568 static void dump_guid(const char *field, struct berval **values)
1570 int i;
1571 UUID_FLAT guid;
1572 for (i=0; values[i]; i++) {
1573 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1574 printf("%s: %s\n", field,
1575 smb_uuid_string_static(smb_uuid_unpack_static(guid)));
1580 dump a sid result from ldap
1582 static void dump_sid(const char *field, struct berval **values)
1584 int i;
1585 for (i=0; values[i]; i++) {
1586 DOM_SID sid;
1587 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1588 printf("%s: %s\n", field, sid_string_static(&sid));
1593 dump ntSecurityDescriptor
1595 static void dump_sd(const char *filed, struct berval **values)
1597 prs_struct ps;
1599 SEC_DESC *psd = 0;
1600 TALLOC_CTX *ctx = 0;
1602 if (!(ctx = talloc_init("sec_io_desc")))
1603 return;
1605 /* prepare data */
1606 prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
1607 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
1608 prs_set_offset(&ps,0);
1610 /* parse secdesc */
1611 if (!sec_io_desc("sd", &psd, &ps, 1)) {
1612 prs_mem_free(&ps);
1613 talloc_destroy(ctx);
1614 return;
1616 if (psd) ads_disp_sd(psd);
1618 prs_mem_free(&ps);
1619 talloc_destroy(ctx);
1623 dump a string result from ldap
1625 static void dump_string(const char *field, char **values)
1627 int i;
1628 for (i=0; values[i]; i++) {
1629 printf("%s: %s\n", field, values[i]);
1634 dump a field from LDAP on stdout
1635 used for debugging
1638 static BOOL ads_dump_field(char *field, void **values, void *data_area)
1640 const struct {
1641 const char *name;
1642 BOOL string;
1643 void (*handler)(const char *, struct berval **);
1644 } handlers[] = {
1645 {"objectGUID", False, dump_guid},
1646 {"netbootGUID", False, dump_guid},
1647 {"nTSecurityDescriptor", False, dump_sd},
1648 {"dnsRecord", False, dump_binary},
1649 {"objectSid", False, dump_sid},
1650 {"tokenGroups", False, dump_sid},
1651 {"tokenGroupsNoGCAcceptable", False, dump_sid},
1652 {"tokengroupsGlobalandUniversal", False, dump_sid},
1653 {NULL, True, NULL}
1655 int i;
1657 if (!field) { /* must be end of an entry */
1658 printf("\n");
1659 return False;
1662 for (i=0; handlers[i].name; i++) {
1663 if (StrCaseCmp(handlers[i].name, field) == 0) {
1664 if (!values) /* first time, indicate string or not */
1665 return handlers[i].string;
1666 handlers[i].handler(field, (struct berval **) values);
1667 break;
1670 if (!handlers[i].name) {
1671 if (!values) /* first time, indicate string conversion */
1672 return True;
1673 dump_string(field, (char **)values);
1675 return False;
1679 * Dump a result from LDAP on stdout
1680 * used for debugging
1681 * @param ads connection to ads server
1682 * @param res Results to dump
1685 void ads_dump(ADS_STRUCT *ads, void *res)
1687 ads_process_results(ads, res, ads_dump_field, NULL);
1691 * Walk through results, calling a function for each entry found.
1692 * The function receives a field name, a berval * array of values,
1693 * and a data area passed through from the start. The function is
1694 * called once with null for field and values at the end of each
1695 * entry.
1696 * @param ads connection to ads server
1697 * @param res Results to process
1698 * @param fn Function for processing each result
1699 * @param data_area user-defined area to pass to function
1701 void ads_process_results(ADS_STRUCT *ads, void *res,
1702 BOOL(*fn)(char *, void **, void *),
1703 void *data_area)
1705 void *msg;
1706 TALLOC_CTX *ctx;
1708 if (!(ctx = talloc_init("ads_process_results")))
1709 return;
1711 for (msg = ads_first_entry(ads, res); msg;
1712 msg = ads_next_entry(ads, msg)) {
1713 char *utf8_field;
1714 BerElement *b;
1716 for (utf8_field=ldap_first_attribute(ads->ld,
1717 (LDAPMessage *)msg,&b);
1718 utf8_field;
1719 utf8_field=ldap_next_attribute(ads->ld,
1720 (LDAPMessage *)msg,b)) {
1721 struct berval **ber_vals;
1722 char **str_vals, **utf8_vals;
1723 char *field;
1724 BOOL string;
1726 pull_utf8_talloc(ctx, &field, utf8_field);
1727 string = fn(field, NULL, data_area);
1729 if (string) {
1730 utf8_vals = ldap_get_values(ads->ld,
1731 (LDAPMessage *)msg, field);
1732 str_vals = ads_pull_strvals(ctx,
1733 (const char **) utf8_vals);
1734 fn(field, (void **) str_vals, data_area);
1735 ldap_value_free(utf8_vals);
1736 } else {
1737 ber_vals = ldap_get_values_len(ads->ld,
1738 (LDAPMessage *)msg, field);
1739 fn(field, (void **) ber_vals, data_area);
1741 ldap_value_free_len(ber_vals);
1743 ldap_memfree(utf8_field);
1745 ber_free(b, 0);
1746 talloc_free_children(ctx);
1747 fn(NULL, NULL, data_area); /* completed an entry */
1750 talloc_destroy(ctx);
1754 * count how many replies are in a LDAPMessage
1755 * @param ads connection to ads server
1756 * @param res Results to count
1757 * @return number of replies
1759 int ads_count_replies(ADS_STRUCT *ads, void *res)
1761 return ldap_count_entries(ads->ld, (LDAPMessage *)res);
1765 * pull the first entry from a ADS result
1766 * @param ads connection to ads server
1767 * @param res Results of search
1768 * @return first entry from result
1770 void *ads_first_entry(ADS_STRUCT *ads, void *res)
1772 return (void *)ldap_first_entry(ads->ld, (LDAPMessage *)res);
1776 * pull the next entry from a ADS result
1777 * @param ads connection to ads server
1778 * @param res Results of search
1779 * @return next entry from result
1781 void *ads_next_entry(ADS_STRUCT *ads, void *res)
1783 return (void *)ldap_next_entry(ads->ld, (LDAPMessage *)res);
1787 * pull a single string from a ADS result
1788 * @param ads connection to ads server
1789 * @param mem_ctx TALLOC_CTX to use for allocating result string
1790 * @param msg Results of search
1791 * @param field Attribute to retrieve
1792 * @return Result string in talloc context
1794 char *ads_pull_string(ADS_STRUCT *ads,
1795 TALLOC_CTX *mem_ctx, void *msg, const char *field)
1797 char **values;
1798 char *ret = NULL;
1799 char *ux_string;
1800 size_t rc;
1802 values = ldap_get_values(ads->ld, msg, field);
1803 if (!values)
1804 return NULL;
1806 if (values[0]) {
1807 rc = pull_utf8_talloc(mem_ctx, &ux_string,
1808 values[0]);
1809 if (rc != (size_t)-1)
1810 ret = ux_string;
1813 ldap_value_free(values);
1814 return ret;
1818 * pull an array of strings from a ADS result
1819 * @param ads connection to ads server
1820 * @param mem_ctx TALLOC_CTX to use for allocating result string
1821 * @param msg Results of search
1822 * @param field Attribute to retrieve
1823 * @return Result strings in talloc context
1825 char **ads_pull_strings(ADS_STRUCT *ads,
1826 TALLOC_CTX *mem_ctx, void *msg, const char *field,
1827 size_t *num_values)
1829 char **values;
1830 char **ret = NULL;
1831 int i;
1833 values = ldap_get_values(ads->ld, msg, field);
1834 if (!values)
1835 return NULL;
1837 *num_values = ldap_count_values(values);
1839 ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
1840 if (!ret) {
1841 ldap_value_free(values);
1842 return NULL;
1845 for (i=0;i<*num_values;i++) {
1846 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
1847 ldap_value_free(values);
1848 return NULL;
1851 ret[i] = NULL;
1853 ldap_value_free(values);
1854 return ret;
1858 * pull an array of strings from a ADS result
1859 * (handle large multivalue attributes with range retrieval)
1860 * @param ads connection to ads server
1861 * @param mem_ctx TALLOC_CTX to use for allocating result string
1862 * @param msg Results of search
1863 * @param field Attribute to retrieve
1864 * @param current_strings strings returned by a previous call to this function
1865 * @param next_attribute The next query should ask for this attribute
1866 * @param num_values How many values did we get this time?
1867 * @param more_values Are there more values to get?
1868 * @return Result strings in talloc context
1870 char **ads_pull_strings_range(ADS_STRUCT *ads,
1871 TALLOC_CTX *mem_ctx,
1872 void *msg, const char *field,
1873 char **current_strings,
1874 const char **next_attribute,
1875 size_t *num_strings,
1876 BOOL *more_strings)
1878 char *attr;
1879 char *expected_range_attrib, *range_attr;
1880 BerElement *ptr = NULL;
1881 char **strings;
1882 char **new_strings;
1883 size_t num_new_strings;
1884 unsigned long int range_start;
1885 unsigned long int range_end;
1887 /* we might have been given the whole lot anyway */
1888 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
1889 *more_strings = False;
1890 return strings;
1893 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
1895 /* look for Range result */
1896 for (attr = ldap_first_attribute(ads->ld, (LDAPMessage *)msg, &ptr);
1897 attr;
1898 attr = ldap_next_attribute(ads->ld, (LDAPMessage *)msg, ptr)) {
1899 /* we ignore the fact that this is utf8, as all attributes are ascii... */
1900 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
1901 range_attr = attr;
1902 break;
1904 ldap_memfree(attr);
1906 if (!attr) {
1907 ber_free(ptr, 0);
1908 /* nothing here - this field is just empty */
1909 *more_strings = False;
1910 return NULL;
1913 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
1914 &range_start, &range_end) == 2) {
1915 *more_strings = True;
1916 } else {
1917 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
1918 &range_start) == 1) {
1919 *more_strings = False;
1920 } else {
1921 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
1922 range_attr));
1923 ldap_memfree(range_attr);
1924 *more_strings = False;
1925 return NULL;
1929 if ((*num_strings) != range_start) {
1930 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
1931 " - aborting range retreival\n",
1932 range_attr, (unsigned int)(*num_strings) + 1, range_start));
1933 ldap_memfree(range_attr);
1934 *more_strings = False;
1935 return NULL;
1938 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
1940 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
1941 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
1942 "strings in this bunch, but we only got %lu - aborting range retreival\n",
1943 range_attr, (unsigned long int)range_end - range_start + 1,
1944 (unsigned long int)num_new_strings));
1945 ldap_memfree(range_attr);
1946 *more_strings = False;
1947 return NULL;
1950 strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
1951 *num_strings + num_new_strings);
1953 if (strings == NULL) {
1954 ldap_memfree(range_attr);
1955 *more_strings = False;
1956 return NULL;
1959 if (new_strings && num_new_strings) {
1960 memcpy(&strings[*num_strings], new_strings,
1961 sizeof(*new_strings) * num_new_strings);
1964 (*num_strings) += num_new_strings;
1966 if (*more_strings) {
1967 *next_attribute = talloc_asprintf(mem_ctx,
1968 "%s;range=%d-*",
1969 field,
1970 (int)*num_strings);
1972 if (!*next_attribute) {
1973 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
1974 ldap_memfree(range_attr);
1975 *more_strings = False;
1976 return NULL;
1980 ldap_memfree(range_attr);
1982 return strings;
1986 * pull a single uint32 from a ADS result
1987 * @param ads connection to ads server
1988 * @param msg Results of search
1989 * @param field Attribute to retrieve
1990 * @param v Pointer to int to store result
1991 * @return boolean inidicating success
1993 BOOL ads_pull_uint32(ADS_STRUCT *ads,
1994 void *msg, const char *field, uint32 *v)
1996 char **values;
1998 values = ldap_get_values(ads->ld, msg, field);
1999 if (!values)
2000 return False;
2001 if (!values[0]) {
2002 ldap_value_free(values);
2003 return False;
2006 *v = atoi(values[0]);
2007 ldap_value_free(values);
2008 return True;
2012 * pull a single objectGUID from an ADS result
2013 * @param ads connection to ADS server
2014 * @param msg results of search
2015 * @param guid 37-byte area to receive text guid
2016 * @return boolean indicating success
2018 BOOL ads_pull_guid(ADS_STRUCT *ads,
2019 void *msg, struct uuid *guid)
2021 char **values;
2022 UUID_FLAT flat_guid;
2024 values = ldap_get_values(ads->ld, msg, "objectGUID");
2025 if (!values)
2026 return False;
2028 if (values[0]) {
2029 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2030 smb_uuid_unpack(flat_guid, guid);
2031 ldap_value_free(values);
2032 return True;
2034 ldap_value_free(values);
2035 return False;
2041 * pull a single DOM_SID from a ADS result
2042 * @param ads connection to ads server
2043 * @param msg Results of search
2044 * @param field Attribute to retrieve
2045 * @param sid Pointer to sid to store result
2046 * @return boolean inidicating success
2048 BOOL ads_pull_sid(ADS_STRUCT *ads,
2049 void *msg, const char *field, DOM_SID *sid)
2051 struct berval **values;
2052 BOOL ret = False;
2054 values = ldap_get_values_len(ads->ld, msg, field);
2056 if (!values)
2057 return False;
2059 if (values[0])
2060 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2062 ldap_value_free_len(values);
2063 return ret;
2067 * pull an array of DOM_SIDs from a ADS result
2068 * @param ads connection to ads server
2069 * @param mem_ctx TALLOC_CTX for allocating sid array
2070 * @param msg Results of search
2071 * @param field Attribute to retrieve
2072 * @param sids pointer to sid array to allocate
2073 * @return the count of SIDs pulled
2075 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2076 void *msg, const char *field, DOM_SID **sids)
2078 struct berval **values;
2079 BOOL ret;
2080 int count, i;
2082 values = ldap_get_values_len(ads->ld, msg, field);
2084 if (!values)
2085 return 0;
2087 for (i=0; values[i]; i++)
2088 /* nop */ ;
2090 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2091 if (!(*sids)) {
2092 ldap_value_free_len(values);
2093 return 0;
2096 count = 0;
2097 for (i=0; values[i]; i++) {
2098 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2099 if (ret) {
2100 fstring sid;
2101 DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid, &(*sids)[count])));
2102 count++;
2106 ldap_value_free_len(values);
2107 return count;
2111 * pull a SEC_DESC from a ADS result
2112 * @param ads connection to ads server
2113 * @param mem_ctx TALLOC_CTX for allocating sid array
2114 * @param msg Results of search
2115 * @param field Attribute to retrieve
2116 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2117 * @return boolean inidicating success
2119 BOOL ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2120 void *msg, const char *field, SEC_DESC **sd)
2122 struct berval **values;
2123 prs_struct ps;
2124 BOOL ret = False;
2126 values = ldap_get_values_len(ads->ld, msg, field);
2128 if (!values) return False;
2130 if (values[0]) {
2131 prs_init(&ps, values[0]->bv_len, mem_ctx, UNMARSHALL);
2132 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
2133 prs_set_offset(&ps,0);
2135 ret = sec_io_desc("sd", sd, &ps, 1);
2138 ldap_value_free_len(values);
2139 return ret;
2143 * in order to support usernames longer than 21 characters we need to
2144 * use both the sAMAccountName and the userPrincipalName attributes
2145 * It seems that not all users have the userPrincipalName attribute set
2147 * @param ads connection to ads server
2148 * @param mem_ctx TALLOC_CTX for allocating sid array
2149 * @param msg Results of search
2150 * @return the username
2152 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, void *msg)
2154 #if 0 /* JERRY */
2155 char *ret, *p;
2157 /* lookup_name() only works on the sAMAccountName to
2158 returning the username portion of userPrincipalName
2159 breaks winbindd_getpwnam() */
2161 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2162 if (ret && (p = strchr_m(ret, '@'))) {
2163 *p = 0;
2164 return ret;
2166 #endif
2167 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2172 * find the update serial number - this is the core of the ldap cache
2173 * @param ads connection to ads server
2174 * @param ads connection to ADS server
2175 * @param usn Pointer to retrieved update serial number
2176 * @return status of search
2178 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2180 const char *attrs[] = {"highestCommittedUSN", NULL};
2181 ADS_STATUS status;
2182 void *res;
2184 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2185 if (!ADS_ERR_OK(status))
2186 return status;
2188 if (ads_count_replies(ads, res) != 1) {
2189 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2192 ads_pull_uint32(ads, res, "highestCommittedUSN", usn);
2193 ads_msgfree(ads, res);
2194 return ADS_SUCCESS;
2197 /* parse a ADS timestring - typical string is
2198 '20020917091222.0Z0' which means 09:12.22 17th September
2199 2002, timezone 0 */
2200 static time_t ads_parse_time(const char *str)
2202 struct tm tm;
2204 ZERO_STRUCT(tm);
2206 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2207 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2208 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2209 return 0;
2211 tm.tm_year -= 1900;
2212 tm.tm_mon -= 1;
2214 return timegm(&tm);
2218 * Find the servers name and realm - this can be done before authentication
2219 * The ldapServiceName field on w2k looks like this:
2220 * vnet3.home.samba.org:win2000-vnet3$@VNET3.HOME.SAMBA.ORG
2221 * @param ads connection to ads server
2222 * @return status of search
2224 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2226 const char *attrs[] = {"currentTime", NULL};
2227 ADS_STATUS status;
2228 void *res;
2229 char *timestr;
2230 TALLOC_CTX *ctx;
2231 ADS_STRUCT *ads_s = ads;
2233 if (!(ctx = talloc_init("ads_server_info"))) {
2234 return ADS_ERROR(LDAP_NO_MEMORY);
2237 /* establish a new ldap tcp session if necessary */
2239 if ( !ads->ld ) {
2240 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2241 ads->server.ldap_server )) == NULL )
2243 goto done;
2245 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2246 status = ads_connect( ads_s );
2247 if ( !ADS_ERR_OK(status))
2248 goto done;
2251 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2252 if (!ADS_ERR_OK(status)) {
2253 talloc_destroy(ctx);
2254 goto done;
2257 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2258 if (!timestr) {
2259 ads_msgfree(ads, res);
2260 talloc_destroy(ctx);
2261 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2262 goto done;
2265 /* but save the time and offset in the original ADS_STRUCT */
2267 ads->config.current_time = ads_parse_time(timestr);
2269 if (ads->config.current_time != 0) {
2270 ads->auth.time_offset = ads->config.current_time - time(NULL);
2271 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2274 ads_msgfree(ads, res);
2276 status = ADS_SUCCESS;
2278 done:
2279 /* free any temporary ads connections */
2280 if ( ads_s != ads ) {
2281 ads_destroy( &ads_s );
2283 talloc_destroy(ctx);
2285 return status;
2289 * find the domain sid for our domain
2290 * @param ads connection to ads server
2291 * @param sid Pointer to domain sid
2292 * @return status of search
2294 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2296 const char *attrs[] = {"objectSid", NULL};
2297 void *res;
2298 ADS_STATUS rc;
2300 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2301 attrs, &res);
2302 if (!ADS_ERR_OK(rc)) return rc;
2303 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2304 ads_msgfree(ads, res);
2305 return ADS_ERROR_SYSTEM(ENOENT);
2307 ads_msgfree(ads, res);
2309 return ADS_SUCCESS;
2313 * find our site name
2314 * @param ads connection to ads server
2315 * @param mem_ctx Pointer to talloc context
2316 * @param site_name Pointer to the sitename
2317 * @return status of search
2319 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2321 ADS_STATUS status;
2322 void *res;
2323 const char *dn, *service_name;
2324 const char *attrs[] = { "dsServiceName", NULL };
2326 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2327 if (!ADS_ERR_OK(status)) {
2328 return status;
2331 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2332 if (service_name == NULL) {
2333 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2336 /* go up three levels */
2337 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2338 if (dn == NULL) {
2339 return ADS_ERROR(LDAP_NO_MEMORY);
2342 *site_name = talloc_strdup(mem_ctx, dn);
2343 if (*site_name == NULL) {
2344 return ADS_ERROR(LDAP_NO_MEMORY);
2347 ads_msgfree(ads, res);
2349 return status;
2351 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2356 * find the site dn where a machine resides
2357 * @param ads connection to ads server
2358 * @param mem_ctx Pointer to talloc context
2359 * @param computer_name name of the machine
2360 * @param site_name Pointer to the sitename
2361 * @return status of search
2363 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
2365 ADS_STATUS status;
2366 void *res;
2367 const char *parent, *config_context, *filter;
2368 const char *attrs[] = { "configurationNamingContext", NULL };
2369 char *dn;
2371 /* shortcut a query */
2372 if (strequal(computer_name, ads->config.ldap_server_name)) {
2373 return ads_site_dn(ads, mem_ctx, site_dn);
2376 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2377 if (!ADS_ERR_OK(status)) {
2378 return status;
2381 config_context = ads_pull_string(ads, mem_ctx, res, "configurationNamingContext");
2382 if (config_context == NULL) {
2383 return ADS_ERROR(LDAP_NO_MEMORY);
2386 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
2387 if (filter == NULL) {
2388 return ADS_ERROR(LDAP_NO_MEMORY);
2391 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE, filter, NULL, &res);
2392 if (!ADS_ERR_OK(status)) {
2393 return status;
2396 if (ads_count_replies(ads, res) != 1) {
2397 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2400 dn = ads_get_dn(ads, res);
2401 if (dn == NULL) {
2402 return ADS_ERROR(LDAP_NO_MEMORY);
2405 /* go up three levels */
2406 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
2407 if (parent == NULL) {
2408 ads_memfree(ads, dn);
2409 return ADS_ERROR(LDAP_NO_MEMORY);
2412 *site_dn = talloc_strdup(mem_ctx, parent);
2413 if (*site_dn == NULL) {
2414 ads_memfree(ads, dn);
2415 ADS_ERROR(LDAP_NO_MEMORY);
2418 ads_memfree(ads, dn);
2419 ads_msgfree(ads, res);
2421 return status;
2425 * get the upn suffixes for a domain
2426 * @param ads connection to ads server
2427 * @param mem_ctx Pointer to talloc context
2428 * @param suffixes Pointer to an array of suffixes
2429 * @param site_name Pointer to the number of suffixes
2430 * @return status of search
2432 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char **suffixes, size_t *num_suffixes)
2434 ADS_STATUS status;
2435 void *res;
2436 const char *config_context, *base;
2437 const char *attrs[] = { "configurationNamingContext", NULL };
2438 const char *attrs2[] = { "uPNSuffixes", NULL };
2440 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2441 if (!ADS_ERR_OK(status)) {
2442 return status;
2445 config_context = ads_pull_string(ads, mem_ctx, res, "configurationNamingContext");
2446 if (config_context == NULL) {
2447 return ADS_ERROR(LDAP_NO_MEMORY);
2450 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
2451 if (base == NULL) {
2452 return ADS_ERROR(LDAP_NO_MEMORY);
2455 status = ads_search_dn(ads, &res, base, attrs2);
2456 if (!ADS_ERR_OK(status)) {
2457 return status;
2460 if (ads_count_replies(ads, res) != 1) {
2461 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2464 suffixes = ads_pull_strings(ads, mem_ctx, &res, "uPNSuffixes", num_suffixes);
2465 if (suffixes == NULL) {
2466 ads_msgfree(ads, res);
2467 return ADS_ERROR(LDAP_NO_MEMORY);
2470 ads_msgfree(ads, res);
2472 return status;
2476 * pull a DOM_SID from an extended dn string
2477 * @param mem_ctx TALLOC_CTX
2478 * @param flags string type of extended_dn
2479 * @param sid pointer to a DOM_SID
2480 * @return boolean inidicating success
2482 BOOL ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
2483 const char *dn,
2484 enum ads_extended_dn_flags flags,
2485 DOM_SID *sid)
2487 char *p, *q;
2489 if (!dn) {
2490 return False;
2494 * ADS_EXTENDED_DN_HEX_STRING:
2495 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
2497 * ADS_EXTENDED_DN_STRING (only with w2k3):
2498 <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
2501 p = strchr(dn, ';');
2502 if (!p) {
2503 return False;
2506 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
2507 return False;
2510 p += strlen(";<SID=");
2512 q = strchr(p, '>');
2513 if (!q) {
2514 return False;
2517 *q = '\0';
2519 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
2521 switch (flags) {
2523 case ADS_EXTENDED_DN_STRING:
2524 if (!string_to_sid(sid, p)) {
2525 return False;
2527 break;
2528 case ADS_EXTENDED_DN_HEX_STRING: {
2529 pstring buf;
2530 size_t buf_len;
2532 buf_len = strhex_to_str(buf, strlen(p), p);
2533 if (buf_len == 0) {
2534 return False;
2537 if (!sid_parse(buf, buf_len, sid)) {
2538 DEBUG(10,("failed to parse sid\n"));
2539 return False;
2541 break;
2543 default:
2544 DEBUG(10,("unknown extended dn format\n"));
2545 return False;
2548 return True;
2552 * pull an array of DOM_SIDs from a ADS result
2553 * @param ads connection to ads server
2554 * @param mem_ctx TALLOC_CTX for allocating sid array
2555 * @param msg Results of search
2556 * @param field Attribute to retrieve
2557 * @param flags string type of extended_dn
2558 * @param sids pointer to sid array to allocate
2559 * @return the count of SIDs pulled
2561 int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads,
2562 TALLOC_CTX *mem_ctx,
2563 void *msg,
2564 const char *field,
2565 enum ads_extended_dn_flags flags,
2566 DOM_SID **sids)
2568 int i;
2569 size_t dn_count;
2570 char **dn_strings;
2572 if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field,
2573 &dn_count)) == NULL) {
2574 return 0;
2577 (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, dn_count + 1);
2578 if (!(*sids)) {
2579 TALLOC_FREE(dn_strings);
2580 return 0;
2583 for (i=0; i<dn_count; i++) {
2585 if (!ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i],
2586 flags, &(*sids)[i])) {
2587 TALLOC_FREE(*sids);
2588 TALLOC_FREE(dn_strings);
2589 return 0;
2593 TALLOC_FREE(dn_strings);
2595 return dn_count;
2598 #endif