r17915: Saturn fixes
[Samba.git] / source / libads / ldap.c
blobc49e3480b80f6da6e35fef597a50c9e9c6653733
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
8 Copyright (C) Gerald Carter 2006
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 #include "includes.h"
27 #ifdef HAVE_LDAP
29 /**
30 * @file ldap.c
31 * @brief basic ldap client-side routines for ads server communications
33 * The routines contained here should do the necessary ldap calls for
34 * ads setups.
36 * Important note: attribute names passed into ads_ routines must
37 * already be in UTF-8 format. We do not convert them because in almost
38 * all cases, they are just ascii (which is represented with the same
39 * codepoints in UTF-8). This may have to change at some point
40 **/
43 #define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"
45 static SIG_ATOMIC_T gotalarm;
47 /***************************************************************
48 Signal function to tell us we timed out.
49 ****************************************************************/
51 static void gotalarm_sig(void)
53 gotalarm = 1;
56 LDAP *ldap_open_with_timeout(const char *server, int port, unsigned int to)
58 LDAP *ldp = NULL;
60 /* Setup timeout */
61 gotalarm = 0;
62 CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
63 alarm(to);
64 /* End setup timeout. */
66 ldp = ldap_open(server, port);
68 /* Teardown timeout. */
69 CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
70 alarm(0);
72 return ldp;
75 static int ldap_search_with_timeout(LDAP *ld,
76 LDAP_CONST char *base,
77 int scope,
78 LDAP_CONST char *filter,
79 char **attrs,
80 int attrsonly,
81 LDAPControl **sctrls,
82 LDAPControl **cctrls,
83 int sizelimit,
84 LDAPMessage **res )
86 struct timeval timeout;
87 int result;
89 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
90 timeout.tv_sec = lp_ldap_timeout();
91 timeout.tv_usec = 0;
93 /* Setup alarm timeout.... Do we need both of these ? JRA. */
94 gotalarm = 0;
95 CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
96 alarm(lp_ldap_timeout());
97 /* End setup timeout. */
99 result = ldap_search_ext_s(ld, base, scope, filter, attrs,
100 attrsonly, sctrls, cctrls, &timeout,
101 sizelimit, res);
103 /* Teardown timeout. */
104 CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
105 alarm(0);
107 if (gotalarm != 0)
108 return LDAP_TIMELIMIT_EXCEEDED;
110 return result;
114 try a connection to a given ldap server, returning True and setting the servers IP
115 in the ads struct if successful
117 BOOL ads_try_connect(ADS_STRUCT *ads, const char *server )
119 char *srv;
120 struct cldap_netlogon_reply cldap_reply;
122 if (!server || !*server) {
123 return False;
126 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
127 server, ads->server.realm));
129 /* this copes with inet_ntoa brokenness */
131 srv = SMB_STRDUP(server);
133 ZERO_STRUCT( cldap_reply );
135 if ( !ads_cldap_netlogon( srv, ads->server.realm, &cldap_reply ) ) {
136 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", srv));
137 return False;
140 /* Check the CLDAP reply flags */
142 if ( !(cldap_reply.flags & ADS_LDAP) ) {
143 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
144 srv));
145 SAFE_FREE( srv );
146 return False;
149 /* Fill in the ads->config values */
151 SAFE_FREE(ads->config.realm);
152 SAFE_FREE(ads->config.bind_path);
153 SAFE_FREE(ads->config.ldap_server_name);
154 SAFE_FREE(ads->server.workgroup);
156 ads->config.ldap_server_name = SMB_STRDUP(cldap_reply.hostname);
157 strupper_m(cldap_reply.domain);
158 ads->config.realm = SMB_STRDUP(cldap_reply.domain);
159 ads->config.bind_path = ads_build_dn(ads->config.realm);
160 ads->server.workgroup = SMB_STRDUP(cldap_reply.netbios_domain);
162 ads->ldap_port = LDAP_PORT;
163 ads->ldap_ip = *interpret_addr2(srv);
164 SAFE_FREE(srv);
166 /* cache the successful connection */
168 saf_store( ads->server.workgroup, server );
170 return True;
173 /**********************************************************************
174 Try to find an AD dc using our internal name resolution routines
175 Try the realm first and then then workgroup name if netbios is not
176 disabled
177 **********************************************************************/
179 static BOOL ads_find_dc(ADS_STRUCT *ads)
181 const char *c_realm;
182 int count, i=0;
183 struct ip_service *ip_list;
184 pstring realm;
185 BOOL got_realm = False;
186 BOOL use_own_domain = False;
188 /* if the realm and workgroup are both empty, assume they are ours */
190 /* realm */
191 c_realm = ads->server.realm;
193 if ( !c_realm || !*c_realm ) {
194 /* special case where no realm and no workgroup means our own */
195 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
196 use_own_domain = True;
197 c_realm = lp_realm();
201 if (c_realm && *c_realm)
202 got_realm = True;
204 again:
205 /* we need to try once with the realm name and fallback to the
206 netbios domain name if we fail (if netbios has not been disabled */
208 if ( !got_realm && !lp_disable_netbios() ) {
209 c_realm = ads->server.workgroup;
210 if (!c_realm || !*c_realm) {
211 if ( use_own_domain )
212 c_realm = lp_workgroup();
215 if ( !c_realm || !*c_realm ) {
216 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
217 return False;
221 pstrcpy( realm, c_realm );
223 DEBUG(6,("ads_find_dc: looking for %s '%s'\n",
224 (got_realm ? "realm" : "domain"), realm));
226 if ( !get_sorted_dc_list(realm, &ip_list, &count, got_realm) ) {
227 /* fall back to netbios if we can */
228 if ( got_realm && !lp_disable_netbios() ) {
229 got_realm = False;
230 goto again;
233 return False;
236 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
237 for ( i=0; i<count; i++ ) {
238 fstring server;
240 fstrcpy( server, inet_ntoa(ip_list[i].ip) );
242 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
243 continue;
245 if ( ads_try_connect(ads, server) ) {
246 SAFE_FREE(ip_list);
247 return True;
250 /* keep track of failures */
251 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
254 SAFE_FREE(ip_list);
256 return False;
261 * Connect to the LDAP server
262 * @param ads Pointer to an existing ADS_STRUCT
263 * @return status of connection
265 ADS_STATUS ads_connect(ADS_STRUCT *ads)
267 int version = LDAP_VERSION3;
268 ADS_STATUS status;
270 ads->last_attempt = time(NULL);
271 ads->ld = NULL;
273 /* try with a user specified server */
275 if (ads->server.ldap_server &&
276 ads_try_connect(ads, ads->server.ldap_server)) {
277 goto got_connection;
280 if (ads_find_dc(ads)) {
281 goto got_connection;
284 return ADS_ERROR_SYSTEM(errno?errno:ENOENT);
286 got_connection:
287 DEBUG(3,("Connected to LDAP server %s\n", inet_ntoa(ads->ldap_ip)));
289 if (!ads->auth.user_name) {
290 /* Must use the userPrincipalName value here or sAMAccountName
291 and not servicePrincipalName; found by Guenther Deschner */
293 asprintf(&ads->auth.user_name, "%s$", global_myname() );
296 if (!ads->auth.realm) {
297 ads->auth.realm = SMB_STRDUP(ads->config.realm);
300 if (!ads->auth.kdc_server) {
301 ads->auth.kdc_server = SMB_STRDUP(inet_ntoa(ads->ldap_ip));
304 #if KRB5_DNS_HACK
305 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
306 to MIT kerberos to work (tridge) */
308 char *env;
309 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
310 setenv(env, ads->auth.kdc_server, 1);
311 free(env);
313 #endif
315 /* If the caller() requested no LDAP bind, then we are done */
317 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
318 return ADS_SUCCESS;
321 /* Otherwise setup the TCP LDAP session */
323 if ( (ads->ld = ldap_open_with_timeout(ads->config.ldap_server_name,
324 LDAP_PORT, lp_ldap_timeout())) == NULL )
326 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
328 ldap_set_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
330 status = ADS_ERROR(smb_ldap_start_tls(ads->ld, version));
331 if (!ADS_ERR_OK(status)) {
332 return status;
335 /* fill in the current time and offsets */
337 status = ads_current_time( ads );
338 if ( !ADS_ERR_OK(status) ) {
339 return status;
342 /* Now do the bind */
344 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
345 return ADS_ERROR(ldap_simple_bind_s( ads->ld, NULL, NULL));
348 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
349 return ADS_ERROR(ldap_simple_bind_s( ads->ld, ads->auth.user_name, ads->auth.password));
352 return ads_sasl_bind(ads);
356 Duplicate a struct berval into talloc'ed memory
358 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
360 struct berval *value;
362 if (!in_val) return NULL;
364 value = TALLOC_ZERO_P(ctx, struct berval);
365 if (value == NULL)
366 return NULL;
367 if (in_val->bv_len == 0) return value;
369 value->bv_len = in_val->bv_len;
370 value->bv_val = TALLOC_MEMDUP(ctx, in_val->bv_val, in_val->bv_len);
371 return value;
375 Make a values list out of an array of (struct berval *)
377 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
378 const struct berval **in_vals)
380 struct berval **values;
381 int i;
383 if (!in_vals) return NULL;
384 for (i=0; in_vals[i]; i++)
385 ; /* count values */
386 values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
387 if (!values) return NULL;
389 for (i=0; in_vals[i]; i++) {
390 values[i] = dup_berval(ctx, in_vals[i]);
392 return values;
396 UTF8-encode a values list out of an array of (char *)
398 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
400 char **values;
401 int i;
403 if (!in_vals) return NULL;
404 for (i=0; in_vals[i]; i++)
405 ; /* count values */
406 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
407 if (!values) return NULL;
409 for (i=0; in_vals[i]; i++) {
410 push_utf8_talloc(ctx, &values[i], in_vals[i]);
412 return values;
416 Pull a (char *) array out of a UTF8-encoded values list
418 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
420 char **values;
421 int i;
423 if (!in_vals) return NULL;
424 for (i=0; in_vals[i]; i++)
425 ; /* count values */
426 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
427 if (!values) return NULL;
429 for (i=0; in_vals[i]; i++) {
430 pull_utf8_talloc(ctx, &values[i], in_vals[i]);
432 return values;
436 * Do a search with paged results. cookie must be null on the first
437 * call, and then returned on each subsequent call. It will be null
438 * again when the entire search is complete
439 * @param ads connection to ads server
440 * @param bind_path Base dn for the search
441 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
442 * @param expr Search expression - specified in local charset
443 * @param attrs Attributes to retrieve - specified in utf8 or ascii
444 * @param res ** which will contain results - free res* with ads_msgfree()
445 * @param count Number of entries retrieved on this page
446 * @param cookie The paged results cookie to be returned on subsequent calls
447 * @return status of search
449 ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads, const char *bind_path,
450 int scope, const char *expr,
451 const char **attrs, void *args, void **res,
452 int *count, void **cookie)
454 int rc, i, version;
455 char *utf8_expr, *utf8_path, **search_attrs;
456 LDAPControl PagedResults, NoReferrals, ExtendedDn, *controls[4], **rcontrols;
457 BerElement *cookie_be = NULL;
458 struct berval *cookie_bv= NULL;
459 BerElement *extdn_be = NULL;
460 struct berval *extdn_bv= NULL;
462 TALLOC_CTX *ctx;
463 ads_control *external_control = (ads_control *) args;
465 *res = NULL;
467 if (!(ctx = talloc_init("ads_do_paged_search_args")))
468 return ADS_ERROR(LDAP_NO_MEMORY);
470 /* 0 means the conversion worked but the result was empty
471 so we only fail if it's -1. In any case, it always
472 at least nulls out the dest */
473 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
474 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
475 rc = LDAP_NO_MEMORY;
476 goto done;
479 if (!attrs || !(*attrs))
480 search_attrs = NULL;
481 else {
482 /* This would be the utf8-encoded version...*/
483 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
484 if (!(str_list_copy(&search_attrs, attrs))) {
485 rc = LDAP_NO_MEMORY;
486 goto done;
491 /* Paged results only available on ldap v3 or later */
492 ldap_get_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
493 if (version < LDAP_VERSION3) {
494 rc = LDAP_NOT_SUPPORTED;
495 goto done;
498 cookie_be = ber_alloc_t(LBER_USE_DER);
499 if (*cookie) {
500 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
501 ber_bvfree(*cookie); /* don't need it from last time */
502 *cookie = NULL;
503 } else {
504 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
506 ber_flatten(cookie_be, &cookie_bv);
507 PagedResults.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID);
508 PagedResults.ldctl_iscritical = (char) 1;
509 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
510 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
512 NoReferrals.ldctl_oid = CONST_DISCARD(char *, ADS_NO_REFERRALS_OID);
513 NoReferrals.ldctl_iscritical = (char) 0;
514 NoReferrals.ldctl_value.bv_len = 0;
515 NoReferrals.ldctl_value.bv_val = CONST_DISCARD(char *, "");
517 if (external_control && strequal(external_control->control, ADS_EXTENDED_DN_OID)) {
519 ExtendedDn.ldctl_oid = CONST_DISCARD(char *, external_control->control);
520 ExtendedDn.ldctl_iscritical = (char) external_control->critical;
522 /* win2k does not accept a ldctl_value beeing passed in */
524 if (external_control->val != 0) {
526 if ((extdn_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
527 rc = LDAP_NO_MEMORY;
528 goto done;
531 if ((ber_printf(extdn_be, "{i}", (ber_int_t) external_control->val)) == -1) {
532 rc = LDAP_NO_MEMORY;
533 goto done;
535 if ((ber_flatten(extdn_be, &extdn_bv)) == -1) {
536 rc = LDAP_NO_MEMORY;
537 goto done;
540 ExtendedDn.ldctl_value.bv_len = extdn_bv->bv_len;
541 ExtendedDn.ldctl_value.bv_val = extdn_bv->bv_val;
543 } else {
544 ExtendedDn.ldctl_value.bv_len = 0;
545 ExtendedDn.ldctl_value.bv_val = NULL;
548 controls[0] = &NoReferrals;
549 controls[1] = &PagedResults;
550 controls[2] = &ExtendedDn;
551 controls[3] = NULL;
553 } else {
554 controls[0] = &NoReferrals;
555 controls[1] = &PagedResults;
556 controls[2] = NULL;
559 /* we need to disable referrals as the openldap libs don't
560 handle them and paged results at the same time. Using them
561 together results in the result record containing the server
562 page control being removed from the result list (tridge/jmcd)
564 leaving this in despite the control that says don't generate
565 referrals, in case the server doesn't support it (jmcd)
567 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
569 rc = ldap_search_with_timeout(ads->ld, utf8_path, scope, utf8_expr,
570 search_attrs, 0, controls,
571 NULL, LDAP_NO_LIMIT,
572 (LDAPMessage **)res);
574 ber_free(cookie_be, 1);
575 ber_bvfree(cookie_bv);
577 if (rc) {
578 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
579 ldap_err2string(rc)));
580 goto done;
583 rc = ldap_parse_result(ads->ld, *res, NULL, NULL, NULL,
584 NULL, &rcontrols, 0);
586 if (!rcontrols) {
587 goto done;
590 for (i=0; rcontrols[i]; i++) {
591 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
592 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
593 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
594 &cookie_bv);
595 /* the berval is the cookie, but must be freed when
596 it is all done */
597 if (cookie_bv->bv_len) /* still more to do */
598 *cookie=ber_bvdup(cookie_bv);
599 else
600 *cookie=NULL;
601 ber_bvfree(cookie_bv);
602 ber_free(cookie_be, 1);
603 break;
606 ldap_controls_free(rcontrols);
608 done:
609 talloc_destroy(ctx);
611 if (extdn_be) {
612 ber_free(extdn_be, 1);
615 if (extdn_bv) {
616 ber_bvfree(extdn_bv);
619 /* if/when we decide to utf8-encode attrs, take out this next line */
620 str_list_free(&search_attrs);
622 return ADS_ERROR(rc);
625 ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
626 int scope, const char *expr,
627 const char **attrs, void **res,
628 int *count, void **cookie)
630 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
635 * Get all results for a search. This uses ads_do_paged_search() to return
636 * all entries in a large search.
637 * @param ads connection to ads server
638 * @param bind_path Base dn for the search
639 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
640 * @param expr Search expression
641 * @param attrs Attributes to retrieve
642 * @param res ** which will contain results - free res* with ads_msgfree()
643 * @return status of search
645 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
646 int scope, const char *expr,
647 const char **attrs, void *args, void **res)
649 void *cookie = NULL;
650 int count = 0;
651 ADS_STATUS status;
653 *res = NULL;
654 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
655 &count, &cookie);
657 if (!ADS_ERR_OK(status))
658 return status;
660 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
661 while (cookie) {
662 void *res2 = NULL;
663 ADS_STATUS status2;
664 LDAPMessage *msg, *next;
666 status2 = ads_do_paged_search_args(ads, bind_path, scope, expr,
667 attrs, args, &res2, &count, &cookie);
669 if (!ADS_ERR_OK(status2)) break;
671 /* this relies on the way that ldap_add_result_entry() works internally. I hope
672 that this works on all ldap libs, but I have only tested with openldap */
673 for (msg = ads_first_entry(ads, res2); msg; msg = next) {
674 next = ads_next_entry(ads, msg);
675 ldap_add_result_entry((LDAPMessage **)res, msg);
677 /* note that we do not free res2, as the memory is now
678 part of the main returned list */
680 #else
681 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
682 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
683 #endif
685 return status;
688 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
689 int scope, const char *expr,
690 const char **attrs, void **res)
692 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
696 * Run a function on all results for a search. Uses ads_do_paged_search() and
697 * runs the function as each page is returned, using ads_process_results()
698 * @param ads connection to ads server
699 * @param bind_path Base dn for the search
700 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
701 * @param expr Search expression - specified in local charset
702 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
703 * @param fn Function which takes attr name, values list, and data_area
704 * @param data_area Pointer which is passed to function on each call
705 * @return status of search
707 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
708 int scope, const char *expr, const char **attrs,
709 BOOL(*fn)(char *, void **, void *),
710 void *data_area)
712 void *cookie = NULL;
713 int count = 0;
714 ADS_STATUS status;
715 void *res;
717 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
718 &count, &cookie);
720 if (!ADS_ERR_OK(status)) return status;
722 ads_process_results(ads, res, fn, data_area);
723 ads_msgfree(ads, res);
725 while (cookie) {
726 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
727 &res, &count, &cookie);
729 if (!ADS_ERR_OK(status)) break;
731 ads_process_results(ads, res, fn, data_area);
732 ads_msgfree(ads, res);
735 return status;
739 * Do a search with a timeout.
740 * @param ads connection to ads server
741 * @param bind_path Base dn for the search
742 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
743 * @param expr Search expression
744 * @param attrs Attributes to retrieve
745 * @param res ** which will contain results - free res* with ads_msgfree()
746 * @return status of search
748 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
749 const char *expr,
750 const char **attrs, void **res)
752 int rc;
753 char *utf8_expr, *utf8_path, **search_attrs = NULL;
754 TALLOC_CTX *ctx;
756 *res = NULL;
757 if (!(ctx = talloc_init("ads_do_search"))) {
758 DEBUG(1,("ads_do_search: talloc_init() failed!"));
759 return ADS_ERROR(LDAP_NO_MEMORY);
762 /* 0 means the conversion worked but the result was empty
763 so we only fail if it's negative. In any case, it always
764 at least nulls out the dest */
765 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
766 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
767 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
768 rc = LDAP_NO_MEMORY;
769 goto done;
772 if (!attrs || !(*attrs))
773 search_attrs = NULL;
774 else {
775 /* This would be the utf8-encoded version...*/
776 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
777 if (!(str_list_copy(&search_attrs, attrs)))
779 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
780 rc = LDAP_NO_MEMORY;
781 goto done;
785 /* see the note in ads_do_paged_search - we *must* disable referrals */
786 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
788 rc = ldap_search_with_timeout(ads->ld, utf8_path, scope, utf8_expr,
789 search_attrs, 0, NULL, NULL,
790 LDAP_NO_LIMIT,
791 (LDAPMessage **)res);
793 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
794 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
795 rc = 0;
798 done:
799 talloc_destroy(ctx);
800 /* if/when we decide to utf8-encode attrs, take out this next line */
801 str_list_free(&search_attrs);
802 return ADS_ERROR(rc);
805 * Do a general ADS search
806 * @param ads connection to ads server
807 * @param res ** which will contain results - free res* with ads_msgfree()
808 * @param expr Search expression
809 * @param attrs Attributes to retrieve
810 * @return status of search
812 ADS_STATUS ads_search(ADS_STRUCT *ads, void **res,
813 const char *expr,
814 const char **attrs)
816 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
817 expr, attrs, res);
821 * Do a search on a specific DistinguishedName
822 * @param ads connection to ads server
823 * @param res ** which will contain results - free res* with ads_msgfree()
824 * @param dn DistinguishName to search
825 * @param attrs Attributes to retrieve
826 * @return status of search
828 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, void **res,
829 const char *dn,
830 const char **attrs)
832 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)", attrs, res);
836 * Free up memory from a ads_search
837 * @param ads connection to ads server
838 * @param msg Search results to free
840 void ads_msgfree(ADS_STRUCT *ads, void *msg)
842 if (!msg) return;
843 ldap_msgfree(msg);
847 * Free up memory from various ads requests
848 * @param ads connection to ads server
849 * @param mem Area to free
851 void ads_memfree(ADS_STRUCT *ads, void *mem)
853 SAFE_FREE(mem);
857 * Get a dn from search results
858 * @param ads connection to ads server
859 * @param msg Search result
860 * @return dn string
862 char *ads_get_dn(ADS_STRUCT *ads, void *msg)
864 char *utf8_dn, *unix_dn;
866 utf8_dn = ldap_get_dn(ads->ld, msg);
868 if (!utf8_dn) {
869 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
870 return NULL;
873 if (pull_utf8_allocate(&unix_dn, utf8_dn) == (size_t)-1) {
874 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
875 utf8_dn ));
876 return NULL;
878 ldap_memfree(utf8_dn);
879 return unix_dn;
883 * Get a canonical dn from search results
884 * @param ads connection to ads server
885 * @param msg Search result
886 * @return dn string
888 char *ads_get_dn_canonical(ADS_STRUCT *ads, void *msg)
890 #ifdef HAVE_LDAP_DN2AD_CANONICAL
891 return ldap_dn2ad_canonical(ads_get_dn(ads, msg));
892 #else
893 return NULL;
894 #endif
898 * Get the parent from a dn
899 * @param dn the dn to return the parent from
900 * @return parent dn string
902 char *ads_parent_dn(const char *dn)
904 char *p;
906 if (dn == NULL) {
907 return NULL;
910 p = strchr(dn, ',');
912 if (p == NULL) {
913 return NULL;
916 return p+1;
920 * Find a machine account given a hostname
921 * @param ads connection to ads server
922 * @param res ** which will contain results - free res* with ads_msgfree()
923 * @param host Hostname to search for
924 * @return status of search
926 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, void **res, const char *machine)
928 ADS_STATUS status;
929 char *expr;
930 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
932 *res = NULL;
934 /* the easiest way to find a machine account anywhere in the tree
935 is to look for hostname$ */
936 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
937 DEBUG(1, ("asprintf failed!\n"));
938 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
941 status = ads_search(ads, res, expr, attrs);
942 SAFE_FREE(expr);
943 return status;
947 * Initialize a list of mods to be used in a modify request
948 * @param ctx An initialized TALLOC_CTX
949 * @return allocated ADS_MODLIST
951 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
953 #define ADS_MODLIST_ALLOC_SIZE 10
954 LDAPMod **mods;
956 if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
957 /* -1 is safety to make sure we don't go over the end.
958 need to reset it to NULL before doing ldap modify */
959 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
961 return (ADS_MODLIST)mods;
966 add an attribute to the list, with values list already constructed
968 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
969 int mod_op, const char *name,
970 const void **invals)
972 int curmod;
973 LDAPMod **modlist = (LDAPMod **) *mods;
974 struct berval **ber_values = NULL;
975 char **char_values = NULL;
977 if (!invals) {
978 mod_op = LDAP_MOD_DELETE;
979 } else {
980 if (mod_op & LDAP_MOD_BVALUES)
981 ber_values = ads_dup_values(ctx,
982 (const struct berval **)invals);
983 else
984 char_values = ads_push_strvals(ctx,
985 (const char **) invals);
988 /* find the first empty slot */
989 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
990 curmod++);
991 if (modlist[curmod] == (LDAPMod *) -1) {
992 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
993 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
994 return ADS_ERROR(LDAP_NO_MEMORY);
995 memset(&modlist[curmod], 0,
996 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
997 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
998 *mods = (ADS_MODLIST)modlist;
1001 if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
1002 return ADS_ERROR(LDAP_NO_MEMORY);
1003 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1004 if (mod_op & LDAP_MOD_BVALUES) {
1005 modlist[curmod]->mod_bvalues = ber_values;
1006 } else if (mod_op & LDAP_MOD_DELETE) {
1007 modlist[curmod]->mod_values = NULL;
1008 } else {
1009 modlist[curmod]->mod_values = char_values;
1012 modlist[curmod]->mod_op = mod_op;
1013 return ADS_ERROR(LDAP_SUCCESS);
1017 * Add a single string value to a mod list
1018 * @param ctx An initialized TALLOC_CTX
1019 * @param mods An initialized ADS_MODLIST
1020 * @param name The attribute name to add
1021 * @param val The value to add - NULL means DELETE
1022 * @return ADS STATUS indicating success of add
1024 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1025 const char *name, const char *val)
1027 const char *values[2];
1029 values[0] = val;
1030 values[1] = NULL;
1032 if (!val)
1033 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1034 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name,
1035 (const void **) values);
1039 * Add an array of string values to a mod list
1040 * @param ctx An initialized TALLOC_CTX
1041 * @param mods An initialized ADS_MODLIST
1042 * @param name The attribute name to add
1043 * @param vals The array of string values to add - NULL means DELETE
1044 * @return ADS STATUS indicating success of add
1046 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1047 const char *name, const char **vals)
1049 if (!vals)
1050 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1051 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1052 name, (const void **) vals);
1055 #if 0
1057 * Add a single ber-encoded value to a mod list
1058 * @param ctx An initialized TALLOC_CTX
1059 * @param mods An initialized ADS_MODLIST
1060 * @param name The attribute name to add
1061 * @param val The value to add - NULL means DELETE
1062 * @return ADS STATUS indicating success of add
1064 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1065 const char *name, const struct berval *val)
1067 const struct berval *values[2];
1069 values[0] = val;
1070 values[1] = NULL;
1071 if (!val)
1072 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1073 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1074 name, (const void **) values);
1076 #endif
1079 * Perform an ldap modify
1080 * @param ads connection to ads server
1081 * @param mod_dn DistinguishedName to modify
1082 * @param mods list of modifications to perform
1083 * @return status of modify
1085 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1087 int ret,i;
1088 char *utf8_dn = NULL;
1090 this control is needed to modify that contains a currently
1091 non-existent attribute (but allowable for the object) to run
1093 LDAPControl PermitModify = {
1094 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
1095 {0, NULL},
1096 (char) 1};
1097 LDAPControl *controls[2];
1099 controls[0] = &PermitModify;
1100 controls[1] = NULL;
1102 if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) {
1103 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1106 /* find the end of the list, marked by NULL or -1 */
1107 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1108 /* make sure the end of the list is NULL */
1109 mods[i] = NULL;
1110 ret = ldap_modify_ext_s(ads->ld, utf8_dn,
1111 (LDAPMod **) mods, controls, NULL);
1112 SAFE_FREE(utf8_dn);
1113 return ADS_ERROR(ret);
1117 * Perform an ldap add
1118 * @param ads connection to ads server
1119 * @param new_dn DistinguishedName to add
1120 * @param mods list of attributes and values for DN
1121 * @return status of add
1123 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1125 int ret, i;
1126 char *utf8_dn = NULL;
1128 if (push_utf8_allocate(&utf8_dn, new_dn) == -1) {
1129 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1130 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1133 /* find the end of the list, marked by NULL or -1 */
1134 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1135 /* make sure the end of the list is NULL */
1136 mods[i] = NULL;
1138 ret = ldap_add_s(ads->ld, utf8_dn, (LDAPMod**)mods);
1139 SAFE_FREE(utf8_dn);
1140 return ADS_ERROR(ret);
1144 * Delete a DistinguishedName
1145 * @param ads connection to ads server
1146 * @param new_dn DistinguishedName to delete
1147 * @return status of delete
1149 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1151 int ret;
1152 char *utf8_dn = NULL;
1153 if (push_utf8_allocate(&utf8_dn, del_dn) == -1) {
1154 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1155 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1158 ret = ldap_delete_s(ads->ld, utf8_dn);
1159 return ADS_ERROR(ret);
1163 * Build an org unit string
1164 * if org unit is Computers or blank then assume a container, otherwise
1165 * assume a / separated list of organisational units.
1166 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1167 * @param ads connection to ads server
1168 * @param org_unit Organizational unit
1169 * @return org unit string - caller must free
1171 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1173 char *ret = NULL;
1175 if (!org_unit || !*org_unit) {
1177 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1179 /* samba4 might not yet respond to a wellknownobject-query */
1180 return ret ? ret : SMB_STRDUP("cn=Computers");
1183 if (strequal(org_unit, "Computers")) {
1184 return SMB_STRDUP("cn=Computers");
1187 /* jmcd: removed "\\" from the separation chars, because it is
1188 needed as an escape for chars like '#' which are valid in an
1189 OU name */
1190 return ads_build_path(org_unit, "/", "ou=", 1);
1194 * Get a org unit string for a well-known GUID
1195 * @param ads connection to ads server
1196 * @param wknguid Well known GUID
1197 * @return org unit string - caller must free
1199 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1201 ADS_STATUS status;
1202 void *res;
1203 char *base, *wkn_dn, *ret, **wkn_dn_exp, **bind_dn_exp;
1204 const char *attrs[] = {"distinguishedName", NULL};
1205 int new_ln, wkn_ln, bind_ln, i;
1207 if (wknguid == NULL) {
1208 return NULL;
1211 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1212 DEBUG(1, ("asprintf failed!\n"));
1213 return NULL;
1216 status = ads_search_dn(ads, &res, base, attrs);
1217 if (!ADS_ERR_OK(status)) {
1218 DEBUG(1,("Failed while searching for: %s\n", base));
1219 SAFE_FREE(base);
1220 return NULL;
1222 SAFE_FREE(base);
1224 if (ads_count_replies(ads, res) != 1) {
1225 return NULL;
1228 /* substitute the bind-path from the well-known-guid-search result */
1229 wkn_dn = ads_get_dn(ads, res);
1230 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1231 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1233 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1235 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1238 new_ln = wkn_ln - bind_ln;
1240 ret = wkn_dn_exp[0];
1242 for (i=1; i < new_ln; i++) {
1243 char *s;
1244 asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]);
1245 ret = SMB_STRDUP(s);
1246 free(s);
1249 ads_memfree(ads, wkn_dn);
1250 ldap_value_free(wkn_dn_exp);
1251 ldap_value_free(bind_dn_exp);
1253 return ret;
1257 * Adds (appends) an item to an attribute array, rather then
1258 * replacing the whole list
1259 * @param ctx An initialized TALLOC_CTX
1260 * @param mods An initialized ADS_MODLIST
1261 * @param name name of the ldap attribute to append to
1262 * @param vals an array of values to add
1263 * @return status of addition
1266 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1267 const char *name, const char **vals)
1269 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name, (const void **) vals);
1273 * Determines the computer account's current KVNO via an LDAP lookup
1274 * @param ads An initialized ADS_STRUCT
1275 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1276 * @return the kvno for the computer account, or -1 in case of a failure.
1279 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *machine_name)
1281 LDAPMessage *res = NULL;
1282 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1283 char *filter;
1284 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1285 char *dn_string = NULL;
1286 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1288 DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name));
1289 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1290 return kvno;
1292 ret = ads_search(ads, (void**)(void *)&res, filter, attrs);
1293 SAFE_FREE(filter);
1294 if (!ADS_ERR_OK(ret) && ads_count_replies(ads, res)) {
1295 DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name));
1296 ads_msgfree(ads, res);
1297 return kvno;
1300 dn_string = ads_get_dn(ads, res);
1301 if (!dn_string) {
1302 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1303 ads_msgfree(ads, res);
1304 return kvno;
1306 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1307 ads_memfree(ads, dn_string);
1309 /* ---------------------------------------------------------
1310 * 0 is returned as a default KVNO from this point on...
1311 * This is done because Windows 2000 does not support key
1312 * version numbers. Chances are that a failure in the next
1313 * step is simply due to Windows 2000 being used for a
1314 * domain controller. */
1315 kvno = 0;
1317 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1318 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1319 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1320 ads_msgfree(ads, res);
1321 return kvno;
1324 /* Success */
1325 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1326 ads_msgfree(ads, res);
1327 return kvno;
1331 * This clears out all registered spn's for a given hostname
1332 * @param ads An initilaized ADS_STRUCT
1333 * @param machine_name the NetBIOS name of the computer.
1334 * @return 0 upon success, non-zero otherwise.
1337 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1339 TALLOC_CTX *ctx;
1340 LDAPMessage *res = NULL;
1341 ADS_MODLIST mods;
1342 const char *servicePrincipalName[1] = {NULL};
1343 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1344 char *dn_string = NULL;
1346 ret = ads_find_machine_acct(ads, (void **)(void *)&res, machine_name);
1347 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1348 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1349 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1350 ads_msgfree(ads, res);
1351 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1354 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1355 ctx = talloc_init("ads_clear_service_principal_names");
1356 if (!ctx) {
1357 ads_msgfree(ads, res);
1358 return ADS_ERROR(LDAP_NO_MEMORY);
1361 if (!(mods = ads_init_mods(ctx))) {
1362 talloc_destroy(ctx);
1363 ads_msgfree(ads, res);
1364 return ADS_ERROR(LDAP_NO_MEMORY);
1366 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1367 if (!ADS_ERR_OK(ret)) {
1368 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1369 ads_msgfree(ads, res);
1370 talloc_destroy(ctx);
1371 return ret;
1373 dn_string = ads_get_dn(ads, res);
1374 if (!dn_string) {
1375 talloc_destroy(ctx);
1376 ads_msgfree(ads, res);
1377 return ADS_ERROR(LDAP_NO_MEMORY);
1379 ret = ads_gen_mod(ads, dn_string, mods);
1380 ads_memfree(ads,dn_string);
1381 if (!ADS_ERR_OK(ret)) {
1382 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1383 machine_name));
1384 ads_msgfree(ads, res);
1385 talloc_destroy(ctx);
1386 return ret;
1389 ads_msgfree(ads, res);
1390 talloc_destroy(ctx);
1391 return ret;
1395 * This adds a service principal name to an existing computer account
1396 * (found by hostname) in AD.
1397 * @param ads An initialized ADS_STRUCT
1398 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1399 * @param my_fqdn The fully qualified DNS name of the machine
1400 * @param spn A string of the service principal to add, i.e. 'host'
1401 * @return 0 upon sucess, or non-zero if a failure occurs
1404 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1405 const char *my_fqdn, const char *spn)
1407 ADS_STATUS ret;
1408 TALLOC_CTX *ctx;
1409 LDAPMessage *res = NULL;
1410 char *psp1, *psp2;
1411 ADS_MODLIST mods;
1412 char *dn_string = NULL;
1413 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1415 ret = ads_find_machine_acct(ads, (void **)(void *)&res, machine_name);
1416 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1417 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1418 machine_name));
1419 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1420 spn, machine_name, ads->config.realm));
1421 ads_msgfree(ads, res);
1422 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1425 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1426 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1427 ads_msgfree(ads, res);
1428 return ADS_ERROR(LDAP_NO_MEMORY);
1431 /* add short name spn */
1433 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1434 talloc_destroy(ctx);
1435 ads_msgfree(ads, res);
1436 return ADS_ERROR(LDAP_NO_MEMORY);
1438 strupper_m(psp1);
1439 strlower_m(&psp1[strlen(spn)]);
1440 servicePrincipalName[0] = psp1;
1442 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1443 psp1, machine_name));
1446 /* add fully qualified spn */
1448 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1449 ret = ADS_ERROR(LDAP_NO_MEMORY);
1450 goto out;
1452 strupper_m(psp2);
1453 strlower_m(&psp2[strlen(spn)]);
1454 servicePrincipalName[1] = psp2;
1456 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1457 psp2, machine_name));
1459 if ( (mods = ads_init_mods(ctx)) == NULL ) {
1460 ret = ADS_ERROR(LDAP_NO_MEMORY);
1461 goto out;
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 goto out;
1470 if ( (dn_string = ads_get_dn(ads, res)) == NULL ) {
1471 ret = ADS_ERROR(LDAP_NO_MEMORY);
1472 goto out;
1475 ret = ads_gen_mod(ads, dn_string, mods);
1476 ads_memfree(ads,dn_string);
1477 if (!ADS_ERR_OK(ret)) {
1478 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1479 goto out;
1482 out:
1483 TALLOC_FREE( ctx );
1484 ads_msgfree(ads, res);
1485 return ret;
1489 * adds a machine account to the ADS server
1490 * @param ads An intialized ADS_STRUCT
1491 * @param machine_name - the NetBIOS machine name of this account.
1492 * @param account_type A number indicating the type of account to create
1493 * @param org_unit The LDAP path in which to place this account
1494 * @return 0 upon success, or non-zero otherwise
1497 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1498 const char *org_unit)
1500 ADS_STATUS ret;
1501 char *samAccountName, *controlstr;
1502 TALLOC_CTX *ctx;
1503 ADS_MODLIST mods;
1504 char *new_dn;
1505 const char *objectClass[] = {"top", "person", "organizationalPerson",
1506 "user", "computer", NULL};
1507 LDAPMessage *res = NULL;
1508 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
1509 UF_DONT_EXPIRE_PASSWD |\
1510 UF_ACCOUNTDISABLE );
1512 if (!(ctx = talloc_init("ads_add_machine_acct")))
1513 return ADS_ERROR(LDAP_NO_MEMORY);
1515 ret = ADS_ERROR(LDAP_NO_MEMORY);
1517 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_name, org_unit);
1518 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
1520 if ( !new_dn || !samAccountName ) {
1521 goto done;
1524 #ifndef ENCTYPE_ARCFOUR_HMAC
1525 acct_control |= UF_USE_DES_KEY_ONLY;
1526 #endif
1528 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
1529 goto done;
1532 if (!(mods = ads_init_mods(ctx))) {
1533 goto done;
1536 ads_mod_str(ctx, &mods, "cn", machine_name);
1537 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1538 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1539 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1541 ret = ads_gen_add(ads, new_dn, mods);
1543 done:
1544 ads_msgfree(ads, res);
1545 talloc_destroy(ctx);
1547 return ret;
1551 dump a binary result from ldap
1553 static void dump_binary(const char *field, struct berval **values)
1555 int i, j;
1556 for (i=0; values[i]; i++) {
1557 printf("%s: ", field);
1558 for (j=0; j<values[i]->bv_len; j++) {
1559 printf("%02X", (unsigned char)values[i]->bv_val[j]);
1561 printf("\n");
1565 static void dump_guid(const char *field, struct berval **values)
1567 int i;
1568 UUID_FLAT guid;
1569 for (i=0; values[i]; i++) {
1570 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1571 printf("%s: %s\n", field,
1572 smb_uuid_string_static(smb_uuid_unpack_static(guid)));
1577 dump a sid result from ldap
1579 static void dump_sid(const char *field, struct berval **values)
1581 int i;
1582 for (i=0; values[i]; i++) {
1583 DOM_SID sid;
1584 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1585 printf("%s: %s\n", field, sid_string_static(&sid));
1590 dump ntSecurityDescriptor
1592 static void dump_sd(const char *filed, struct berval **values)
1594 prs_struct ps;
1596 SEC_DESC *psd = 0;
1597 TALLOC_CTX *ctx = 0;
1599 if (!(ctx = talloc_init("sec_io_desc")))
1600 return;
1602 /* prepare data */
1603 prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
1604 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
1605 prs_set_offset(&ps,0);
1607 /* parse secdesc */
1608 if (!sec_io_desc("sd", &psd, &ps, 1)) {
1609 prs_mem_free(&ps);
1610 talloc_destroy(ctx);
1611 return;
1613 if (psd) ads_disp_sd(psd);
1615 prs_mem_free(&ps);
1616 talloc_destroy(ctx);
1620 dump a string result from ldap
1622 static void dump_string(const char *field, char **values)
1624 int i;
1625 for (i=0; values[i]; i++) {
1626 printf("%s: %s\n", field, values[i]);
1631 dump a field from LDAP on stdout
1632 used for debugging
1635 static BOOL ads_dump_field(char *field, void **values, void *data_area)
1637 const struct {
1638 const char *name;
1639 BOOL string;
1640 void (*handler)(const char *, struct berval **);
1641 } handlers[] = {
1642 {"objectGUID", False, dump_guid},
1643 {"netbootGUID", False, dump_guid},
1644 {"nTSecurityDescriptor", False, dump_sd},
1645 {"dnsRecord", False, dump_binary},
1646 {"objectSid", False, dump_sid},
1647 {"tokenGroups", False, dump_sid},
1648 {"tokenGroupsNoGCAcceptable", False, dump_sid},
1649 {"tokengroupsGlobalandUniversal", False, dump_sid},
1650 {NULL, True, NULL}
1652 int i;
1654 if (!field) { /* must be end of an entry */
1655 printf("\n");
1656 return False;
1659 for (i=0; handlers[i].name; i++) {
1660 if (StrCaseCmp(handlers[i].name, field) == 0) {
1661 if (!values) /* first time, indicate string or not */
1662 return handlers[i].string;
1663 handlers[i].handler(field, (struct berval **) values);
1664 break;
1667 if (!handlers[i].name) {
1668 if (!values) /* first time, indicate string conversion */
1669 return True;
1670 dump_string(field, (char **)values);
1672 return False;
1676 * Dump a result from LDAP on stdout
1677 * used for debugging
1678 * @param ads connection to ads server
1679 * @param res Results to dump
1682 void ads_dump(ADS_STRUCT *ads, void *res)
1684 ads_process_results(ads, res, ads_dump_field, NULL);
1688 * Walk through results, calling a function for each entry found.
1689 * The function receives a field name, a berval * array of values,
1690 * and a data area passed through from the start. The function is
1691 * called once with null for field and values at the end of each
1692 * entry.
1693 * @param ads connection to ads server
1694 * @param res Results to process
1695 * @param fn Function for processing each result
1696 * @param data_area user-defined area to pass to function
1698 void ads_process_results(ADS_STRUCT *ads, void *res,
1699 BOOL(*fn)(char *, void **, void *),
1700 void *data_area)
1702 void *msg;
1703 TALLOC_CTX *ctx;
1705 if (!(ctx = talloc_init("ads_process_results")))
1706 return;
1708 for (msg = ads_first_entry(ads, res); msg;
1709 msg = ads_next_entry(ads, msg)) {
1710 char *utf8_field;
1711 BerElement *b;
1713 for (utf8_field=ldap_first_attribute(ads->ld,
1714 (LDAPMessage *)msg,&b);
1715 utf8_field;
1716 utf8_field=ldap_next_attribute(ads->ld,
1717 (LDAPMessage *)msg,b)) {
1718 struct berval **ber_vals;
1719 char **str_vals, **utf8_vals;
1720 char *field;
1721 BOOL string;
1723 pull_utf8_talloc(ctx, &field, utf8_field);
1724 string = fn(field, NULL, data_area);
1726 if (string) {
1727 utf8_vals = ldap_get_values(ads->ld,
1728 (LDAPMessage *)msg, field);
1729 str_vals = ads_pull_strvals(ctx,
1730 (const char **) utf8_vals);
1731 fn(field, (void **) str_vals, data_area);
1732 ldap_value_free(utf8_vals);
1733 } else {
1734 ber_vals = ldap_get_values_len(ads->ld,
1735 (LDAPMessage *)msg, field);
1736 fn(field, (void **) ber_vals, data_area);
1738 ldap_value_free_len(ber_vals);
1740 ldap_memfree(utf8_field);
1742 ber_free(b, 0);
1743 talloc_free_children(ctx);
1744 fn(NULL, NULL, data_area); /* completed an entry */
1747 talloc_destroy(ctx);
1751 * count how many replies are in a LDAPMessage
1752 * @param ads connection to ads server
1753 * @param res Results to count
1754 * @return number of replies
1756 int ads_count_replies(ADS_STRUCT *ads, void *res)
1758 return ldap_count_entries(ads->ld, (LDAPMessage *)res);
1762 * pull the first entry from a ADS result
1763 * @param ads connection to ads server
1764 * @param res Results of search
1765 * @return first entry from result
1767 void *ads_first_entry(ADS_STRUCT *ads, void *res)
1769 return (void *)ldap_first_entry(ads->ld, (LDAPMessage *)res);
1773 * pull the next entry from a ADS result
1774 * @param ads connection to ads server
1775 * @param res Results of search
1776 * @return next entry from result
1778 void *ads_next_entry(ADS_STRUCT *ads, void *res)
1780 return (void *)ldap_next_entry(ads->ld, (LDAPMessage *)res);
1784 * pull a single string from a ADS result
1785 * @param ads connection to ads server
1786 * @param mem_ctx TALLOC_CTX to use for allocating result string
1787 * @param msg Results of search
1788 * @param field Attribute to retrieve
1789 * @return Result string in talloc context
1791 char *ads_pull_string(ADS_STRUCT *ads,
1792 TALLOC_CTX *mem_ctx, void *msg, const char *field)
1794 char **values;
1795 char *ret = NULL;
1796 char *ux_string;
1797 size_t rc;
1799 values = ldap_get_values(ads->ld, msg, field);
1800 if (!values)
1801 return NULL;
1803 if (values[0]) {
1804 rc = pull_utf8_talloc(mem_ctx, &ux_string,
1805 values[0]);
1806 if (rc != (size_t)-1)
1807 ret = ux_string;
1810 ldap_value_free(values);
1811 return ret;
1815 * pull an array of strings from a ADS result
1816 * @param ads connection to ads server
1817 * @param mem_ctx TALLOC_CTX to use for allocating result string
1818 * @param msg Results of search
1819 * @param field Attribute to retrieve
1820 * @return Result strings in talloc context
1822 char **ads_pull_strings(ADS_STRUCT *ads,
1823 TALLOC_CTX *mem_ctx, void *msg, const char *field,
1824 size_t *num_values)
1826 char **values;
1827 char **ret = NULL;
1828 int i;
1830 values = ldap_get_values(ads->ld, msg, field);
1831 if (!values)
1832 return NULL;
1834 *num_values = ldap_count_values(values);
1836 ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
1837 if (!ret) {
1838 ldap_value_free(values);
1839 return NULL;
1842 for (i=0;i<*num_values;i++) {
1843 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
1844 ldap_value_free(values);
1845 return NULL;
1848 ret[i] = NULL;
1850 ldap_value_free(values);
1851 return ret;
1855 * pull an array of strings from a ADS result
1856 * (handle large multivalue attributes with range retrieval)
1857 * @param ads connection to ads server
1858 * @param mem_ctx TALLOC_CTX to use for allocating result string
1859 * @param msg Results of search
1860 * @param field Attribute to retrieve
1861 * @param current_strings strings returned by a previous call to this function
1862 * @param next_attribute The next query should ask for this attribute
1863 * @param num_values How many values did we get this time?
1864 * @param more_values Are there more values to get?
1865 * @return Result strings in talloc context
1867 char **ads_pull_strings_range(ADS_STRUCT *ads,
1868 TALLOC_CTX *mem_ctx,
1869 void *msg, const char *field,
1870 char **current_strings,
1871 const char **next_attribute,
1872 size_t *num_strings,
1873 BOOL *more_strings)
1875 char *attr;
1876 char *expected_range_attrib, *range_attr;
1877 BerElement *ptr = NULL;
1878 char **strings;
1879 char **new_strings;
1880 size_t num_new_strings;
1881 unsigned long int range_start;
1882 unsigned long int range_end;
1884 /* we might have been given the whole lot anyway */
1885 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
1886 *more_strings = False;
1887 return strings;
1890 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
1892 /* look for Range result */
1893 for (attr = ldap_first_attribute(ads->ld, (LDAPMessage *)msg, &ptr);
1894 attr;
1895 attr = ldap_next_attribute(ads->ld, (LDAPMessage *)msg, ptr)) {
1896 /* we ignore the fact that this is utf8, as all attributes are ascii... */
1897 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
1898 range_attr = attr;
1899 break;
1901 ldap_memfree(attr);
1903 if (!attr) {
1904 ber_free(ptr, 0);
1905 /* nothing here - this field is just empty */
1906 *more_strings = False;
1907 return NULL;
1910 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
1911 &range_start, &range_end) == 2) {
1912 *more_strings = True;
1913 } else {
1914 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
1915 &range_start) == 1) {
1916 *more_strings = False;
1917 } else {
1918 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
1919 range_attr));
1920 ldap_memfree(range_attr);
1921 *more_strings = False;
1922 return NULL;
1926 if ((*num_strings) != range_start) {
1927 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
1928 " - aborting range retreival\n",
1929 range_attr, (unsigned int)(*num_strings) + 1, range_start));
1930 ldap_memfree(range_attr);
1931 *more_strings = False;
1932 return NULL;
1935 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
1937 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
1938 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
1939 "strings in this bunch, but we only got %lu - aborting range retreival\n",
1940 range_attr, (unsigned long int)range_end - range_start + 1,
1941 (unsigned long int)num_new_strings));
1942 ldap_memfree(range_attr);
1943 *more_strings = False;
1944 return NULL;
1947 strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
1948 *num_strings + num_new_strings);
1950 if (strings == NULL) {
1951 ldap_memfree(range_attr);
1952 *more_strings = False;
1953 return NULL;
1956 if (new_strings && num_new_strings) {
1957 memcpy(&strings[*num_strings], new_strings,
1958 sizeof(*new_strings) * num_new_strings);
1961 (*num_strings) += num_new_strings;
1963 if (*more_strings) {
1964 *next_attribute = talloc_asprintf(mem_ctx,
1965 "%s;range=%d-*",
1966 field,
1967 (int)*num_strings);
1969 if (!*next_attribute) {
1970 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
1971 ldap_memfree(range_attr);
1972 *more_strings = False;
1973 return NULL;
1977 ldap_memfree(range_attr);
1979 return strings;
1983 * pull a single uint32 from a ADS result
1984 * @param ads connection to ads server
1985 * @param msg Results of search
1986 * @param field Attribute to retrieve
1987 * @param v Pointer to int to store result
1988 * @return boolean inidicating success
1990 BOOL ads_pull_uint32(ADS_STRUCT *ads,
1991 void *msg, const char *field, uint32 *v)
1993 char **values;
1995 values = ldap_get_values(ads->ld, msg, field);
1996 if (!values)
1997 return False;
1998 if (!values[0]) {
1999 ldap_value_free(values);
2000 return False;
2003 *v = atoi(values[0]);
2004 ldap_value_free(values);
2005 return True;
2009 * pull a single objectGUID from an ADS result
2010 * @param ads connection to ADS server
2011 * @param msg results of search
2012 * @param guid 37-byte area to receive text guid
2013 * @return boolean indicating success
2015 BOOL ads_pull_guid(ADS_STRUCT *ads,
2016 void *msg, struct uuid *guid)
2018 char **values;
2019 UUID_FLAT flat_guid;
2021 values = ldap_get_values(ads->ld, msg, "objectGUID");
2022 if (!values)
2023 return False;
2025 if (values[0]) {
2026 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2027 smb_uuid_unpack(flat_guid, guid);
2028 ldap_value_free(values);
2029 return True;
2031 ldap_value_free(values);
2032 return False;
2038 * pull a single DOM_SID from a ADS result
2039 * @param ads connection to ads server
2040 * @param msg Results of search
2041 * @param field Attribute to retrieve
2042 * @param sid Pointer to sid to store result
2043 * @return boolean inidicating success
2045 BOOL ads_pull_sid(ADS_STRUCT *ads,
2046 void *msg, const char *field, DOM_SID *sid)
2048 struct berval **values;
2049 BOOL ret = False;
2051 values = ldap_get_values_len(ads->ld, msg, field);
2053 if (!values)
2054 return False;
2056 if (values[0])
2057 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2059 ldap_value_free_len(values);
2060 return ret;
2064 * pull an array of DOM_SIDs from a ADS result
2065 * @param ads connection to ads server
2066 * @param mem_ctx TALLOC_CTX for allocating sid array
2067 * @param msg Results of search
2068 * @param field Attribute to retrieve
2069 * @param sids pointer to sid array to allocate
2070 * @return the count of SIDs pulled
2072 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2073 void *msg, const char *field, DOM_SID **sids)
2075 struct berval **values;
2076 BOOL ret;
2077 int count, i;
2079 values = ldap_get_values_len(ads->ld, msg, field);
2081 if (!values)
2082 return 0;
2084 for (i=0; values[i]; i++)
2085 /* nop */ ;
2087 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2088 if (!(*sids)) {
2089 ldap_value_free_len(values);
2090 return 0;
2093 count = 0;
2094 for (i=0; values[i]; i++) {
2095 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2096 if (ret) {
2097 fstring sid;
2098 DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid, &(*sids)[count])));
2099 count++;
2103 ldap_value_free_len(values);
2104 return count;
2108 * pull a SEC_DESC from a ADS result
2109 * @param ads connection to ads server
2110 * @param mem_ctx TALLOC_CTX for allocating sid array
2111 * @param msg Results of search
2112 * @param field Attribute to retrieve
2113 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2114 * @return boolean inidicating success
2116 BOOL ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2117 void *msg, const char *field, SEC_DESC **sd)
2119 struct berval **values;
2120 prs_struct ps;
2121 BOOL ret = False;
2123 values = ldap_get_values_len(ads->ld, msg, field);
2125 if (!values) return False;
2127 if (values[0]) {
2128 prs_init(&ps, values[0]->bv_len, mem_ctx, UNMARSHALL);
2129 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
2130 prs_set_offset(&ps,0);
2132 ret = sec_io_desc("sd", sd, &ps, 1);
2135 ldap_value_free_len(values);
2136 return ret;
2140 * in order to support usernames longer than 21 characters we need to
2141 * use both the sAMAccountName and the userPrincipalName attributes
2142 * It seems that not all users have the userPrincipalName attribute set
2144 * @param ads connection to ads server
2145 * @param mem_ctx TALLOC_CTX for allocating sid array
2146 * @param msg Results of search
2147 * @return the username
2149 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, void *msg)
2151 #if 0 /* JERRY */
2152 char *ret, *p;
2154 /* lookup_name() only works on the sAMAccountName to
2155 returning the username portion of userPrincipalName
2156 breaks winbindd_getpwnam() */
2158 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2159 if (ret && (p = strchr_m(ret, '@'))) {
2160 *p = 0;
2161 return ret;
2163 #endif
2164 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2169 * find the update serial number - this is the core of the ldap cache
2170 * @param ads connection to ads server
2171 * @param ads connection to ADS server
2172 * @param usn Pointer to retrieved update serial number
2173 * @return status of search
2175 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2177 const char *attrs[] = {"highestCommittedUSN", NULL};
2178 ADS_STATUS status;
2179 void *res;
2181 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2182 if (!ADS_ERR_OK(status))
2183 return status;
2185 if (ads_count_replies(ads, res) != 1) {
2186 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2189 ads_pull_uint32(ads, res, "highestCommittedUSN", usn);
2190 ads_msgfree(ads, res);
2191 return ADS_SUCCESS;
2194 /* parse a ADS timestring - typical string is
2195 '20020917091222.0Z0' which means 09:12.22 17th September
2196 2002, timezone 0 */
2197 static time_t ads_parse_time(const char *str)
2199 struct tm tm;
2201 ZERO_STRUCT(tm);
2203 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2204 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2205 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2206 return 0;
2208 tm.tm_year -= 1900;
2209 tm.tm_mon -= 1;
2211 return timegm(&tm);
2214 /********************************************************************
2215 ********************************************************************/
2217 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2219 const char *attrs[] = {"currentTime", NULL};
2220 ADS_STATUS status;
2221 void *res;
2222 char *timestr;
2223 TALLOC_CTX *ctx;
2224 ADS_STRUCT *ads_s = ads;
2226 if (!(ctx = talloc_init("ads_current_time"))) {
2227 return ADS_ERROR(LDAP_NO_MEMORY);
2230 /* establish a new ldap tcp session if necessary */
2232 if ( !ads->ld ) {
2233 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2234 ads->server.ldap_server )) == NULL )
2236 goto done;
2238 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2239 status = ads_connect( ads_s );
2240 if ( !ADS_ERR_OK(status))
2241 goto done;
2244 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2245 if (!ADS_ERR_OK(status)) {
2246 goto done;
2249 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2250 if (!timestr) {
2251 ads_msgfree(ads, res);
2252 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2253 goto done;
2256 /* but save the time and offset in the original ADS_STRUCT */
2258 ads->config.current_time = ads_parse_time(timestr);
2260 if (ads->config.current_time != 0) {
2261 ads->auth.time_offset = ads->config.current_time - time(NULL);
2262 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2265 ads_msgfree(ads, res);
2267 status = ADS_SUCCESS;
2269 done:
2270 /* free any temporary ads connections */
2271 if ( ads_s != ads ) {
2272 ads_destroy( &ads_s );
2274 talloc_destroy(ctx);
2276 return status;
2279 /********************************************************************
2280 ********************************************************************/
2282 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2284 const char *attrs[] = {"domainFunctionality", NULL};
2285 ADS_STATUS status;
2286 void *res;
2287 ADS_STRUCT *ads_s = ads;
2289 *val = DS_DOMAIN_FUNCTION_2000;
2291 /* establish a new ldap tcp session if necessary */
2293 if ( !ads->ld ) {
2294 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2295 ads->server.ldap_server )) == NULL )
2297 goto done;
2299 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2300 status = ads_connect( ads_s );
2301 if ( !ADS_ERR_OK(status))
2302 goto done;
2305 /* If the attribute does not exist assume it is a Windows 2000
2306 functional domain */
2308 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2309 if (!ADS_ERR_OK(status)) {
2310 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2311 status = ADS_SUCCESS;
2313 goto done;
2316 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2317 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2319 DEBUG(3,("ads_domain_func_level: %d\n", *val));
2322 ads_msgfree(ads, res);
2324 done:
2325 /* free any temporary ads connections */
2326 if ( ads_s != ads ) {
2327 ads_destroy( &ads_s );
2330 return status;
2334 * find the domain sid for our domain
2335 * @param ads connection to ads server
2336 * @param sid Pointer to domain sid
2337 * @return status of search
2339 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2341 const char *attrs[] = {"objectSid", NULL};
2342 void *res;
2343 ADS_STATUS rc;
2345 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2346 attrs, &res);
2347 if (!ADS_ERR_OK(rc)) return rc;
2348 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2349 ads_msgfree(ads, res);
2350 return ADS_ERROR_SYSTEM(ENOENT);
2352 ads_msgfree(ads, res);
2354 return ADS_SUCCESS;
2358 * find our site name
2359 * @param ads connection to ads server
2360 * @param mem_ctx Pointer to talloc context
2361 * @param site_name Pointer to the sitename
2362 * @return status of search
2364 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2366 ADS_STATUS status;
2367 void *res;
2368 const char *dn, *service_name;
2369 const char *attrs[] = { "dsServiceName", NULL };
2371 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2372 if (!ADS_ERR_OK(status)) {
2373 return status;
2376 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2377 if (service_name == NULL) {
2378 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2381 /* go up three levels */
2382 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2383 if (dn == NULL) {
2384 return ADS_ERROR(LDAP_NO_MEMORY);
2387 *site_name = talloc_strdup(mem_ctx, dn);
2388 if (*site_name == NULL) {
2389 return ADS_ERROR(LDAP_NO_MEMORY);
2392 ads_msgfree(ads, res);
2394 return status;
2396 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2401 * find the site dn where a machine resides
2402 * @param ads connection to ads server
2403 * @param mem_ctx Pointer to talloc context
2404 * @param computer_name name of the machine
2405 * @param site_name Pointer to the sitename
2406 * @return status of search
2408 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
2410 ADS_STATUS status;
2411 void *res;
2412 const char *parent, *config_context, *filter;
2413 const char *attrs[] = { "configurationNamingContext", NULL };
2414 char *dn;
2416 /* shortcut a query */
2417 if (strequal(computer_name, ads->config.ldap_server_name)) {
2418 return ads_site_dn(ads, mem_ctx, site_dn);
2421 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2422 if (!ADS_ERR_OK(status)) {
2423 return status;
2426 config_context = ads_pull_string(ads, mem_ctx, res, "configurationNamingContext");
2427 if (config_context == NULL) {
2428 return ADS_ERROR(LDAP_NO_MEMORY);
2431 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
2432 if (filter == NULL) {
2433 return ADS_ERROR(LDAP_NO_MEMORY);
2436 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE, filter, NULL, &res);
2437 if (!ADS_ERR_OK(status)) {
2438 return status;
2441 if (ads_count_replies(ads, res) != 1) {
2442 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2445 dn = ads_get_dn(ads, res);
2446 if (dn == NULL) {
2447 return ADS_ERROR(LDAP_NO_MEMORY);
2450 /* go up three levels */
2451 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
2452 if (parent == NULL) {
2453 ads_memfree(ads, dn);
2454 return ADS_ERROR(LDAP_NO_MEMORY);
2457 *site_dn = talloc_strdup(mem_ctx, parent);
2458 if (*site_dn == NULL) {
2459 ads_memfree(ads, dn);
2460 ADS_ERROR(LDAP_NO_MEMORY);
2463 ads_memfree(ads, dn);
2464 ads_msgfree(ads, res);
2466 return status;
2470 * get the upn suffixes for a domain
2471 * @param ads connection to ads server
2472 * @param mem_ctx Pointer to talloc context
2473 * @param suffixes Pointer to an array of suffixes
2474 * @param site_name Pointer to the number of suffixes
2475 * @return status of search
2477 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char **suffixes, size_t *num_suffixes)
2479 ADS_STATUS status;
2480 void *res;
2481 const char *config_context, *base;
2482 const char *attrs[] = { "configurationNamingContext", NULL };
2483 const char *attrs2[] = { "uPNSuffixes", NULL };
2485 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2486 if (!ADS_ERR_OK(status)) {
2487 return status;
2490 config_context = ads_pull_string(ads, mem_ctx, res, "configurationNamingContext");
2491 if (config_context == NULL) {
2492 return ADS_ERROR(LDAP_NO_MEMORY);
2495 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
2496 if (base == NULL) {
2497 return ADS_ERROR(LDAP_NO_MEMORY);
2500 status = ads_search_dn(ads, &res, base, attrs2);
2501 if (!ADS_ERR_OK(status)) {
2502 return status;
2505 if (ads_count_replies(ads, res) != 1) {
2506 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2509 suffixes = ads_pull_strings(ads, mem_ctx, &res, "uPNSuffixes", num_suffixes);
2510 if (suffixes == NULL) {
2511 ads_msgfree(ads, res);
2512 return ADS_ERROR(LDAP_NO_MEMORY);
2515 ads_msgfree(ads, res);
2517 return status;
2521 * pull a DOM_SID from an extended dn string
2522 * @param mem_ctx TALLOC_CTX
2523 * @param flags string type of extended_dn
2524 * @param sid pointer to a DOM_SID
2525 * @return boolean inidicating success
2527 BOOL ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
2528 const char *dn,
2529 enum ads_extended_dn_flags flags,
2530 DOM_SID *sid)
2532 char *p, *q;
2534 if (!dn) {
2535 return False;
2539 * ADS_EXTENDED_DN_HEX_STRING:
2540 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
2542 * ADS_EXTENDED_DN_STRING (only with w2k3):
2543 <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
2546 p = strchr(dn, ';');
2547 if (!p) {
2548 return False;
2551 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
2552 return False;
2555 p += strlen(";<SID=");
2557 q = strchr(p, '>');
2558 if (!q) {
2559 return False;
2562 *q = '\0';
2564 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
2566 switch (flags) {
2568 case ADS_EXTENDED_DN_STRING:
2569 if (!string_to_sid(sid, p)) {
2570 return False;
2572 break;
2573 case ADS_EXTENDED_DN_HEX_STRING: {
2574 pstring buf;
2575 size_t buf_len;
2577 buf_len = strhex_to_str(buf, strlen(p), p);
2578 if (buf_len == 0) {
2579 return False;
2582 if (!sid_parse(buf, buf_len, sid)) {
2583 DEBUG(10,("failed to parse sid\n"));
2584 return False;
2586 break;
2588 default:
2589 DEBUG(10,("unknown extended dn format\n"));
2590 return False;
2593 return True;
2597 * pull an array of DOM_SIDs from a ADS result
2598 * @param ads connection to ads server
2599 * @param mem_ctx TALLOC_CTX for allocating sid array
2600 * @param msg Results of search
2601 * @param field Attribute to retrieve
2602 * @param flags string type of extended_dn
2603 * @param sids pointer to sid array to allocate
2604 * @return the count of SIDs pulled
2606 int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads,
2607 TALLOC_CTX *mem_ctx,
2608 void *msg,
2609 const char *field,
2610 enum ads_extended_dn_flags flags,
2611 DOM_SID **sids)
2613 int i;
2614 size_t dn_count;
2615 char **dn_strings;
2617 if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field,
2618 &dn_count)) == NULL) {
2619 return 0;
2622 (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, dn_count + 1);
2623 if (!(*sids)) {
2624 TALLOC_FREE(dn_strings);
2625 return 0;
2628 for (i=0; i<dn_count; i++) {
2630 if (!ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i],
2631 flags, &(*sids)[i])) {
2632 TALLOC_FREE(*sids);
2633 TALLOC_FREE(dn_strings);
2634 return 0;
2638 TALLOC_FREE(dn_strings);
2640 return dn_count;
2643 /********************************************************************
2644 ********************************************************************/
2646 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2648 LDAPMessage *res = NULL;
2649 ADS_STATUS status;
2650 int count = 0;
2651 char *name = NULL;
2653 status = ads_find_machine_acct(ads, (void **)(void *)&res, global_myname());
2654 if (!ADS_ERR_OK(status)) {
2655 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
2656 global_myname()));
2657 goto out;
2660 if ( (count = ads_count_replies(ads, res)) != 1 ) {
2661 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
2662 goto out;
2665 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
2666 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
2669 out:
2670 ads_msgfree(ads, res);
2672 return name;
2675 /********************************************************************
2676 ********************************************************************/
2678 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2680 LDAPMessage *res = NULL;
2681 ADS_STATUS status;
2682 int count = 0;
2683 char *name = NULL;
2685 status = ads_find_machine_acct(ads, (void **)(void *)&res, global_myname());
2686 if (!ADS_ERR_OK(status)) {
2687 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
2688 global_myname()));
2689 goto out;
2692 if ( (count = ads_count_replies(ads, res)) != 1 ) {
2693 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
2694 goto out;
2697 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
2698 DEBUG(0,("ads_get_dnshostname: No userPrincipalName attribute!\n"));
2701 out:
2702 ads_msgfree(ads, res);
2704 return name;
2707 /********************************************************************
2708 ********************************************************************/
2710 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2712 LDAPMessage *res = NULL;
2713 ADS_STATUS status;
2714 int count = 0;
2715 char *name = NULL;
2717 status = ads_find_machine_acct(ads, (void **)(void *)&res, global_myname());
2718 if (!ADS_ERR_OK(status)) {
2719 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
2720 global_myname()));
2721 goto out;
2724 if ( (count = ads_count_replies(ads, res)) != 1 ) {
2725 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
2726 goto out;
2729 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
2730 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
2733 out:
2734 ads_msgfree(ads, res);
2736 return name;
2739 #endif