Samba 3: added Samba 3.0.24 sources
[tomato.git] / release / src / router / samba3 / source / libads / ldap.c
blobd4d97cf376bf6a7cf411f7a9039246c513e29369
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 SAFE_FREE(utf8_dn);
1160 return ADS_ERROR(ret);
1164 * Build an org unit string
1165 * if org unit is Computers or blank then assume a container, otherwise
1166 * assume a / separated list of organisational units.
1167 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1168 * @param ads connection to ads server
1169 * @param org_unit Organizational unit
1170 * @return org unit string - caller must free
1172 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1174 char *ret = NULL;
1176 if (!org_unit || !*org_unit) {
1178 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1180 /* samba4 might not yet respond to a wellknownobject-query */
1181 return ret ? ret : SMB_STRDUP("cn=Computers");
1184 if (strequal(org_unit, "Computers")) {
1185 return SMB_STRDUP("cn=Computers");
1188 /* jmcd: removed "\\" from the separation chars, because it is
1189 needed as an escape for chars like '#' which are valid in an
1190 OU name */
1191 return ads_build_path(org_unit, "/", "ou=", 1);
1195 * Get a org unit string for a well-known GUID
1196 * @param ads connection to ads server
1197 * @param wknguid Well known GUID
1198 * @return org unit string - caller must free
1200 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1202 ADS_STATUS status;
1203 void *res = NULL;
1204 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL, **bind_dn_exp = NULL;
1205 const char *attrs[] = {"distinguishedName", NULL};
1206 int new_ln, wkn_ln, bind_ln, i;
1208 if (wknguid == NULL) {
1209 return NULL;
1212 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1213 DEBUG(1, ("asprintf failed!\n"));
1214 return NULL;
1217 status = ads_search_dn(ads, &res, base, attrs);
1218 if (!ADS_ERR_OK(status)) {
1219 DEBUG(1,("Failed while searching for: %s\n", base));
1220 goto out;
1223 if (ads_count_replies(ads, res) != 1) {
1224 goto out;
1227 /* substitute the bind-path from the well-known-guid-search result */
1228 wkn_dn = ads_get_dn(ads, res);
1229 if (!wkn_dn) {
1230 goto out;
1233 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1234 if (!wkn_dn_exp) {
1235 goto out;
1238 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1239 if (!bind_dn_exp) {
1240 goto out;
1243 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1245 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1248 new_ln = wkn_ln - bind_ln;
1250 ret = SMB_STRDUP(wkn_dn_exp[0]);
1251 if (!ret) {
1252 goto out;
1255 for (i=1; i < new_ln; i++) {
1256 char *s = NULL;
1258 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1259 SAFE_FREE(ret);
1260 goto out;
1263 SAFE_FREE(ret);
1264 ret = SMB_STRDUP(s);
1265 free(s);
1266 if (!ret) {
1267 goto out;
1271 out:
1272 SAFE_FREE(base);
1273 ads_msgfree(ads, res);
1274 ads_memfree(ads, wkn_dn);
1275 if (wkn_dn_exp) {
1276 ldap_value_free(wkn_dn_exp);
1278 if (bind_dn_exp) {
1279 ldap_value_free(bind_dn_exp);
1282 return ret;
1286 * Adds (appends) an item to an attribute array, rather then
1287 * replacing the whole list
1288 * @param ctx An initialized TALLOC_CTX
1289 * @param mods An initialized ADS_MODLIST
1290 * @param name name of the ldap attribute to append to
1291 * @param vals an array of values to add
1292 * @return status of addition
1295 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1296 const char *name, const char **vals)
1298 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name, (const void **) vals);
1302 * Determines the computer account's current KVNO via an LDAP lookup
1303 * @param ads An initialized ADS_STRUCT
1304 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1305 * @return the kvno for the computer account, or -1 in case of a failure.
1308 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *machine_name)
1310 LDAPMessage *res = NULL;
1311 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1312 char *filter;
1313 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1314 char *dn_string = NULL;
1315 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1317 DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name));
1318 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1319 return kvno;
1321 ret = ads_search(ads, (void**)(void *)&res, filter, attrs);
1322 SAFE_FREE(filter);
1323 if (!ADS_ERR_OK(ret) && ads_count_replies(ads, res)) {
1324 DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name));
1325 ads_msgfree(ads, res);
1326 return kvno;
1329 dn_string = ads_get_dn(ads, res);
1330 if (!dn_string) {
1331 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1332 ads_msgfree(ads, res);
1333 return kvno;
1335 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1336 ads_memfree(ads, dn_string);
1338 /* ---------------------------------------------------------
1339 * 0 is returned as a default KVNO from this point on...
1340 * This is done because Windows 2000 does not support key
1341 * version numbers. Chances are that a failure in the next
1342 * step is simply due to Windows 2000 being used for a
1343 * domain controller. */
1344 kvno = 0;
1346 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1347 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1348 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1349 ads_msgfree(ads, res);
1350 return kvno;
1353 /* Success */
1354 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1355 ads_msgfree(ads, res);
1356 return kvno;
1360 * This clears out all registered spn's for a given hostname
1361 * @param ads An initilaized ADS_STRUCT
1362 * @param machine_name the NetBIOS name of the computer.
1363 * @return 0 upon success, non-zero otherwise.
1366 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1368 TALLOC_CTX *ctx;
1369 LDAPMessage *res = NULL;
1370 ADS_MODLIST mods;
1371 const char *servicePrincipalName[1] = {NULL};
1372 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1373 char *dn_string = NULL;
1375 ret = ads_find_machine_acct(ads, (void **)(void *)&res, machine_name);
1376 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1377 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1378 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1379 ads_msgfree(ads, res);
1380 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1383 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1384 ctx = talloc_init("ads_clear_service_principal_names");
1385 if (!ctx) {
1386 ads_msgfree(ads, res);
1387 return ADS_ERROR(LDAP_NO_MEMORY);
1390 if (!(mods = ads_init_mods(ctx))) {
1391 talloc_destroy(ctx);
1392 ads_msgfree(ads, res);
1393 return ADS_ERROR(LDAP_NO_MEMORY);
1395 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1396 if (!ADS_ERR_OK(ret)) {
1397 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1398 ads_msgfree(ads, res);
1399 talloc_destroy(ctx);
1400 return ret;
1402 dn_string = ads_get_dn(ads, res);
1403 if (!dn_string) {
1404 talloc_destroy(ctx);
1405 ads_msgfree(ads, res);
1406 return ADS_ERROR(LDAP_NO_MEMORY);
1408 ret = ads_gen_mod(ads, dn_string, mods);
1409 ads_memfree(ads,dn_string);
1410 if (!ADS_ERR_OK(ret)) {
1411 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1412 machine_name));
1413 ads_msgfree(ads, res);
1414 talloc_destroy(ctx);
1415 return ret;
1418 ads_msgfree(ads, res);
1419 talloc_destroy(ctx);
1420 return ret;
1424 * This adds a service principal name to an existing computer account
1425 * (found by hostname) in AD.
1426 * @param ads An initialized ADS_STRUCT
1427 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1428 * @param my_fqdn The fully qualified DNS name of the machine
1429 * @param spn A string of the service principal to add, i.e. 'host'
1430 * @return 0 upon sucess, or non-zero if a failure occurs
1433 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1434 const char *my_fqdn, const char *spn)
1436 ADS_STATUS ret;
1437 TALLOC_CTX *ctx;
1438 LDAPMessage *res = NULL;
1439 char *psp1, *psp2;
1440 ADS_MODLIST mods;
1441 char *dn_string = NULL;
1442 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1444 ret = ads_find_machine_acct(ads, (void **)(void *)&res, machine_name);
1445 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1446 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1447 machine_name));
1448 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1449 spn, machine_name, ads->config.realm));
1450 ads_msgfree(ads, res);
1451 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1454 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1455 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1456 ads_msgfree(ads, res);
1457 return ADS_ERROR(LDAP_NO_MEMORY);
1460 /* add short name spn */
1462 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1463 talloc_destroy(ctx);
1464 ads_msgfree(ads, res);
1465 return ADS_ERROR(LDAP_NO_MEMORY);
1467 strupper_m(psp1);
1468 strlower_m(&psp1[strlen(spn)]);
1469 servicePrincipalName[0] = psp1;
1471 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1472 psp1, machine_name));
1475 /* add fully qualified spn */
1477 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1478 ret = ADS_ERROR(LDAP_NO_MEMORY);
1479 goto out;
1481 strupper_m(psp2);
1482 strlower_m(&psp2[strlen(spn)]);
1483 servicePrincipalName[1] = psp2;
1485 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1486 psp2, machine_name));
1488 if ( (mods = ads_init_mods(ctx)) == NULL ) {
1489 ret = ADS_ERROR(LDAP_NO_MEMORY);
1490 goto out;
1493 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1494 if (!ADS_ERR_OK(ret)) {
1495 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1496 goto out;
1499 if ( (dn_string = ads_get_dn(ads, res)) == NULL ) {
1500 ret = ADS_ERROR(LDAP_NO_MEMORY);
1501 goto out;
1504 ret = ads_gen_mod(ads, dn_string, mods);
1505 ads_memfree(ads,dn_string);
1506 if (!ADS_ERR_OK(ret)) {
1507 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1508 goto out;
1511 out:
1512 TALLOC_FREE( ctx );
1513 ads_msgfree(ads, res);
1514 return ret;
1518 * adds a machine account to the ADS server
1519 * @param ads An intialized ADS_STRUCT
1520 * @param machine_name - the NetBIOS machine name of this account.
1521 * @param account_type A number indicating the type of account to create
1522 * @param org_unit The LDAP path in which to place this account
1523 * @return 0 upon success, or non-zero otherwise
1526 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1527 const char *org_unit)
1529 ADS_STATUS ret;
1530 char *samAccountName, *controlstr;
1531 TALLOC_CTX *ctx;
1532 ADS_MODLIST mods;
1533 char *new_dn;
1534 const char *objectClass[] = {"top", "person", "organizationalPerson",
1535 "user", "computer", NULL};
1536 LDAPMessage *res = NULL;
1537 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
1538 UF_DONT_EXPIRE_PASSWD |\
1539 UF_ACCOUNTDISABLE );
1541 if (!(ctx = talloc_init("ads_add_machine_acct")))
1542 return ADS_ERROR(LDAP_NO_MEMORY);
1544 ret = ADS_ERROR(LDAP_NO_MEMORY);
1546 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_name, org_unit);
1547 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
1549 if ( !new_dn || !samAccountName ) {
1550 goto done;
1553 #ifndef ENCTYPE_ARCFOUR_HMAC
1554 acct_control |= UF_USE_DES_KEY_ONLY;
1555 #endif
1557 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
1558 goto done;
1561 if (!(mods = ads_init_mods(ctx))) {
1562 goto done;
1565 ads_mod_str(ctx, &mods, "cn", machine_name);
1566 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1567 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1568 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1570 ret = ads_gen_add(ads, new_dn, mods);
1572 done:
1573 ads_msgfree(ads, res);
1574 talloc_destroy(ctx);
1576 return ret;
1580 dump a binary result from ldap
1582 static void dump_binary(const char *field, struct berval **values)
1584 int i, j;
1585 for (i=0; values[i]; i++) {
1586 printf("%s: ", field);
1587 for (j=0; j<values[i]->bv_len; j++) {
1588 printf("%02X", (unsigned char)values[i]->bv_val[j]);
1590 printf("\n");
1594 static void dump_guid(const char *field, struct berval **values)
1596 int i;
1597 UUID_FLAT guid;
1598 for (i=0; values[i]; i++) {
1599 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1600 printf("%s: %s\n", field,
1601 smb_uuid_string_static(smb_uuid_unpack_static(guid)));
1606 dump a sid result from ldap
1608 static void dump_sid(const char *field, struct berval **values)
1610 int i;
1611 for (i=0; values[i]; i++) {
1612 DOM_SID sid;
1613 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1614 printf("%s: %s\n", field, sid_string_static(&sid));
1619 dump ntSecurityDescriptor
1621 static void dump_sd(const char *filed, struct berval **values)
1623 prs_struct ps;
1625 SEC_DESC *psd = 0;
1626 TALLOC_CTX *ctx = 0;
1628 if (!(ctx = talloc_init("sec_io_desc")))
1629 return;
1631 /* prepare data */
1632 prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
1633 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
1634 prs_set_offset(&ps,0);
1636 /* parse secdesc */
1637 if (!sec_io_desc("sd", &psd, &ps, 1)) {
1638 prs_mem_free(&ps);
1639 talloc_destroy(ctx);
1640 return;
1642 if (psd) ads_disp_sd(psd);
1644 prs_mem_free(&ps);
1645 talloc_destroy(ctx);
1649 dump a string result from ldap
1651 static void dump_string(const char *field, char **values)
1653 int i;
1654 for (i=0; values[i]; i++) {
1655 printf("%s: %s\n", field, values[i]);
1660 dump a field from LDAP on stdout
1661 used for debugging
1664 static BOOL ads_dump_field(char *field, void **values, void *data_area)
1666 const struct {
1667 const char *name;
1668 BOOL string;
1669 void (*handler)(const char *, struct berval **);
1670 } handlers[] = {
1671 {"objectGUID", False, dump_guid},
1672 {"netbootGUID", False, dump_guid},
1673 {"nTSecurityDescriptor", False, dump_sd},
1674 {"dnsRecord", False, dump_binary},
1675 {"objectSid", False, dump_sid},
1676 {"tokenGroups", False, dump_sid},
1677 {"tokenGroupsNoGCAcceptable", False, dump_sid},
1678 {"tokengroupsGlobalandUniversal", False, dump_sid},
1679 {NULL, True, NULL}
1681 int i;
1683 if (!field) { /* must be end of an entry */
1684 printf("\n");
1685 return False;
1688 for (i=0; handlers[i].name; i++) {
1689 if (StrCaseCmp(handlers[i].name, field) == 0) {
1690 if (!values) /* first time, indicate string or not */
1691 return handlers[i].string;
1692 handlers[i].handler(field, (struct berval **) values);
1693 break;
1696 if (!handlers[i].name) {
1697 if (!values) /* first time, indicate string conversion */
1698 return True;
1699 dump_string(field, (char **)values);
1701 return False;
1705 * Dump a result from LDAP on stdout
1706 * used for debugging
1707 * @param ads connection to ads server
1708 * @param res Results to dump
1711 void ads_dump(ADS_STRUCT *ads, void *res)
1713 ads_process_results(ads, res, ads_dump_field, NULL);
1717 * Walk through results, calling a function for each entry found.
1718 * The function receives a field name, a berval * array of values,
1719 * and a data area passed through from the start. The function is
1720 * called once with null for field and values at the end of each
1721 * entry.
1722 * @param ads connection to ads server
1723 * @param res Results to process
1724 * @param fn Function for processing each result
1725 * @param data_area user-defined area to pass to function
1727 void ads_process_results(ADS_STRUCT *ads, void *res,
1728 BOOL(*fn)(char *, void **, void *),
1729 void *data_area)
1731 void *msg;
1732 TALLOC_CTX *ctx;
1734 if (!(ctx = talloc_init("ads_process_results")))
1735 return;
1737 for (msg = ads_first_entry(ads, res); msg;
1738 msg = ads_next_entry(ads, msg)) {
1739 char *utf8_field;
1740 BerElement *b;
1742 for (utf8_field=ldap_first_attribute(ads->ld,
1743 (LDAPMessage *)msg,&b);
1744 utf8_field;
1745 utf8_field=ldap_next_attribute(ads->ld,
1746 (LDAPMessage *)msg,b)) {
1747 struct berval **ber_vals;
1748 char **str_vals, **utf8_vals;
1749 char *field;
1750 BOOL string;
1752 pull_utf8_talloc(ctx, &field, utf8_field);
1753 string = fn(field, NULL, data_area);
1755 if (string) {
1756 utf8_vals = ldap_get_values(ads->ld,
1757 (LDAPMessage *)msg, field);
1758 str_vals = ads_pull_strvals(ctx,
1759 (const char **) utf8_vals);
1760 fn(field, (void **) str_vals, data_area);
1761 ldap_value_free(utf8_vals);
1762 } else {
1763 ber_vals = ldap_get_values_len(ads->ld,
1764 (LDAPMessage *)msg, field);
1765 fn(field, (void **) ber_vals, data_area);
1767 ldap_value_free_len(ber_vals);
1769 ldap_memfree(utf8_field);
1771 ber_free(b, 0);
1772 talloc_free_children(ctx);
1773 fn(NULL, NULL, data_area); /* completed an entry */
1776 talloc_destroy(ctx);
1780 * count how many replies are in a LDAPMessage
1781 * @param ads connection to ads server
1782 * @param res Results to count
1783 * @return number of replies
1785 int ads_count_replies(ADS_STRUCT *ads, void *res)
1787 return ldap_count_entries(ads->ld, (LDAPMessage *)res);
1791 * pull the first entry from a ADS result
1792 * @param ads connection to ads server
1793 * @param res Results of search
1794 * @return first entry from result
1796 void *ads_first_entry(ADS_STRUCT *ads, void *res)
1798 return (void *)ldap_first_entry(ads->ld, (LDAPMessage *)res);
1802 * pull the next entry from a ADS result
1803 * @param ads connection to ads server
1804 * @param res Results of search
1805 * @return next entry from result
1807 void *ads_next_entry(ADS_STRUCT *ads, void *res)
1809 return (void *)ldap_next_entry(ads->ld, (LDAPMessage *)res);
1813 * pull a single string from a ADS result
1814 * @param ads connection to ads server
1815 * @param mem_ctx TALLOC_CTX to use for allocating result string
1816 * @param msg Results of search
1817 * @param field Attribute to retrieve
1818 * @return Result string in talloc context
1820 char *ads_pull_string(ADS_STRUCT *ads,
1821 TALLOC_CTX *mem_ctx, void *msg, const char *field)
1823 char **values;
1824 char *ret = NULL;
1825 char *ux_string;
1826 size_t rc;
1828 values = ldap_get_values(ads->ld, msg, field);
1829 if (!values)
1830 return NULL;
1832 if (values[0]) {
1833 rc = pull_utf8_talloc(mem_ctx, &ux_string,
1834 values[0]);
1835 if (rc != (size_t)-1)
1836 ret = ux_string;
1839 ldap_value_free(values);
1840 return ret;
1844 * pull an array of strings from a ADS result
1845 * @param ads connection to ads server
1846 * @param mem_ctx TALLOC_CTX to use for allocating result string
1847 * @param msg Results of search
1848 * @param field Attribute to retrieve
1849 * @return Result strings in talloc context
1851 char **ads_pull_strings(ADS_STRUCT *ads,
1852 TALLOC_CTX *mem_ctx, void *msg, const char *field,
1853 size_t *num_values)
1855 char **values;
1856 char **ret = NULL;
1857 int i;
1859 values = ldap_get_values(ads->ld, msg, field);
1860 if (!values)
1861 return NULL;
1863 *num_values = ldap_count_values(values);
1865 ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
1866 if (!ret) {
1867 ldap_value_free(values);
1868 return NULL;
1871 for (i=0;i<*num_values;i++) {
1872 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
1873 ldap_value_free(values);
1874 return NULL;
1877 ret[i] = NULL;
1879 ldap_value_free(values);
1880 return ret;
1884 * pull an array of strings from a ADS result
1885 * (handle large multivalue attributes with range retrieval)
1886 * @param ads connection to ads server
1887 * @param mem_ctx TALLOC_CTX to use for allocating result string
1888 * @param msg Results of search
1889 * @param field Attribute to retrieve
1890 * @param current_strings strings returned by a previous call to this function
1891 * @param next_attribute The next query should ask for this attribute
1892 * @param num_values How many values did we get this time?
1893 * @param more_values Are there more values to get?
1894 * @return Result strings in talloc context
1896 char **ads_pull_strings_range(ADS_STRUCT *ads,
1897 TALLOC_CTX *mem_ctx,
1898 void *msg, const char *field,
1899 char **current_strings,
1900 const char **next_attribute,
1901 size_t *num_strings,
1902 BOOL *more_strings)
1904 char *attr;
1905 char *expected_range_attrib, *range_attr;
1906 BerElement *ptr = NULL;
1907 char **strings;
1908 char **new_strings;
1909 size_t num_new_strings;
1910 unsigned long int range_start;
1911 unsigned long int range_end;
1913 /* we might have been given the whole lot anyway */
1914 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
1915 *more_strings = False;
1916 return strings;
1919 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
1921 /* look for Range result */
1922 for (attr = ldap_first_attribute(ads->ld, (LDAPMessage *)msg, &ptr);
1923 attr;
1924 attr = ldap_next_attribute(ads->ld, (LDAPMessage *)msg, ptr)) {
1925 /* we ignore the fact that this is utf8, as all attributes are ascii... */
1926 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
1927 range_attr = attr;
1928 break;
1930 ldap_memfree(attr);
1932 if (!attr) {
1933 ber_free(ptr, 0);
1934 /* nothing here - this field is just empty */
1935 *more_strings = False;
1936 return NULL;
1939 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
1940 &range_start, &range_end) == 2) {
1941 *more_strings = True;
1942 } else {
1943 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
1944 &range_start) == 1) {
1945 *more_strings = False;
1946 } else {
1947 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
1948 range_attr));
1949 ldap_memfree(range_attr);
1950 *more_strings = False;
1951 return NULL;
1955 if ((*num_strings) != range_start) {
1956 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
1957 " - aborting range retreival\n",
1958 range_attr, (unsigned int)(*num_strings) + 1, range_start));
1959 ldap_memfree(range_attr);
1960 *more_strings = False;
1961 return NULL;
1964 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
1966 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
1967 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
1968 "strings in this bunch, but we only got %lu - aborting range retreival\n",
1969 range_attr, (unsigned long int)range_end - range_start + 1,
1970 (unsigned long int)num_new_strings));
1971 ldap_memfree(range_attr);
1972 *more_strings = False;
1973 return NULL;
1976 strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
1977 *num_strings + num_new_strings);
1979 if (strings == NULL) {
1980 ldap_memfree(range_attr);
1981 *more_strings = False;
1982 return NULL;
1985 if (new_strings && num_new_strings) {
1986 memcpy(&strings[*num_strings], new_strings,
1987 sizeof(*new_strings) * num_new_strings);
1990 (*num_strings) += num_new_strings;
1992 if (*more_strings) {
1993 *next_attribute = talloc_asprintf(mem_ctx,
1994 "%s;range=%d-*",
1995 field,
1996 (int)*num_strings);
1998 if (!*next_attribute) {
1999 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2000 ldap_memfree(range_attr);
2001 *more_strings = False;
2002 return NULL;
2006 ldap_memfree(range_attr);
2008 return strings;
2012 * pull a single uint32 from a ADS result
2013 * @param ads connection to ads server
2014 * @param msg Results of search
2015 * @param field Attribute to retrieve
2016 * @param v Pointer to int to store result
2017 * @return boolean inidicating success
2019 BOOL ads_pull_uint32(ADS_STRUCT *ads,
2020 void *msg, const char *field, uint32 *v)
2022 char **values;
2024 values = ldap_get_values(ads->ld, msg, field);
2025 if (!values)
2026 return False;
2027 if (!values[0]) {
2028 ldap_value_free(values);
2029 return False;
2032 *v = atoi(values[0]);
2033 ldap_value_free(values);
2034 return True;
2038 * pull a single objectGUID from an ADS result
2039 * @param ads connection to ADS server
2040 * @param msg results of search
2041 * @param guid 37-byte area to receive text guid
2042 * @return boolean indicating success
2044 BOOL ads_pull_guid(ADS_STRUCT *ads,
2045 void *msg, struct uuid *guid)
2047 char **values;
2048 UUID_FLAT flat_guid;
2050 values = ldap_get_values(ads->ld, msg, "objectGUID");
2051 if (!values)
2052 return False;
2054 if (values[0]) {
2055 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2056 smb_uuid_unpack(flat_guid, guid);
2057 ldap_value_free(values);
2058 return True;
2060 ldap_value_free(values);
2061 return False;
2067 * pull a single DOM_SID from a ADS result
2068 * @param ads connection to ads server
2069 * @param msg Results of search
2070 * @param field Attribute to retrieve
2071 * @param sid Pointer to sid to store result
2072 * @return boolean inidicating success
2074 BOOL ads_pull_sid(ADS_STRUCT *ads,
2075 void *msg, const char *field, DOM_SID *sid)
2077 struct berval **values;
2078 BOOL ret = False;
2080 values = ldap_get_values_len(ads->ld, msg, field);
2082 if (!values)
2083 return False;
2085 if (values[0])
2086 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2088 ldap_value_free_len(values);
2089 return ret;
2093 * pull an array of DOM_SIDs from a ADS result
2094 * @param ads connection to ads server
2095 * @param mem_ctx TALLOC_CTX for allocating sid array
2096 * @param msg Results of search
2097 * @param field Attribute to retrieve
2098 * @param sids pointer to sid array to allocate
2099 * @return the count of SIDs pulled
2101 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2102 void *msg, const char *field, DOM_SID **sids)
2104 struct berval **values;
2105 BOOL ret;
2106 int count, i;
2108 values = ldap_get_values_len(ads->ld, msg, field);
2110 if (!values)
2111 return 0;
2113 for (i=0; values[i]; i++)
2114 /* nop */ ;
2116 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2117 if (!(*sids)) {
2118 ldap_value_free_len(values);
2119 return 0;
2122 count = 0;
2123 for (i=0; values[i]; i++) {
2124 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2125 if (ret) {
2126 fstring sid;
2127 DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid, &(*sids)[count])));
2128 count++;
2132 ldap_value_free_len(values);
2133 return count;
2137 * pull a SEC_DESC from a ADS result
2138 * @param ads connection to ads server
2139 * @param mem_ctx TALLOC_CTX for allocating sid array
2140 * @param msg Results of search
2141 * @param field Attribute to retrieve
2142 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2143 * @return boolean inidicating success
2145 BOOL ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2146 void *msg, const char *field, SEC_DESC **sd)
2148 struct berval **values;
2149 prs_struct ps;
2150 BOOL ret = False;
2152 values = ldap_get_values_len(ads->ld, msg, field);
2154 if (!values) return False;
2156 if (values[0]) {
2157 prs_init(&ps, values[0]->bv_len, mem_ctx, UNMARSHALL);
2158 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
2159 prs_set_offset(&ps,0);
2161 ret = sec_io_desc("sd", sd, &ps, 1);
2164 ldap_value_free_len(values);
2165 return ret;
2169 * in order to support usernames longer than 21 characters we need to
2170 * use both the sAMAccountName and the userPrincipalName attributes
2171 * It seems that not all users have the userPrincipalName attribute set
2173 * @param ads connection to ads server
2174 * @param mem_ctx TALLOC_CTX for allocating sid array
2175 * @param msg Results of search
2176 * @return the username
2178 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, void *msg)
2180 #if 0 /* JERRY */
2181 char *ret, *p;
2183 /* lookup_name() only works on the sAMAccountName to
2184 returning the username portion of userPrincipalName
2185 breaks winbindd_getpwnam() */
2187 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2188 if (ret && (p = strchr_m(ret, '@'))) {
2189 *p = 0;
2190 return ret;
2192 #endif
2193 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2198 * find the update serial number - this is the core of the ldap cache
2199 * @param ads connection to ads server
2200 * @param ads connection to ADS server
2201 * @param usn Pointer to retrieved update serial number
2202 * @return status of search
2204 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2206 const char *attrs[] = {"highestCommittedUSN", NULL};
2207 ADS_STATUS status;
2208 void *res;
2210 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2211 if (!ADS_ERR_OK(status))
2212 return status;
2214 if (ads_count_replies(ads, res) != 1) {
2215 ads_msgfree(ads, res);
2216 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2219 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2220 ads_msgfree(ads, res);
2221 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2224 ads_msgfree(ads, res);
2225 return ADS_SUCCESS;
2228 /* parse a ADS timestring - typical string is
2229 '20020917091222.0Z0' which means 09:12.22 17th September
2230 2002, timezone 0 */
2231 static time_t ads_parse_time(const char *str)
2233 struct tm tm;
2235 ZERO_STRUCT(tm);
2237 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2238 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2239 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2240 return 0;
2242 tm.tm_year -= 1900;
2243 tm.tm_mon -= 1;
2245 return timegm(&tm);
2248 /********************************************************************
2249 ********************************************************************/
2251 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2253 const char *attrs[] = {"currentTime", NULL};
2254 ADS_STATUS status;
2255 void *res;
2256 char *timestr;
2257 TALLOC_CTX *ctx;
2258 ADS_STRUCT *ads_s = ads;
2260 if (!(ctx = talloc_init("ads_current_time"))) {
2261 return ADS_ERROR(LDAP_NO_MEMORY);
2264 /* establish a new ldap tcp session if necessary */
2266 if ( !ads->ld ) {
2267 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2268 ads->server.ldap_server )) == NULL )
2270 goto done;
2272 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2273 status = ads_connect( ads_s );
2274 if ( !ADS_ERR_OK(status))
2275 goto done;
2278 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2279 if (!ADS_ERR_OK(status)) {
2280 goto done;
2283 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2284 if (!timestr) {
2285 ads_msgfree(ads_s, res);
2286 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2287 goto done;
2290 /* but save the time and offset in the original ADS_STRUCT */
2292 ads->config.current_time = ads_parse_time(timestr);
2294 if (ads->config.current_time != 0) {
2295 ads->auth.time_offset = ads->config.current_time - time(NULL);
2296 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2299 ads_msgfree(ads, res);
2301 status = ADS_SUCCESS;
2303 done:
2304 /* free any temporary ads connections */
2305 if ( ads_s != ads ) {
2306 ads_destroy( &ads_s );
2308 talloc_destroy(ctx);
2310 return status;
2313 /********************************************************************
2314 ********************************************************************/
2316 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2318 const char *attrs[] = {"domainFunctionality", NULL};
2319 ADS_STATUS status;
2320 void *res;
2321 ADS_STRUCT *ads_s = ads;
2323 *val = DS_DOMAIN_FUNCTION_2000;
2325 /* establish a new ldap tcp session if necessary */
2327 if ( !ads->ld ) {
2328 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2329 ads->server.ldap_server )) == NULL )
2331 goto done;
2333 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2334 status = ads_connect( ads_s );
2335 if ( !ADS_ERR_OK(status))
2336 goto done;
2339 /* If the attribute does not exist assume it is a Windows 2000
2340 functional domain */
2342 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2343 if (!ADS_ERR_OK(status)) {
2344 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2345 status = ADS_SUCCESS;
2347 goto done;
2350 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2351 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2353 DEBUG(3,("ads_domain_func_level: %d\n", *val));
2356 ads_msgfree(ads, res);
2358 done:
2359 /* free any temporary ads connections */
2360 if ( ads_s != ads ) {
2361 ads_destroy( &ads_s );
2364 return status;
2368 * find the domain sid for our domain
2369 * @param ads connection to ads server
2370 * @param sid Pointer to domain sid
2371 * @return status of search
2373 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2375 const char *attrs[] = {"objectSid", NULL};
2376 void *res;
2377 ADS_STATUS rc;
2379 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2380 attrs, &res);
2381 if (!ADS_ERR_OK(rc)) return rc;
2382 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2383 ads_msgfree(ads, res);
2384 return ADS_ERROR_SYSTEM(ENOENT);
2386 ads_msgfree(ads, res);
2388 return ADS_SUCCESS;
2392 * find our site name
2393 * @param ads connection to ads server
2394 * @param mem_ctx Pointer to talloc context
2395 * @param site_name Pointer to the sitename
2396 * @return status of search
2398 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2400 ADS_STATUS status;
2401 void *res;
2402 const char *dn, *service_name;
2403 const char *attrs[] = { "dsServiceName", NULL };
2405 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2406 if (!ADS_ERR_OK(status)) {
2407 return status;
2410 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2411 if (service_name == NULL) {
2412 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2415 /* go up three levels */
2416 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2417 if (dn == NULL) {
2418 return ADS_ERROR(LDAP_NO_MEMORY);
2421 *site_name = talloc_strdup(mem_ctx, dn);
2422 if (*site_name == NULL) {
2423 return ADS_ERROR(LDAP_NO_MEMORY);
2426 ads_msgfree(ads, res);
2428 return status;
2430 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2435 * find the site dn where a machine resides
2436 * @param ads connection to ads server
2437 * @param mem_ctx Pointer to talloc context
2438 * @param computer_name name of the machine
2439 * @param site_name Pointer to the sitename
2440 * @return status of search
2442 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
2444 ADS_STATUS status;
2445 void *res;
2446 const char *parent, *config_context, *filter;
2447 const char *attrs[] = { "configurationNamingContext", NULL };
2448 char *dn;
2450 /* shortcut a query */
2451 if (strequal(computer_name, ads->config.ldap_server_name)) {
2452 return ads_site_dn(ads, mem_ctx, site_dn);
2455 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2456 if (!ADS_ERR_OK(status)) {
2457 return status;
2460 config_context = ads_pull_string(ads, mem_ctx, res, "configurationNamingContext");
2461 if (config_context == NULL) {
2462 return ADS_ERROR(LDAP_NO_MEMORY);
2465 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
2466 if (filter == NULL) {
2467 return ADS_ERROR(LDAP_NO_MEMORY);
2470 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE, filter, NULL, &res);
2471 if (!ADS_ERR_OK(status)) {
2472 return status;
2475 if (ads_count_replies(ads, res) != 1) {
2476 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2479 dn = ads_get_dn(ads, res);
2480 if (dn == NULL) {
2481 return ADS_ERROR(LDAP_NO_MEMORY);
2484 /* go up three levels */
2485 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
2486 if (parent == NULL) {
2487 ads_memfree(ads, dn);
2488 return ADS_ERROR(LDAP_NO_MEMORY);
2491 *site_dn = talloc_strdup(mem_ctx, parent);
2492 if (*site_dn == NULL) {
2493 ads_memfree(ads, dn);
2494 ADS_ERROR(LDAP_NO_MEMORY);
2497 ads_memfree(ads, dn);
2498 ads_msgfree(ads, res);
2500 return status;
2504 * get the upn suffixes for a domain
2505 * @param ads connection to ads server
2506 * @param mem_ctx Pointer to talloc context
2507 * @param suffixes Pointer to an array of suffixes
2508 * @param site_name Pointer to the number of suffixes
2509 * @return status of search
2511 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char **suffixes, size_t *num_suffixes)
2513 ADS_STATUS status;
2514 void *res;
2515 const char *config_context, *base;
2516 const char *attrs[] = { "configurationNamingContext", NULL };
2517 const char *attrs2[] = { "uPNSuffixes", NULL };
2519 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2520 if (!ADS_ERR_OK(status)) {
2521 return status;
2524 config_context = ads_pull_string(ads, mem_ctx, res, "configurationNamingContext");
2525 if (config_context == NULL) {
2526 return ADS_ERROR(LDAP_NO_MEMORY);
2529 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
2530 if (base == NULL) {
2531 return ADS_ERROR(LDAP_NO_MEMORY);
2534 status = ads_search_dn(ads, &res, base, attrs2);
2535 if (!ADS_ERR_OK(status)) {
2536 return status;
2539 if (ads_count_replies(ads, res) != 1) {
2540 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2543 suffixes = ads_pull_strings(ads, mem_ctx, &res, "uPNSuffixes", num_suffixes);
2544 if (suffixes == NULL) {
2545 ads_msgfree(ads, res);
2546 return ADS_ERROR(LDAP_NO_MEMORY);
2549 ads_msgfree(ads, res);
2551 return status;
2555 * pull a DOM_SID from an extended dn string
2556 * @param mem_ctx TALLOC_CTX
2557 * @param flags string type of extended_dn
2558 * @param sid pointer to a DOM_SID
2559 * @return boolean inidicating success
2561 BOOL ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
2562 const char *dn,
2563 enum ads_extended_dn_flags flags,
2564 DOM_SID *sid)
2566 char *p, *q;
2568 if (!dn) {
2569 return False;
2573 * ADS_EXTENDED_DN_HEX_STRING:
2574 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
2576 * ADS_EXTENDED_DN_STRING (only with w2k3):
2577 <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
2580 p = strchr(dn, ';');
2581 if (!p) {
2582 return False;
2585 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
2586 return False;
2589 p += strlen(";<SID=");
2591 q = strchr(p, '>');
2592 if (!q) {
2593 return False;
2596 *q = '\0';
2598 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
2600 switch (flags) {
2602 case ADS_EXTENDED_DN_STRING:
2603 if (!string_to_sid(sid, p)) {
2604 return False;
2606 break;
2607 case ADS_EXTENDED_DN_HEX_STRING: {
2608 pstring buf;
2609 size_t buf_len;
2611 buf_len = strhex_to_str(buf, strlen(p), p);
2612 if (buf_len == 0) {
2613 return False;
2616 if (!sid_parse(buf, buf_len, sid)) {
2617 DEBUG(10,("failed to parse sid\n"));
2618 return False;
2620 break;
2622 default:
2623 DEBUG(10,("unknown extended dn format\n"));
2624 return False;
2627 return True;
2631 * pull an array of DOM_SIDs from a ADS result
2632 * @param ads connection to ads server
2633 * @param mem_ctx TALLOC_CTX for allocating sid array
2634 * @param msg Results of search
2635 * @param field Attribute to retrieve
2636 * @param flags string type of extended_dn
2637 * @param sids pointer to sid array to allocate
2638 * @return the count of SIDs pulled
2640 int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads,
2641 TALLOC_CTX *mem_ctx,
2642 void *msg,
2643 const char *field,
2644 enum ads_extended_dn_flags flags,
2645 DOM_SID **sids)
2647 int i;
2648 size_t dn_count;
2649 char **dn_strings;
2651 if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field,
2652 &dn_count)) == NULL) {
2653 return 0;
2656 (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, dn_count + 1);
2657 if (!(*sids)) {
2658 TALLOC_FREE(dn_strings);
2659 return 0;
2662 for (i=0; i<dn_count; i++) {
2664 if (!ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i],
2665 flags, &(*sids)[i])) {
2666 TALLOC_FREE(*sids);
2667 TALLOC_FREE(dn_strings);
2668 return 0;
2672 TALLOC_FREE(dn_strings);
2674 return dn_count;
2677 /********************************************************************
2678 ********************************************************************/
2680 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2682 LDAPMessage *res = NULL;
2683 ADS_STATUS status;
2684 int count = 0;
2685 char *name = NULL;
2687 status = ads_find_machine_acct(ads, (void **)(void *)&res, global_myname());
2688 if (!ADS_ERR_OK(status)) {
2689 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
2690 global_myname()));
2691 goto out;
2694 if ( (count = ads_count_replies(ads, res)) != 1 ) {
2695 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
2696 goto out;
2699 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
2700 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
2703 out:
2704 ads_msgfree(ads, res);
2706 return name;
2709 /********************************************************************
2710 ********************************************************************/
2712 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2714 LDAPMessage *res = NULL;
2715 ADS_STATUS status;
2716 int count = 0;
2717 char *name = NULL;
2719 status = ads_find_machine_acct(ads, (void **)(void *)&res, global_myname());
2720 if (!ADS_ERR_OK(status)) {
2721 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
2722 global_myname()));
2723 goto out;
2726 if ( (count = ads_count_replies(ads, res)) != 1 ) {
2727 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
2728 goto out;
2731 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
2732 DEBUG(0,("ads_get_dnshostname: No userPrincipalName attribute!\n"));
2735 out:
2736 ads_msgfree(ads, res);
2738 return name;
2741 /********************************************************************
2742 ********************************************************************/
2744 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
2746 LDAPMessage *res = NULL;
2747 ADS_STATUS status;
2748 int count = 0;
2749 char *name = NULL;
2751 status = ads_find_machine_acct(ads, (void **)(void *)&res, global_myname());
2752 if (!ADS_ERR_OK(status)) {
2753 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
2754 global_myname()));
2755 goto out;
2758 if ( (count = ads_count_replies(ads, res)) != 1 ) {
2759 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
2760 goto out;
2763 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
2764 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
2767 out:
2768 ads_msgfree(ads, res);
2770 return name;
2773 #endif