r15635: Fix a bogus gcc uninit variable message
[Samba/nascimento.git] / source3 / libads / ldap.c
blob5f0e296a04c2a7afb60d0255a67051d4c41c1a2b
1 /*
2 Unix SMB/CIFS implementation.
3 ads (active directory) utility library
4 Copyright (C) Andrew Tridgell 2001
5 Copyright (C) Remus Koos 2001
6 Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002
7 Copyright (C) Guenther Deschner 2005
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 #include "includes.h"
26 #ifdef HAVE_LDAP
28 /**
29 * @file ldap.c
30 * @brief basic ldap client-side routines for ads server communications
32 * The routines contained here should do the necessary ldap calls for
33 * ads setups.
35 * Important note: attribute names passed into ads_ routines must
36 * already be in UTF-8 format. We do not convert them because in almost
37 * all cases, they are just ascii (which is represented with the same
38 * codepoints in UTF-8). This may have to change at some point
39 **/
42 #define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"
44 static SIG_ATOMIC_T gotalarm;
46 /***************************************************************
47 Signal function to tell us we timed out.
48 ****************************************************************/
50 static void gotalarm_sig(void)
52 gotalarm = 1;
55 LDAP *ldap_open_with_timeout(const char *server, int port, unsigned int to)
57 LDAP *ldp = NULL;
59 /* Setup timeout */
60 gotalarm = 0;
61 CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
62 alarm(to);
63 /* End setup timeout. */
65 ldp = ldap_open(server, port);
67 /* Teardown timeout. */
68 CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
69 alarm(0);
71 return ldp;
74 static int ldap_search_with_timeout(LDAP *ld,
75 LDAP_CONST char *base,
76 int scope,
77 LDAP_CONST char *filter,
78 char **attrs,
79 int attrsonly,
80 LDAPControl **sctrls,
81 LDAPControl **cctrls,
82 int sizelimit,
83 LDAPMessage **res )
85 struct timeval timeout;
86 int result;
88 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
89 timeout.tv_sec = lp_ldap_timeout();
90 timeout.tv_usec = 0;
92 /* Setup alarm timeout.... Do we need both of these ? JRA. */
93 gotalarm = 0;
94 CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
95 alarm(lp_ldap_timeout());
96 /* End setup timeout. */
98 result = ldap_search_ext_s(ld, base, scope, filter, attrs,
99 attrsonly, sctrls, cctrls, &timeout,
100 sizelimit, res);
102 /* Teardown timeout. */
103 CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
104 alarm(0);
106 if (gotalarm != 0)
107 return LDAP_TIMELIMIT_EXCEEDED;
109 return result;
113 try a connection to a given ldap server, returning True and setting the servers IP
114 in the ads struct if successful
116 BOOL ads_try_connect(ADS_STRUCT *ads, const char *server )
118 char *srv;
119 struct cldap_netlogon_reply cldap_reply;
121 if (!server || !*server) {
122 return False;
125 DEBUG(5,("ads_try_connect: sending CLDAP request to %s\n", server));
127 /* this copes with inet_ntoa brokenness */
129 srv = SMB_STRDUP(server);
131 ZERO_STRUCT( cldap_reply );
133 if ( !ads_cldap_netlogon( srv, ads->server.realm, &cldap_reply ) ) {
134 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", srv));
135 return False;
138 /* Check the CLDAP reply flags */
140 if ( !(cldap_reply.flags & ADS_LDAP) ) {
141 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
142 srv));
143 SAFE_FREE( srv );
144 return False;
147 /* Fill in the ads->config values */
149 SAFE_FREE(ads->config.realm);
150 SAFE_FREE(ads->config.bind_path);
151 SAFE_FREE(ads->config.ldap_server_name);
153 ads->config.ldap_server_name = SMB_STRDUP(cldap_reply.hostname);
154 strupper_m(cldap_reply.domain);
155 ads->config.realm = SMB_STRDUP(cldap_reply.domain);
156 ads->config.bind_path = ads_build_dn(ads->config.realm);
158 ads->ldap_port = LDAP_PORT;
159 ads->ldap_ip = *interpret_addr2(srv);
160 SAFE_FREE(srv);
162 /* cache the successful connection */
164 saf_store( ads->server.workgroup, server );
166 return True;
169 /**********************************************************************
170 Try to find an AD dc using our internal name resolution routines
171 Try the realm first and then then workgroup name if netbios is not
172 disabled
173 **********************************************************************/
175 static BOOL ads_find_dc(ADS_STRUCT *ads)
177 const char *c_realm;
178 int count, i=0;
179 struct ip_service *ip_list;
180 pstring realm;
181 BOOL got_realm = False;
182 BOOL use_own_domain = False;
184 /* if the realm and workgroup are both empty, assume they are ours */
186 /* realm */
187 c_realm = ads->server.realm;
189 if ( !c_realm || !*c_realm ) {
190 /* special case where no realm and no workgroup means our own */
191 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
192 use_own_domain = True;
193 c_realm = lp_realm();
197 if (c_realm && *c_realm)
198 got_realm = True;
200 again:
201 /* we need to try once with the realm name and fallback to the
202 netbios domain name if we fail (if netbios has not been disabled */
204 if ( !got_realm && !lp_disable_netbios() ) {
205 c_realm = ads->server.workgroup;
206 if (!c_realm || !*c_realm) {
207 if ( use_own_domain )
208 c_realm = lp_workgroup();
211 if ( !c_realm || !*c_realm ) {
212 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
213 return False;
217 pstrcpy( realm, c_realm );
219 DEBUG(6,("ads_find_dc: looking for %s '%s'\n",
220 (got_realm ? "realm" : "domain"), realm));
222 if ( !get_sorted_dc_list(realm, &ip_list, &count, got_realm) ) {
223 /* fall back to netbios if we can */
224 if ( got_realm && !lp_disable_netbios() ) {
225 got_realm = False;
226 goto again;
229 return False;
232 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
233 for ( i=0; i<count; i++ ) {
234 fstring server;
236 fstrcpy( server, inet_ntoa(ip_list[i].ip) );
238 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
239 continue;
241 if ( ads_try_connect(ads, server) ) {
242 SAFE_FREE(ip_list);
243 return True;
246 /* keep track of failures */
247 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
250 SAFE_FREE(ip_list);
252 return False;
257 * Connect to the LDAP server
258 * @param ads Pointer to an existing ADS_STRUCT
259 * @return status of connection
261 ADS_STATUS ads_connect(ADS_STRUCT *ads)
263 int version = LDAP_VERSION3;
264 ADS_STATUS status;
266 ads->last_attempt = time(NULL);
267 ads->ld = NULL;
269 /* try with a user specified server */
271 if (ads->server.ldap_server &&
272 ads_try_connect(ads, ads->server.ldap_server)) {
273 goto got_connection;
276 if (ads_find_dc(ads)) {
277 goto got_connection;
280 return ADS_ERROR_SYSTEM(errno?errno:ENOENT);
282 got_connection:
283 DEBUG(3,("Connected to LDAP server %s\n", inet_ntoa(ads->ldap_ip)));
285 if (!ads->auth.user_name) {
286 /* have to use the userPrincipalName value here and
287 not servicePrincipalName; found by Guenther Deschner @ Sernet.
289 Is this still correct? The comment does not match
290 the code. --jerry */
292 asprintf(&ads->auth.user_name, "host/%s", global_myname() );
295 if (!ads->auth.realm) {
296 ads->auth.realm = SMB_STRDUP(ads->config.realm);
299 if (!ads->auth.kdc_server) {
300 ads->auth.kdc_server = SMB_STRDUP(inet_ntoa(ads->ldap_ip));
303 #if KRB5_DNS_HACK
304 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
305 to MIT kerberos to work (tridge) */
307 char *env;
308 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
309 setenv(env, ads->auth.kdc_server, 1);
310 free(env);
312 #endif
314 /* If the caller() requested no LDAP bind, then we are done */
316 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
317 return ADS_SUCCESS;
320 /* Otherwise setup the TCP LDAP session */
322 if ( (ads->ld = ldap_open_with_timeout(ads->config.ldap_server_name,
323 LDAP_PORT, lp_ldap_timeout())) == NULL )
325 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
327 ldap_set_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
329 status = ADS_ERROR(smb_ldap_start_tls(ads->ld, version));
330 if (!ADS_ERR_OK(status)) {
331 return status;
334 /* fill in the current time and offsets */
336 status = ads_current_time( ads );
337 if ( !ADS_ERR_OK(status) ) {
338 return status;
341 /* Now do the bind */
343 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
344 return ADS_ERROR(ldap_simple_bind_s( ads->ld, NULL, NULL));
347 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
348 return ADS_ERROR(ldap_simple_bind_s( ads->ld, ads->auth.user_name, ads->auth.password));
351 return ads_sasl_bind(ads);
355 Duplicate a struct berval into talloc'ed memory
357 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
359 struct berval *value;
361 if (!in_val) return NULL;
363 value = TALLOC_ZERO_P(ctx, struct berval);
364 if (value == NULL)
365 return NULL;
366 if (in_val->bv_len == 0) return value;
368 value->bv_len = in_val->bv_len;
369 value->bv_val = TALLOC_MEMDUP(ctx, in_val->bv_val, in_val->bv_len);
370 return value;
374 Make a values list out of an array of (struct berval *)
376 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
377 const struct berval **in_vals)
379 struct berval **values;
380 int i;
382 if (!in_vals) return NULL;
383 for (i=0; in_vals[i]; i++)
384 ; /* count values */
385 values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
386 if (!values) return NULL;
388 for (i=0; in_vals[i]; i++) {
389 values[i] = dup_berval(ctx, in_vals[i]);
391 return values;
395 UTF8-encode a values list out of an array of (char *)
397 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
399 char **values;
400 int i;
402 if (!in_vals) return NULL;
403 for (i=0; in_vals[i]; i++)
404 ; /* count values */
405 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
406 if (!values) return NULL;
408 for (i=0; in_vals[i]; i++) {
409 push_utf8_talloc(ctx, &values[i], in_vals[i]);
411 return values;
415 Pull a (char *) array out of a UTF8-encoded values list
417 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
419 char **values;
420 int i;
422 if (!in_vals) return NULL;
423 for (i=0; in_vals[i]; i++)
424 ; /* count values */
425 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
426 if (!values) return NULL;
428 for (i=0; in_vals[i]; i++) {
429 pull_utf8_talloc(ctx, &values[i], in_vals[i]);
431 return values;
435 * Do a search with paged results. cookie must be null on the first
436 * call, and then returned on each subsequent call. It will be null
437 * again when the entire search is complete
438 * @param ads connection to ads server
439 * @param bind_path Base dn for the search
440 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
441 * @param expr Search expression - specified in local charset
442 * @param attrs Attributes to retrieve - specified in utf8 or ascii
443 * @param res ** which will contain results - free res* with ads_msgfree()
444 * @param count Number of entries retrieved on this page
445 * @param cookie The paged results cookie to be returned on subsequent calls
446 * @return status of search
448 ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
449 int scope, const char *expr,
450 const char **attrs, void **res,
451 int *count, void **cookie)
453 int rc, i, version;
454 char *utf8_expr, *utf8_path, **search_attrs;
455 LDAPControl PagedResults, NoReferrals, *controls[3], **rcontrols;
456 BerElement *cookie_be = NULL;
457 struct berval *cookie_bv= NULL;
458 TALLOC_CTX *ctx;
460 *res = NULL;
462 if (!(ctx = talloc_init("ads_do_paged_search")))
463 return ADS_ERROR(LDAP_NO_MEMORY);
465 /* 0 means the conversion worked but the result was empty
466 so we only fail if it's -1. In any case, it always
467 at least nulls out the dest */
468 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
469 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
470 rc = LDAP_NO_MEMORY;
471 goto done;
474 if (!attrs || !(*attrs))
475 search_attrs = NULL;
476 else {
477 /* This would be the utf8-encoded version...*/
478 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
479 if (!(str_list_copy(&search_attrs, attrs))) {
480 rc = LDAP_NO_MEMORY;
481 goto done;
486 /* Paged results only available on ldap v3 or later */
487 ldap_get_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
488 if (version < LDAP_VERSION3) {
489 rc = LDAP_NOT_SUPPORTED;
490 goto done;
493 cookie_be = ber_alloc_t(LBER_USE_DER);
494 if (cookie && *cookie) {
495 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
496 ber_bvfree(*cookie); /* don't need it from last time */
497 *cookie = NULL;
498 } else {
499 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
501 ber_flatten(cookie_be, &cookie_bv);
502 PagedResults.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID);
503 PagedResults.ldctl_iscritical = (char) 1;
504 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
505 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
507 NoReferrals.ldctl_oid = CONST_DISCARD(char *, ADS_NO_REFERRALS_OID);
508 NoReferrals.ldctl_iscritical = (char) 0;
509 NoReferrals.ldctl_value.bv_len = 0;
510 NoReferrals.ldctl_value.bv_val = CONST_DISCARD(char *, "");
513 controls[0] = &NoReferrals;
514 controls[1] = &PagedResults;
515 controls[2] = NULL;
517 /* we need to disable referrals as the openldap libs don't
518 handle them and paged results at the same time. Using them
519 together results in the result record containing the server
520 page control being removed from the result list (tridge/jmcd)
522 leaving this in despite the control that says don't generate
523 referrals, in case the server doesn't support it (jmcd)
525 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
527 rc = ldap_search_with_timeout(ads->ld, utf8_path, scope, utf8_expr,
528 search_attrs, 0, controls,
529 NULL, LDAP_NO_LIMIT,
530 (LDAPMessage **)res);
532 ber_free(cookie_be, 1);
533 ber_bvfree(cookie_bv);
535 if (rc) {
536 DEBUG(3,("ads_do_paged_search: ldap_search_with_timeout(%s) -> %s\n", expr,
537 ldap_err2string(rc)));
538 goto done;
541 rc = ldap_parse_result(ads->ld, *res, NULL, NULL, NULL,
542 NULL, &rcontrols, 0);
544 if (!rcontrols) {
545 goto done;
548 for (i=0; rcontrols[i]; i++) {
549 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
550 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
551 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
552 &cookie_bv);
553 /* the berval is the cookie, but must be freed when
554 it is all done */
555 if (cookie_bv->bv_len) /* still more to do */
556 *cookie=ber_bvdup(cookie_bv);
557 else
558 *cookie=NULL;
559 ber_bvfree(cookie_bv);
560 ber_free(cookie_be, 1);
561 break;
564 ldap_controls_free(rcontrols);
566 done:
567 talloc_destroy(ctx);
568 /* if/when we decide to utf8-encode attrs, take out this next line */
569 str_list_free(&search_attrs);
571 return ADS_ERROR(rc);
576 * Get all results for a search. This uses ads_do_paged_search() to return
577 * all entries in a large search.
578 * @param ads connection to ads server
579 * @param bind_path Base dn for the search
580 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
581 * @param expr Search expression
582 * @param attrs Attributes to retrieve
583 * @param res ** which will contain results - free res* with ads_msgfree()
584 * @return status of search
586 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
587 int scope, const char *expr,
588 const char **attrs, void **res)
590 void *cookie = NULL;
591 int count = 0;
592 ADS_STATUS status;
594 *res = NULL;
595 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, res,
596 &count, &cookie);
598 if (!ADS_ERR_OK(status))
599 return status;
601 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
602 while (cookie) {
603 void *res2 = NULL;
604 ADS_STATUS status2;
605 LDAPMessage *msg, *next;
607 status2 = ads_do_paged_search(ads, bind_path, scope, expr,
608 attrs, &res2, &count, &cookie);
610 if (!ADS_ERR_OK(status2)) break;
612 /* this relies on the way that ldap_add_result_entry() works internally. I hope
613 that this works on all ldap libs, but I have only tested with openldap */
614 for (msg = ads_first_entry(ads, res2); msg; msg = next) {
615 next = ads_next_entry(ads, msg);
616 ldap_add_result_entry((LDAPMessage **)res, msg);
618 /* note that we do not free res2, as the memory is now
619 part of the main returned list */
621 #else
622 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
623 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
624 #endif
626 return status;
630 * Run a function on all results for a search. Uses ads_do_paged_search() and
631 * runs the function as each page is returned, using ads_process_results()
632 * @param ads connection to ads server
633 * @param bind_path Base dn for the search
634 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
635 * @param expr Search expression - specified in local charset
636 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
637 * @param fn Function which takes attr name, values list, and data_area
638 * @param data_area Pointer which is passed to function on each call
639 * @return status of search
641 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
642 int scope, const char *expr, const char **attrs,
643 BOOL(*fn)(char *, void **, void *),
644 void *data_area)
646 void *cookie = NULL;
647 int count = 0;
648 ADS_STATUS status;
649 void *res;
651 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
652 &count, &cookie);
654 if (!ADS_ERR_OK(status)) return status;
656 ads_process_results(ads, res, fn, data_area);
657 ads_msgfree(ads, res);
659 while (cookie) {
660 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
661 &res, &count, &cookie);
663 if (!ADS_ERR_OK(status)) break;
665 ads_process_results(ads, res, fn, data_area);
666 ads_msgfree(ads, res);
669 return status;
673 * Do a search with a timeout.
674 * @param ads connection to ads server
675 * @param bind_path Base dn for the search
676 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
677 * @param expr Search expression
678 * @param attrs Attributes to retrieve
679 * @param res ** which will contain results - free res* with ads_msgfree()
680 * @return status of search
682 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
683 const char *expr,
684 const char **attrs, void **res)
686 int rc;
687 char *utf8_expr, *utf8_path, **search_attrs = NULL;
688 TALLOC_CTX *ctx;
690 *res = NULL;
691 if (!(ctx = talloc_init("ads_do_search"))) {
692 DEBUG(1,("ads_do_search: talloc_init() failed!"));
693 return ADS_ERROR(LDAP_NO_MEMORY);
696 /* 0 means the conversion worked but the result was empty
697 so we only fail if it's negative. In any case, it always
698 at least nulls out the dest */
699 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
700 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
701 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
702 rc = LDAP_NO_MEMORY;
703 goto done;
706 if (!attrs || !(*attrs))
707 search_attrs = NULL;
708 else {
709 /* This would be the utf8-encoded version...*/
710 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
711 if (!(str_list_copy(&search_attrs, attrs)))
713 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
714 rc = LDAP_NO_MEMORY;
715 goto done;
719 /* see the note in ads_do_paged_search - we *must* disable referrals */
720 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
722 rc = ldap_search_with_timeout(ads->ld, utf8_path, scope, utf8_expr,
723 search_attrs, 0, NULL, NULL,
724 LDAP_NO_LIMIT,
725 (LDAPMessage **)res);
727 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
728 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
729 rc = 0;
732 done:
733 talloc_destroy(ctx);
734 /* if/when we decide to utf8-encode attrs, take out this next line */
735 str_list_free(&search_attrs);
736 return ADS_ERROR(rc);
739 * Do a general ADS search
740 * @param ads connection to ads server
741 * @param res ** which will contain results - free res* with ads_msgfree()
742 * @param expr Search expression
743 * @param attrs Attributes to retrieve
744 * @return status of search
746 ADS_STATUS ads_search(ADS_STRUCT *ads, void **res,
747 const char *expr,
748 const char **attrs)
750 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
751 expr, attrs, res);
755 * Do a search on a specific DistinguishedName
756 * @param ads connection to ads server
757 * @param res ** which will contain results - free res* with ads_msgfree()
758 * @param dn DistinguishName to search
759 * @param attrs Attributes to retrieve
760 * @return status of search
762 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, void **res,
763 const char *dn,
764 const char **attrs)
766 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)", attrs, res);
770 * Free up memory from a ads_search
771 * @param ads connection to ads server
772 * @param msg Search results to free
774 void ads_msgfree(ADS_STRUCT *ads, void *msg)
776 if (!msg) return;
777 ldap_msgfree(msg);
781 * Free up memory from various ads requests
782 * @param ads connection to ads server
783 * @param mem Area to free
785 void ads_memfree(ADS_STRUCT *ads, void *mem)
787 SAFE_FREE(mem);
791 * Get a dn from search results
792 * @param ads connection to ads server
793 * @param msg Search result
794 * @return dn string
796 char *ads_get_dn(ADS_STRUCT *ads, void *msg)
798 char *utf8_dn, *unix_dn;
800 utf8_dn = ldap_get_dn(ads->ld, msg);
802 if (!utf8_dn) {
803 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
804 return NULL;
807 if (pull_utf8_allocate(&unix_dn, utf8_dn) == (size_t)-1) {
808 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
809 utf8_dn ));
810 return NULL;
812 ldap_memfree(utf8_dn);
813 return unix_dn;
817 * Get a canonical dn from search results
818 * @param ads connection to ads server
819 * @param msg Search result
820 * @return dn string
822 char *ads_get_dn_canonical(ADS_STRUCT *ads, void *msg)
824 #ifdef HAVE_LDAP_DN2AD_CANONICAL
825 return ldap_dn2ad_canonical(ads_get_dn(ads, msg));
826 #else
827 return NULL;
828 #endif
832 * Get the parent from a dn
833 * @param dn the dn to return the parent from
834 * @return parent dn string
836 char *ads_parent_dn(const char *dn)
838 char *p = strchr(dn, ',');
840 if (p == NULL) {
841 return NULL;
844 return p+1;
848 * Find a machine account given a hostname
849 * @param ads connection to ads server
850 * @param res ** which will contain results - free res* with ads_msgfree()
851 * @param host Hostname to search for
852 * @return status of search
854 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, void **res, const char *machine)
856 ADS_STATUS status;
857 char *expr;
858 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
860 *res = NULL;
862 /* the easiest way to find a machine account anywhere in the tree
863 is to look for hostname$ */
864 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
865 DEBUG(1, ("asprintf failed!\n"));
866 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
869 status = ads_search(ads, res, expr, attrs);
870 SAFE_FREE(expr);
871 return status;
875 * Initialize a list of mods to be used in a modify request
876 * @param ctx An initialized TALLOC_CTX
877 * @return allocated ADS_MODLIST
879 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
881 #define ADS_MODLIST_ALLOC_SIZE 10
882 LDAPMod **mods;
884 if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
885 /* -1 is safety to make sure we don't go over the end.
886 need to reset it to NULL before doing ldap modify */
887 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
889 return (ADS_MODLIST)mods;
894 add an attribute to the list, with values list already constructed
896 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
897 int mod_op, const char *name,
898 const void **invals)
900 int curmod;
901 LDAPMod **modlist = (LDAPMod **) *mods;
902 struct berval **ber_values = NULL;
903 char **char_values = NULL;
905 if (!invals) {
906 mod_op = LDAP_MOD_DELETE;
907 } else {
908 if (mod_op & LDAP_MOD_BVALUES)
909 ber_values = ads_dup_values(ctx,
910 (const struct berval **)invals);
911 else
912 char_values = ads_push_strvals(ctx,
913 (const char **) invals);
916 /* find the first empty slot */
917 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
918 curmod++);
919 if (modlist[curmod] == (LDAPMod *) -1) {
920 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
921 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
922 return ADS_ERROR(LDAP_NO_MEMORY);
923 memset(&modlist[curmod], 0,
924 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
925 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
926 *mods = (ADS_MODLIST)modlist;
929 if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
930 return ADS_ERROR(LDAP_NO_MEMORY);
931 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
932 if (mod_op & LDAP_MOD_BVALUES) {
933 modlist[curmod]->mod_bvalues = ber_values;
934 } else if (mod_op & LDAP_MOD_DELETE) {
935 modlist[curmod]->mod_values = NULL;
936 } else {
937 modlist[curmod]->mod_values = char_values;
940 modlist[curmod]->mod_op = mod_op;
941 return ADS_ERROR(LDAP_SUCCESS);
945 * Add a single string value to a mod list
946 * @param ctx An initialized TALLOC_CTX
947 * @param mods An initialized ADS_MODLIST
948 * @param name The attribute name to add
949 * @param val The value to add - NULL means DELETE
950 * @return ADS STATUS indicating success of add
952 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
953 const char *name, const char *val)
955 const char *values[2];
957 values[0] = val;
958 values[1] = NULL;
960 if (!val)
961 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
962 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name,
963 (const void **) values);
967 * Add an array of string values to a mod list
968 * @param ctx An initialized TALLOC_CTX
969 * @param mods An initialized ADS_MODLIST
970 * @param name The attribute name to add
971 * @param vals The array of string values to add - NULL means DELETE
972 * @return ADS STATUS indicating success of add
974 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
975 const char *name, const char **vals)
977 if (!vals)
978 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
979 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
980 name, (const void **) vals);
983 #if 0
985 * Add a single ber-encoded value to a mod list
986 * @param ctx An initialized TALLOC_CTX
987 * @param mods An initialized ADS_MODLIST
988 * @param name The attribute name to add
989 * @param val The value to add - NULL means DELETE
990 * @return ADS STATUS indicating success of add
992 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
993 const char *name, const struct berval *val)
995 const struct berval *values[2];
997 values[0] = val;
998 values[1] = NULL;
999 if (!val)
1000 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1001 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1002 name, (const void **) values);
1004 #endif
1007 * Perform an ldap modify
1008 * @param ads connection to ads server
1009 * @param mod_dn DistinguishedName to modify
1010 * @param mods list of modifications to perform
1011 * @return status of modify
1013 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1015 int ret,i;
1016 char *utf8_dn = NULL;
1018 this control is needed to modify that contains a currently
1019 non-existent attribute (but allowable for the object) to run
1021 LDAPControl PermitModify = {
1022 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
1023 {0, NULL},
1024 (char) 1};
1025 LDAPControl *controls[2];
1027 controls[0] = &PermitModify;
1028 controls[1] = NULL;
1030 if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) {
1031 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1034 /* find the end of the list, marked by NULL or -1 */
1035 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1036 /* make sure the end of the list is NULL */
1037 mods[i] = NULL;
1038 ret = ldap_modify_ext_s(ads->ld, utf8_dn,
1039 (LDAPMod **) mods, controls, NULL);
1040 SAFE_FREE(utf8_dn);
1041 return ADS_ERROR(ret);
1045 * Perform an ldap add
1046 * @param ads connection to ads server
1047 * @param new_dn DistinguishedName to add
1048 * @param mods list of attributes and values for DN
1049 * @return status of add
1051 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1053 int ret, i;
1054 char *utf8_dn = NULL;
1056 if (push_utf8_allocate(&utf8_dn, new_dn) == -1) {
1057 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1058 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1061 /* find the end of the list, marked by NULL or -1 */
1062 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1063 /* make sure the end of the list is NULL */
1064 mods[i] = NULL;
1066 ret = ldap_add_s(ads->ld, utf8_dn, (LDAPMod**)mods);
1067 SAFE_FREE(utf8_dn);
1068 return ADS_ERROR(ret);
1072 * Delete a DistinguishedName
1073 * @param ads connection to ads server
1074 * @param new_dn DistinguishedName to delete
1075 * @return status of delete
1077 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1079 int ret;
1080 char *utf8_dn = NULL;
1081 if (push_utf8_allocate(&utf8_dn, del_dn) == -1) {
1082 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1083 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1086 ret = ldap_delete_s(ads->ld, utf8_dn);
1087 return ADS_ERROR(ret);
1091 * Build an org unit string
1092 * if org unit is Computers or blank then assume a container, otherwise
1093 * assume a / separated list of organisational units.
1094 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1095 * @param ads connection to ads server
1096 * @param org_unit Organizational unit
1097 * @return org unit string - caller must free
1099 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1101 char *ret = NULL;
1103 if (!org_unit || !*org_unit) {
1105 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1107 /* samba4 might not yet respond to a wellknownobject-query */
1108 return ret ? ret : SMB_STRDUP("cn=Computers");
1111 if (strequal(org_unit, "Computers")) {
1112 return SMB_STRDUP("cn=Computers");
1115 /* jmcd: removed "\\" from the separation chars, because it is
1116 needed as an escape for chars like '#' which are valid in an
1117 OU name */
1118 return ads_build_path(org_unit, "/", "ou=", 1);
1122 * Get a org unit string for a well-known GUID
1123 * @param ads connection to ads server
1124 * @param wknguid Well known GUID
1125 * @return org unit string - caller must free
1127 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1129 ADS_STATUS status;
1130 void *res;
1131 char *base, *wkn_dn, *ret, **wkn_dn_exp, **bind_dn_exp;
1132 const char *attrs[] = {"distinguishedName", NULL};
1133 int new_ln, wkn_ln, bind_ln, i;
1135 if (wknguid == NULL) {
1136 return NULL;
1139 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1140 DEBUG(1, ("asprintf failed!\n"));
1141 return NULL;
1144 status = ads_search_dn(ads, &res, base, attrs);
1145 if (!ADS_ERR_OK(status)) {
1146 DEBUG(1,("Failed while searching for: %s\n", base));
1147 return NULL;
1149 free(base);
1151 if (ads_count_replies(ads, res) != 1) {
1152 return NULL;
1155 /* substitute the bind-path from the well-known-guid-search result */
1156 wkn_dn = ads_get_dn(ads, res);
1157 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1158 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1160 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1162 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1165 new_ln = wkn_ln - bind_ln;
1167 ret = wkn_dn_exp[0];
1169 for (i=1; i < new_ln; i++) {
1170 char *s;
1171 asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]);
1172 ret = SMB_STRDUP(s);
1173 free(s);
1176 return ret;
1180 * Adds (appends) an item to an attribute array, rather then
1181 * replacing the whole list
1182 * @param ctx An initialized TALLOC_CTX
1183 * @param mods An initialized ADS_MODLIST
1184 * @param name name of the ldap attribute to append to
1185 * @param vals an array of values to add
1186 * @return status of addition
1189 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1190 const char *name, const char **vals)
1192 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name, (const void **) vals);
1196 * Determines the computer account's current KVNO via an LDAP lookup
1197 * @param ads An initialized ADS_STRUCT
1198 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1199 * @return the kvno for the computer account, or -1 in case of a failure.
1202 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *machine_name)
1204 LDAPMessage *res = NULL;
1205 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1206 char *filter;
1207 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1208 char *dn_string = NULL;
1209 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1211 DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name));
1212 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1213 return kvno;
1215 ret = ads_search(ads, (void**)(void *)&res, filter, attrs);
1216 SAFE_FREE(filter);
1217 if (!ADS_ERR_OK(ret) && ads_count_replies(ads, res)) {
1218 DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name));
1219 ads_msgfree(ads, res);
1220 return kvno;
1223 dn_string = ads_get_dn(ads, res);
1224 if (!dn_string) {
1225 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1226 ads_msgfree(ads, res);
1227 return kvno;
1229 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1230 ads_memfree(ads, dn_string);
1232 /* ---------------------------------------------------------
1233 * 0 is returned as a default KVNO from this point on...
1234 * This is done because Windows 2000 does not support key
1235 * version numbers. Chances are that a failure in the next
1236 * step is simply due to Windows 2000 being used for a
1237 * domain controller. */
1238 kvno = 0;
1240 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1241 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1242 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1243 ads_msgfree(ads, res);
1244 return kvno;
1247 /* Success */
1248 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1249 ads_msgfree(ads, res);
1250 return kvno;
1254 * This clears out all registered spn's for a given hostname
1255 * @param ads An initilaized ADS_STRUCT
1256 * @param machine_name the NetBIOS name of the computer.
1257 * @return 0 upon success, non-zero otherwise.
1260 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1262 TALLOC_CTX *ctx;
1263 LDAPMessage *res = NULL;
1264 ADS_MODLIST mods;
1265 const char *servicePrincipalName[1] = {NULL};
1266 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1267 char *dn_string = NULL;
1269 ret = ads_find_machine_acct(ads, (void **)(void *)&res, machine_name);
1270 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1271 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1272 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1273 ads_msgfree(ads, res);
1274 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1277 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1278 ctx = talloc_init("ads_clear_service_principal_names");
1279 if (!ctx) {
1280 ads_msgfree(ads, res);
1281 return ADS_ERROR(LDAP_NO_MEMORY);
1284 if (!(mods = ads_init_mods(ctx))) {
1285 talloc_destroy(ctx);
1286 ads_msgfree(ads, res);
1287 return ADS_ERROR(LDAP_NO_MEMORY);
1289 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1290 if (!ADS_ERR_OK(ret)) {
1291 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1292 ads_msgfree(ads, res);
1293 talloc_destroy(ctx);
1294 return ret;
1296 dn_string = ads_get_dn(ads, res);
1297 if (!dn_string) {
1298 talloc_destroy(ctx);
1299 ads_msgfree(ads, res);
1300 return ADS_ERROR(LDAP_NO_MEMORY);
1302 ret = ads_gen_mod(ads, dn_string, mods);
1303 ads_memfree(ads,dn_string);
1304 if (!ADS_ERR_OK(ret)) {
1305 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1306 machine_name));
1307 ads_msgfree(ads, res);
1308 talloc_destroy(ctx);
1309 return ret;
1312 ads_msgfree(ads, res);
1313 talloc_destroy(ctx);
1314 return ret;
1318 * This adds a service principal name to an existing computer account
1319 * (found by hostname) in AD.
1320 * @param ads An initialized ADS_STRUCT
1321 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1322 * @param spn A string of the service principal to add, i.e. 'host'
1323 * @return 0 upon sucess, or non-zero if a failure occurs
1326 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name, const char *spn)
1328 ADS_STATUS ret;
1329 TALLOC_CTX *ctx;
1330 LDAPMessage *res = NULL;
1331 char *host_spn, *psp1, *psp2, *psp3;
1332 ADS_MODLIST mods;
1333 fstring my_fqdn;
1334 char *dn_string = NULL;
1335 const char *servicePrincipalName[4] = {NULL, NULL, NULL, NULL};
1337 ret = ads_find_machine_acct(ads, (void **)(void *)&res, machine_name);
1338 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1339 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1340 machine_name));
1341 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1342 spn, machine_name, ads->config.realm));
1343 ads_msgfree(ads, res);
1344 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1347 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1348 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1349 ads_msgfree(ads, res);
1350 return ADS_ERROR(LDAP_NO_MEMORY);
1353 name_to_fqdn(my_fqdn, machine_name);
1354 strlower_m(my_fqdn);
1356 if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", my_fqdn))) {
1357 talloc_destroy(ctx);
1358 ads_msgfree(ads, res);
1359 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1362 /* Add the extra principal */
1363 psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name);
1364 strupper_m(psp1);
1365 strlower_m(&psp1[strlen(spn)]);
1366 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp1, machine_name));
1367 servicePrincipalName[0] = psp1;
1368 psp2 = talloc_asprintf(ctx, "%s/%s.%s", spn, machine_name, ads->config.realm);
1369 strupper_m(psp2);
1370 strlower_m(&psp2[strlen(spn)]);
1371 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp2, machine_name));
1372 servicePrincipalName[1] = psp2;
1374 /* Add another principal in case the realm != the DNS domain, so that
1375 * the KDC doesn't send "server principal unknown" errors to clients
1376 * which use the DNS name in determining service principal names. */
1377 psp3 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn);
1378 strupper_m(psp3);
1379 strlower_m(&psp3[strlen(spn)]);
1380 if (strcmp(psp2, psp3) != 0) {
1381 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp3, machine_name));
1382 servicePrincipalName[2] = psp3;
1385 if (!(mods = ads_init_mods(ctx))) {
1386 talloc_destroy(ctx);
1387 ads_msgfree(ads, res);
1388 return ADS_ERROR(LDAP_NO_MEMORY);
1390 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1391 if (!ADS_ERR_OK(ret)) {
1392 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1393 talloc_destroy(ctx);
1394 ads_msgfree(ads, res);
1395 return ret;
1397 dn_string = ads_get_dn(ads, res);
1398 if (!dn_string) {
1399 talloc_destroy(ctx);
1400 ads_msgfree(ads, res);
1401 return ADS_ERROR(LDAP_NO_MEMORY);
1403 ret = ads_gen_mod(ads, dn_string, mods);
1404 ads_memfree(ads,dn_string);
1405 if (!ADS_ERR_OK(ret)) {
1406 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1407 talloc_destroy(ctx);
1408 ads_msgfree(ads, res);
1409 return ret;
1412 talloc_destroy(ctx);
1413 ads_msgfree(ads, res);
1414 return ret;
1418 * adds a machine account to the ADS server
1419 * @param ads An intialized ADS_STRUCT
1420 * @param machine_name - the NetBIOS machine name of this account.
1421 * @param account_type A number indicating the type of account to create
1422 * @param org_unit The LDAP path in which to place this account
1423 * @return 0 upon success, or non-zero otherwise
1426 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1427 const char *org_unit)
1429 ADS_STATUS ret;
1430 char *samAccountName, *controlstr;
1431 TALLOC_CTX *ctx;
1432 ADS_MODLIST mods;
1433 char *new_dn;
1434 const char *objectClass[] = {"top", "person", "organizationalPerson",
1435 "user", "computer", NULL};
1436 LDAPMessage *res = NULL;
1437 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
1438 UF_DONT_EXPIRE_PASSWD |\
1439 UF_ACCOUNTDISABLE );
1441 if (!(ctx = talloc_init("ads_add_machine_acct")))
1442 return ADS_ERROR(LDAP_NO_MEMORY);
1444 ret = ADS_ERROR(LDAP_NO_MEMORY);
1446 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_name, org_unit);
1447 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
1449 if ( !new_dn || !samAccountName ) {
1450 goto done;
1453 #ifndef ENCTYPE_ARCFOUR_HMAC
1454 acct_control |= UF_USE_DES_KEY_ONLY;
1455 #endif
1457 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
1458 goto done;
1461 if (!(mods = ads_init_mods(ctx))) {
1462 goto done;
1465 ads_mod_str(ctx, &mods, "cn", machine_name);
1466 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1467 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1468 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1470 ret = ads_gen_add(ads, new_dn, mods);
1472 done:
1473 ads_msgfree(ads, res);
1474 talloc_destroy(ctx);
1476 return ret;
1480 dump a binary result from ldap
1482 static void dump_binary(const char *field, struct berval **values)
1484 int i, j;
1485 for (i=0; values[i]; i++) {
1486 printf("%s: ", field);
1487 for (j=0; j<values[i]->bv_len; j++) {
1488 printf("%02X", (unsigned char)values[i]->bv_val[j]);
1490 printf("\n");
1494 static void dump_guid(const char *field, struct berval **values)
1496 int i;
1497 UUID_FLAT guid;
1498 for (i=0; values[i]; i++) {
1499 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1500 printf("%s: %s\n", field,
1501 smb_uuid_string_static(smb_uuid_unpack_static(guid)));
1506 dump a sid result from ldap
1508 static void dump_sid(const char *field, struct berval **values)
1510 int i;
1511 for (i=0; values[i]; i++) {
1512 DOM_SID sid;
1513 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1514 printf("%s: %s\n", field, sid_string_static(&sid));
1519 dump ntSecurityDescriptor
1521 static void dump_sd(const char *filed, struct berval **values)
1523 prs_struct ps;
1525 SEC_DESC *psd = 0;
1526 TALLOC_CTX *ctx = 0;
1528 if (!(ctx = talloc_init("sec_io_desc")))
1529 return;
1531 /* prepare data */
1532 prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
1533 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
1534 prs_set_offset(&ps,0);
1536 /* parse secdesc */
1537 if (!sec_io_desc("sd", &psd, &ps, 1)) {
1538 prs_mem_free(&ps);
1539 talloc_destroy(ctx);
1540 return;
1542 if (psd) ads_disp_sd(psd);
1544 prs_mem_free(&ps);
1545 talloc_destroy(ctx);
1549 dump a string result from ldap
1551 static void dump_string(const char *field, char **values)
1553 int i;
1554 for (i=0; values[i]; i++) {
1555 printf("%s: %s\n", field, values[i]);
1560 dump a field from LDAP on stdout
1561 used for debugging
1564 static BOOL ads_dump_field(char *field, void **values, void *data_area)
1566 const struct {
1567 const char *name;
1568 BOOL string;
1569 void (*handler)(const char *, struct berval **);
1570 } handlers[] = {
1571 {"objectGUID", False, dump_guid},
1572 {"netbootGUID", False, dump_guid},
1573 {"nTSecurityDescriptor", False, dump_sd},
1574 {"dnsRecord", False, dump_binary},
1575 {"objectSid", False, dump_sid},
1576 {"tokenGroups", False, dump_sid},
1577 {"tokenGroupsNoGCAcceptable", False, dump_sid},
1578 {"tokengroupsGlobalandUniversal", False, dump_sid},
1579 {NULL, True, NULL}
1581 int i;
1583 if (!field) { /* must be end of an entry */
1584 printf("\n");
1585 return False;
1588 for (i=0; handlers[i].name; i++) {
1589 if (StrCaseCmp(handlers[i].name, field) == 0) {
1590 if (!values) /* first time, indicate string or not */
1591 return handlers[i].string;
1592 handlers[i].handler(field, (struct berval **) values);
1593 break;
1596 if (!handlers[i].name) {
1597 if (!values) /* first time, indicate string conversion */
1598 return True;
1599 dump_string(field, (char **)values);
1601 return False;
1605 * Dump a result from LDAP on stdout
1606 * used for debugging
1607 * @param ads connection to ads server
1608 * @param res Results to dump
1611 void ads_dump(ADS_STRUCT *ads, void *res)
1613 ads_process_results(ads, res, ads_dump_field, NULL);
1617 * Walk through results, calling a function for each entry found.
1618 * The function receives a field name, a berval * array of values,
1619 * and a data area passed through from the start. The function is
1620 * called once with null for field and values at the end of each
1621 * entry.
1622 * @param ads connection to ads server
1623 * @param res Results to process
1624 * @param fn Function for processing each result
1625 * @param data_area user-defined area to pass to function
1627 void ads_process_results(ADS_STRUCT *ads, void *res,
1628 BOOL(*fn)(char *, void **, void *),
1629 void *data_area)
1631 void *msg;
1632 TALLOC_CTX *ctx;
1634 if (!(ctx = talloc_init("ads_process_results")))
1635 return;
1637 for (msg = ads_first_entry(ads, res); msg;
1638 msg = ads_next_entry(ads, msg)) {
1639 char *utf8_field;
1640 BerElement *b;
1642 for (utf8_field=ldap_first_attribute(ads->ld,
1643 (LDAPMessage *)msg,&b);
1644 utf8_field;
1645 utf8_field=ldap_next_attribute(ads->ld,
1646 (LDAPMessage *)msg,b)) {
1647 struct berval **ber_vals;
1648 char **str_vals, **utf8_vals;
1649 char *field;
1650 BOOL string;
1652 pull_utf8_talloc(ctx, &field, utf8_field);
1653 string = fn(field, NULL, data_area);
1655 if (string) {
1656 utf8_vals = ldap_get_values(ads->ld,
1657 (LDAPMessage *)msg, field);
1658 str_vals = ads_pull_strvals(ctx,
1659 (const char **) utf8_vals);
1660 fn(field, (void **) str_vals, data_area);
1661 ldap_value_free(utf8_vals);
1662 } else {
1663 ber_vals = ldap_get_values_len(ads->ld,
1664 (LDAPMessage *)msg, field);
1665 fn(field, (void **) ber_vals, data_area);
1667 ldap_value_free_len(ber_vals);
1669 ldap_memfree(utf8_field);
1671 ber_free(b, 0);
1672 talloc_free_children(ctx);
1673 fn(NULL, NULL, data_area); /* completed an entry */
1676 talloc_destroy(ctx);
1680 * count how many replies are in a LDAPMessage
1681 * @param ads connection to ads server
1682 * @param res Results to count
1683 * @return number of replies
1685 int ads_count_replies(ADS_STRUCT *ads, void *res)
1687 return ldap_count_entries(ads->ld, (LDAPMessage *)res);
1691 * Delete a machine from the realm
1692 * @param ads connection to ads server
1693 * @param hostname Machine to remove
1694 * @return status of delete
1696 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
1698 ADS_STATUS status;
1699 void *res, *msg;
1700 char *hostnameDN, *host;
1701 int rc;
1702 LDAPControl ldap_control;
1703 LDAPControl * pldap_control[2] = {NULL, NULL};
1705 pldap_control[0] = &ldap_control;
1706 memset(&ldap_control, 0, sizeof(LDAPControl));
1707 ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
1709 /* hostname must be lowercase */
1710 host = SMB_STRDUP(hostname);
1711 strlower_m(host);
1713 status = ads_find_machine_acct(ads, &res, host);
1714 if (!ADS_ERR_OK(status)) {
1715 DEBUG(0, ("Host account for %s does not exist.\n", host));
1716 return status;
1719 msg = ads_first_entry(ads, res);
1720 if (!msg) {
1721 return ADS_ERROR_SYSTEM(ENOENT);
1724 hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
1727 rc = ldap_delete_ext_s(ads->ld, hostnameDN, pldap_control, NULL);
1728 if (rc) {
1729 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
1730 }else {
1731 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
1734 ads_memfree(ads, hostnameDN);
1735 if (rc != LDAP_SUCCESS) {
1736 return ADS_ERROR(rc);
1739 status = ads_find_machine_acct(ads, &res, host);
1740 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1741 DEBUG(0, ("Failed to remove host account.\n"));
1742 return status;
1745 free(host);
1747 return status;
1750 #if 0
1752 * add machine account to existing security descriptor
1753 * @param ads connection to ads server
1754 * @param hostname machine to add
1755 * @param dn DN of security descriptor
1756 * @return status
1758 static ADS_STATUS ads_set_machine_sd(ADS_STRUCT *ads, const char *hostname, char *dn)
1760 const char *attrs[] = {"nTSecurityDescriptor", "objectSid", 0};
1761 char *expr = 0;
1762 size_t sd_size = 0;
1763 struct berval bval = {0, NULL};
1764 prs_struct ps_wire;
1765 char *escaped_hostname = escape_ldap_string_alloc(hostname);
1767 LDAPMessage *res = 0;
1768 LDAPMessage *msg = 0;
1769 ADS_MODLIST mods = 0;
1771 NTSTATUS status;
1772 ADS_STATUS ret;
1773 DOM_SID sid;
1774 SEC_DESC *psd = NULL;
1775 TALLOC_CTX *ctx = NULL;
1777 /* Avoid segmentation fault in prs_mem_free if
1778 * we have to bail out before prs_init */
1779 ps_wire.is_dynamic = False;
1781 if (!ads) {
1782 SAFE_FREE(escaped_hostname);
1783 return ADS_ERROR(LDAP_SERVER_DOWN);
1786 ret = ADS_ERROR(LDAP_SUCCESS);
1788 if (!escaped_hostname) {
1789 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1792 if (asprintf(&expr, "(samAccountName=%s$)", escaped_hostname) == -1) {
1793 DEBUG(1, ("ads_set_machine_sd: asprintf failed!\n"));
1794 SAFE_FREE(escaped_hostname);
1795 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1798 SAFE_FREE(escaped_hostname);
1800 ret = ads_search(ads, (void *) &res, expr, attrs);
1802 SAFE_FREE(expr);
1804 if (!ADS_ERR_OK(ret)) return ret;
1806 if ( !(msg = ads_first_entry(ads, res) )) {
1807 ret = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1808 goto ads_set_sd_error;
1811 if (!ads_pull_sid(ads, msg, attrs[1], &sid)) {
1812 ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
1813 goto ads_set_sd_error;
1816 if (!(ctx = talloc_init("sec_io_desc"))) {
1817 ret = ADS_ERROR(LDAP_NO_MEMORY);
1818 goto ads_set_sd_error;
1821 if (!ads_pull_sd(ads, ctx, msg, attrs[0], &psd)) {
1822 ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
1823 goto ads_set_sd_error;
1826 status = sec_desc_add_sid(ctx, &psd, &sid, SEC_RIGHTS_FULL_CTRL, &sd_size);
1828 if (!NT_STATUS_IS_OK(status)) {
1829 ret = ADS_ERROR_NT(status);
1830 goto ads_set_sd_error;
1833 if (!prs_init(&ps_wire, sd_size, ctx, MARSHALL)) {
1834 ret = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1837 if (!sec_io_desc("sd_wire", &psd, &ps_wire, 1)) {
1838 ret = ADS_ERROR(LDAP_NO_MEMORY);
1839 goto ads_set_sd_error;
1842 #if 0
1843 file_save("/tmp/sec_desc.new", ps_wire.data_p, sd_size);
1844 #endif
1845 if (!(mods = ads_init_mods(ctx))) return ADS_ERROR(LDAP_NO_MEMORY);
1847 bval.bv_len = prs_offset(&ps_wire);
1848 bval.bv_val = TALLOC(ctx, bval.bv_len);
1849 if (!bval.bv_val) {
1850 ret = ADS_ERROR(LDAP_NO_MEMORY);
1851 goto ads_set_sd_error;
1854 prs_set_offset(&ps_wire, 0);
1856 if (!prs_copy_data_out(bval.bv_val, &ps_wire, bval.bv_len)) {
1857 ret = ADS_ERROR(LDAP_NO_MEMORY);
1858 goto ads_set_sd_error;
1861 ret = ads_mod_ber(ctx, &mods, attrs[0], &bval);
1862 if (ADS_ERR_OK(ret)) {
1863 ret = ads_gen_mod(ads, dn, mods);
1866 ads_set_sd_error:
1867 ads_msgfree(ads, res);
1868 prs_mem_free(&ps_wire);
1869 talloc_destroy(ctx);
1870 return ret;
1872 #endif
1875 * pull the first entry from a ADS result
1876 * @param ads connection to ads server
1877 * @param res Results of search
1878 * @return first entry from result
1880 void *ads_first_entry(ADS_STRUCT *ads, void *res)
1882 return (void *)ldap_first_entry(ads->ld, (LDAPMessage *)res);
1886 * pull the next entry from a ADS result
1887 * @param ads connection to ads server
1888 * @param res Results of search
1889 * @return next entry from result
1891 void *ads_next_entry(ADS_STRUCT *ads, void *res)
1893 return (void *)ldap_next_entry(ads->ld, (LDAPMessage *)res);
1897 * pull a single string from a ADS result
1898 * @param ads connection to ads server
1899 * @param mem_ctx TALLOC_CTX to use for allocating result string
1900 * @param msg Results of search
1901 * @param field Attribute to retrieve
1902 * @return Result string in talloc context
1904 char *ads_pull_string(ADS_STRUCT *ads,
1905 TALLOC_CTX *mem_ctx, void *msg, const char *field)
1907 char **values;
1908 char *ret = NULL;
1909 char *ux_string;
1910 size_t rc;
1912 values = ldap_get_values(ads->ld, msg, field);
1913 if (!values)
1914 return NULL;
1916 if (values[0]) {
1917 rc = pull_utf8_talloc(mem_ctx, &ux_string,
1918 values[0]);
1919 if (rc != (size_t)-1)
1920 ret = ux_string;
1923 ldap_value_free(values);
1924 return ret;
1928 * pull an array of strings from a ADS result
1929 * @param ads connection to ads server
1930 * @param mem_ctx TALLOC_CTX to use for allocating result string
1931 * @param msg Results of search
1932 * @param field Attribute to retrieve
1933 * @return Result strings in talloc context
1935 char **ads_pull_strings(ADS_STRUCT *ads,
1936 TALLOC_CTX *mem_ctx, void *msg, const char *field,
1937 size_t *num_values)
1939 char **values;
1940 char **ret = NULL;
1941 int i;
1943 values = ldap_get_values(ads->ld, msg, field);
1944 if (!values)
1945 return NULL;
1947 *num_values = ldap_count_values(values);
1949 ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
1950 if (!ret) {
1951 ldap_value_free(values);
1952 return NULL;
1955 for (i=0;i<*num_values;i++) {
1956 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
1957 ldap_value_free(values);
1958 return NULL;
1961 ret[i] = NULL;
1963 ldap_value_free(values);
1964 return ret;
1968 * pull an array of strings from a ADS result
1969 * (handle large multivalue attributes with range retrieval)
1970 * @param ads connection to ads server
1971 * @param mem_ctx TALLOC_CTX to use for allocating result string
1972 * @param msg Results of search
1973 * @param field Attribute to retrieve
1974 * @param current_strings strings returned by a previous call to this function
1975 * @param next_attribute The next query should ask for this attribute
1976 * @param num_values How many values did we get this time?
1977 * @param more_values Are there more values to get?
1978 * @return Result strings in talloc context
1980 char **ads_pull_strings_range(ADS_STRUCT *ads,
1981 TALLOC_CTX *mem_ctx,
1982 void *msg, const char *field,
1983 char **current_strings,
1984 const char **next_attribute,
1985 size_t *num_strings,
1986 BOOL *more_strings)
1988 char *attr;
1989 char *expected_range_attrib, *range_attr;
1990 BerElement *ptr = NULL;
1991 char **strings;
1992 char **new_strings;
1993 size_t num_new_strings;
1994 unsigned long int range_start;
1995 unsigned long int range_end;
1997 /* we might have been given the whole lot anyway */
1998 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
1999 *more_strings = False;
2000 return strings;
2003 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2005 /* look for Range result */
2006 for (attr = ldap_first_attribute(ads->ld, (LDAPMessage *)msg, &ptr);
2007 attr;
2008 attr = ldap_next_attribute(ads->ld, (LDAPMessage *)msg, ptr)) {
2009 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2010 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2011 range_attr = attr;
2012 break;
2014 ldap_memfree(attr);
2016 if (!attr) {
2017 ber_free(ptr, 0);
2018 /* nothing here - this field is just empty */
2019 *more_strings = False;
2020 return NULL;
2023 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2024 &range_start, &range_end) == 2) {
2025 *more_strings = True;
2026 } else {
2027 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2028 &range_start) == 1) {
2029 *more_strings = False;
2030 } else {
2031 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2032 range_attr));
2033 ldap_memfree(range_attr);
2034 *more_strings = False;
2035 return NULL;
2039 if ((*num_strings) != range_start) {
2040 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2041 " - aborting range retreival\n",
2042 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2043 ldap_memfree(range_attr);
2044 *more_strings = False;
2045 return NULL;
2048 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2050 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2051 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2052 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2053 range_attr, (unsigned long int)range_end - range_start + 1,
2054 (unsigned long int)num_new_strings));
2055 ldap_memfree(range_attr);
2056 *more_strings = False;
2057 return NULL;
2060 strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2061 *num_strings + num_new_strings);
2063 if (strings == NULL) {
2064 ldap_memfree(range_attr);
2065 *more_strings = False;
2066 return NULL;
2069 memcpy(&strings[*num_strings], new_strings,
2070 sizeof(*new_strings) * num_new_strings);
2072 (*num_strings) += num_new_strings;
2074 if (*more_strings) {
2075 *next_attribute = talloc_asprintf(mem_ctx,
2076 "%s;range=%d-*",
2077 field,
2078 (int)*num_strings);
2080 if (!*next_attribute) {
2081 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2082 ldap_memfree(range_attr);
2083 *more_strings = False;
2084 return NULL;
2088 ldap_memfree(range_attr);
2090 return strings;
2094 * pull a single uint32 from a ADS result
2095 * @param ads connection to ads server
2096 * @param msg Results of search
2097 * @param field Attribute to retrieve
2098 * @param v Pointer to int to store result
2099 * @return boolean inidicating success
2101 BOOL ads_pull_uint32(ADS_STRUCT *ads,
2102 void *msg, const char *field, uint32 *v)
2104 char **values;
2106 values = ldap_get_values(ads->ld, msg, field);
2107 if (!values)
2108 return False;
2109 if (!values[0]) {
2110 ldap_value_free(values);
2111 return False;
2114 *v = atoi(values[0]);
2115 ldap_value_free(values);
2116 return True;
2120 * pull a single objectGUID from an ADS result
2121 * @param ads connection to ADS server
2122 * @param msg results of search
2123 * @param guid 37-byte area to receive text guid
2124 * @return boolean indicating success
2126 BOOL ads_pull_guid(ADS_STRUCT *ads,
2127 void *msg, struct uuid *guid)
2129 char **values;
2130 UUID_FLAT flat_guid;
2132 values = ldap_get_values(ads->ld, msg, "objectGUID");
2133 if (!values)
2134 return False;
2136 if (values[0]) {
2137 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2138 smb_uuid_unpack(flat_guid, guid);
2139 ldap_value_free(values);
2140 return True;
2142 ldap_value_free(values);
2143 return False;
2149 * pull a single DOM_SID from a ADS result
2150 * @param ads connection to ads server
2151 * @param msg Results of search
2152 * @param field Attribute to retrieve
2153 * @param sid Pointer to sid to store result
2154 * @return boolean inidicating success
2156 BOOL ads_pull_sid(ADS_STRUCT *ads,
2157 void *msg, const char *field, DOM_SID *sid)
2159 struct berval **values;
2160 BOOL ret = False;
2162 values = ldap_get_values_len(ads->ld, msg, field);
2164 if (!values)
2165 return False;
2167 if (values[0])
2168 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2170 ldap_value_free_len(values);
2171 return ret;
2175 * pull an array of DOM_SIDs from a ADS result
2176 * @param ads connection to ads server
2177 * @param mem_ctx TALLOC_CTX for allocating sid array
2178 * @param msg Results of search
2179 * @param field Attribute to retrieve
2180 * @param sids pointer to sid array to allocate
2181 * @return the count of SIDs pulled
2183 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2184 void *msg, const char *field, DOM_SID **sids)
2186 struct berval **values;
2187 BOOL ret;
2188 int count, i;
2190 values = ldap_get_values_len(ads->ld, msg, field);
2192 if (!values)
2193 return 0;
2195 for (i=0; values[i]; i++)
2196 /* nop */ ;
2198 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2199 if (!(*sids)) {
2200 ldap_value_free_len(values);
2201 return 0;
2204 count = 0;
2205 for (i=0; values[i]; i++) {
2206 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2207 if (ret) {
2208 fstring sid;
2209 DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid, &(*sids)[count])));
2210 count++;
2214 ldap_value_free_len(values);
2215 return count;
2219 * pull a SEC_DESC from a ADS result
2220 * @param ads connection to ads server
2221 * @param mem_ctx TALLOC_CTX for allocating sid array
2222 * @param msg Results of search
2223 * @param field Attribute to retrieve
2224 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2225 * @return boolean inidicating success
2227 BOOL ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2228 void *msg, const char *field, SEC_DESC **sd)
2230 struct berval **values;
2231 prs_struct ps;
2232 BOOL ret = False;
2234 values = ldap_get_values_len(ads->ld, msg, field);
2236 if (!values) return False;
2238 if (values[0]) {
2239 prs_init(&ps, values[0]->bv_len, mem_ctx, UNMARSHALL);
2240 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
2241 prs_set_offset(&ps,0);
2243 ret = sec_io_desc("sd", sd, &ps, 1);
2246 ldap_value_free_len(values);
2247 return ret;
2251 * in order to support usernames longer than 21 characters we need to
2252 * use both the sAMAccountName and the userPrincipalName attributes
2253 * It seems that not all users have the userPrincipalName attribute set
2255 * @param ads connection to ads server
2256 * @param mem_ctx TALLOC_CTX for allocating sid array
2257 * @param msg Results of search
2258 * @return the username
2260 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, void *msg)
2262 #if 0 /* JERRY */
2263 char *ret, *p;
2265 /* lookup_name() only works on the sAMAccountName to
2266 returning the username portion of userPrincipalName
2267 breaks winbindd_getpwnam() */
2269 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2270 if (ret && (p = strchr_m(ret, '@'))) {
2271 *p = 0;
2272 return ret;
2274 #endif
2275 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2280 * find the update serial number - this is the core of the ldap cache
2281 * @param ads connection to ads server
2282 * @param ads connection to ADS server
2283 * @param usn Pointer to retrieved update serial number
2284 * @return status of search
2286 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2288 const char *attrs[] = {"highestCommittedUSN", NULL};
2289 ADS_STATUS status;
2290 void *res;
2292 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2293 if (!ADS_ERR_OK(status))
2294 return status;
2296 if (ads_count_replies(ads, res) != 1) {
2297 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2300 ads_pull_uint32(ads, res, "highestCommittedUSN", usn);
2301 ads_msgfree(ads, res);
2302 return ADS_SUCCESS;
2305 /* parse a ADS timestring - typical string is
2306 '20020917091222.0Z0' which means 09:12.22 17th September
2307 2002, timezone 0 */
2308 static time_t ads_parse_time(const char *str)
2310 struct tm tm;
2312 ZERO_STRUCT(tm);
2314 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2315 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2316 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2317 return 0;
2319 tm.tm_year -= 1900;
2320 tm.tm_mon -= 1;
2322 return timegm(&tm);
2326 const char *ads_get_attrname_by_oid(ADS_STRUCT *ads, const char *schema_path, TALLOC_CTX *mem_ctx, const char * OID)
2328 ADS_STATUS rc;
2329 int count = 0;
2330 void *res = NULL;
2331 char *expr = NULL;
2332 const char *attrs[] = { "lDAPDisplayName", NULL };
2333 char *result;
2335 if (ads == NULL || mem_ctx == NULL || OID == NULL) {
2336 goto failed;
2339 expr = talloc_asprintf(mem_ctx, "(attributeId=%s)", OID);
2340 if (expr == NULL) {
2341 goto failed;
2344 rc = ads_do_search_retry(ads, schema_path, LDAP_SCOPE_SUBTREE,
2345 expr, attrs, &res);
2346 if (!ADS_ERR_OK(rc)) {
2347 goto failed;
2350 count = ads_count_replies(ads, res);
2351 if (count == 0 || !res) {
2352 goto failed;
2355 result = ads_pull_string(ads, mem_ctx, res, "lDAPDisplayName");
2356 ads_msgfree(ads, res);
2358 return result;
2360 failed:
2361 DEBUG(0,("ads_get_attrname_by_oid: failed to retrieve name for oid: %s\n",
2362 OID));
2364 ads_msgfree(ads, res);
2365 return NULL;
2369 * Find the servers name and realm - this can be done before authentication
2370 * The ldapServiceName field on w2k looks like this:
2371 * vnet3.home.samba.org:win2000-vnet3$@VNET3.HOME.SAMBA.ORG
2372 * @param ads connection to ads server
2373 * @return status of search
2375 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2377 const char *attrs[] = {"currentTime", NULL};
2378 ADS_STATUS status;
2379 void *res;
2380 char *timestr;
2381 TALLOC_CTX *ctx;
2382 ADS_STRUCT *ads_s = ads;
2384 if (!(ctx = talloc_init("ads_server_info"))) {
2385 return ADS_ERROR(LDAP_NO_MEMORY);
2388 /* establish a new ldap tcp session if necessary */
2390 if ( !ads->ld ) {
2391 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2392 ads->server.ldap_server )) == NULL )
2394 goto done;
2396 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2397 status = ads_connect( ads_s );
2398 if ( !ADS_ERR_OK(status))
2399 goto done;
2402 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2403 if (!ADS_ERR_OK(status)) {
2404 talloc_destroy(ctx);
2405 goto done;
2408 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2409 if (!timestr) {
2410 ads_msgfree(ads, res);
2411 talloc_destroy(ctx);
2412 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2413 goto done;
2416 /* but save the time and offset in the original ADS_STRUCT */
2418 ads->config.current_time = ads_parse_time(timestr);
2420 if (ads->config.current_time != 0) {
2421 ads->auth.time_offset = ads->config.current_time - time(NULL);
2422 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2425 status = ADS_SUCCESS;
2427 done:
2428 /* free any temporary ads connections */
2429 if ( ads_s != ads ) {
2430 ads_destroy( &ads_s );
2432 talloc_destroy(ctx);
2434 return status;
2437 /*********************************************************************
2438 *********************************************************************/
2440 static ADS_STATUS ads_schema_path(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char **schema_path)
2442 ADS_STATUS status;
2443 void *res;
2444 const char *schema;
2445 const char *attrs[] = { "schemaNamingContext", NULL };
2447 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2448 if (!ADS_ERR_OK(status)) {
2449 return status;
2452 if ( (schema = ads_pull_string(ads, mem_ctx, res, "schemaNamingContext")) == NULL ) {
2453 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2456 if ( (*schema_path = talloc_strdup(mem_ctx, schema)) == NULL ) {
2457 return ADS_ERROR(LDAP_NO_MEMORY);
2460 ads_msgfree(ads, res);
2462 return status;
2466 * Check for "Services for Unix"-Schema and load some attributes into the ADS_STRUCT
2467 * @param ads connection to ads server
2468 * @return BOOL status of search (False if one or more attributes couldn't be
2469 * found in Active Directory)
2470 **/
2471 BOOL ads_check_sfu_mapping(ADS_STRUCT *ads)
2473 BOOL ret = False;
2474 TALLOC_CTX *ctx = NULL;
2475 const char *gidnumber, *uidnumber, *homedir, *shell, *gecos;
2476 char *schema_path = NULL;
2477 ADS_STRUCT *ads_s = ads;
2478 ADS_STATUS status;
2480 if ( (ctx = talloc_init("ads_check_sfu_mapping")) == NULL ) {
2481 goto done;
2484 /* establish a new ldap tcp session if necessary */
2486 if ( !ads->ld ) {
2487 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2488 ads->server.ldap_server )) == NULL )
2490 goto done;
2493 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2494 status = ads_connect( ads_s );
2495 if ( !ADS_ERR_OK(status))
2496 goto done;
2499 status = ads_schema_path( ads, ctx, &schema_path );
2500 if ( !ADS_ERR_OK(status) ) {
2501 DEBUG(3,("ads_check_sfu_mapping: Unable to retrieve schema DN!\n"));
2502 goto done;
2505 gidnumber = ads_get_attrname_by_oid(ads_s, schema_path, ctx, ADS_ATTR_SFU_GIDNUMBER_OID);
2506 if (gidnumber == NULL)
2507 goto done;
2508 ads->schema.sfu_gidnumber_attr = SMB_STRDUP(gidnumber);
2510 uidnumber = ads_get_attrname_by_oid(ads_s, schema_path, ctx, ADS_ATTR_SFU_UIDNUMBER_OID);
2511 if (uidnumber == NULL)
2512 goto done;
2513 ads->schema.sfu_uidnumber_attr = SMB_STRDUP(uidnumber);
2515 homedir = ads_get_attrname_by_oid(ads_s, schema_path, ctx, ADS_ATTR_SFU_HOMEDIR_OID);
2516 if (homedir == NULL)
2517 goto done;
2518 ads->schema.sfu_homedir_attr = SMB_STRDUP(homedir);
2520 shell = ads_get_attrname_by_oid(ads_s, schema_path, ctx, ADS_ATTR_SFU_SHELL_OID);
2521 if (shell == NULL)
2522 goto done;
2523 ads->schema.sfu_shell_attr = SMB_STRDUP(shell);
2525 gecos = ads_get_attrname_by_oid(ads_s, schema_path, ctx, ADS_ATTR_SFU_GECOS_OID);
2526 if (gecos == NULL)
2527 goto done;
2528 ads->schema.sfu_gecos_attr = SMB_STRDUP(gecos);
2530 ret = True;
2531 done:
2532 /* free any temporary ads connections */
2533 if ( ads_s != ads ) {
2534 ads_destroy( &ads_s );
2537 if (ctx) {
2538 talloc_destroy(ctx);
2541 return ret;
2545 * find the domain sid for our domain
2546 * @param ads connection to ads server
2547 * @param sid Pointer to domain sid
2548 * @return status of search
2550 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2552 const char *attrs[] = {"objectSid", NULL};
2553 void *res;
2554 ADS_STATUS rc;
2556 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2557 attrs, &res);
2558 if (!ADS_ERR_OK(rc)) return rc;
2559 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2560 ads_msgfree(ads, res);
2561 return ADS_ERROR_SYSTEM(ENOENT);
2563 ads_msgfree(ads, res);
2565 return ADS_SUCCESS;
2569 * find our site name
2570 * @param ads connection to ads server
2571 * @param mem_ctx Pointer to talloc context
2572 * @param site_name Pointer to the sitename
2573 * @return status of search
2575 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2577 ADS_STATUS status;
2578 void *res;
2579 const char *dn, *service_name;
2580 const char *attrs[] = { "dsServiceName", NULL };
2582 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2583 if (!ADS_ERR_OK(status)) {
2584 return status;
2587 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2588 if (service_name == NULL) {
2589 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2592 /* go up three levels */
2593 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2594 if (dn == NULL) {
2595 return ADS_ERROR(LDAP_NO_MEMORY);
2598 *site_name = talloc_strdup(mem_ctx, dn);
2599 if (*site_name == NULL) {
2600 return ADS_ERROR(LDAP_NO_MEMORY);
2603 ads_msgfree(ads, res);
2605 return status;
2607 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2612 * find the site dn where a machine resides
2613 * @param ads connection to ads server
2614 * @param mem_ctx Pointer to talloc context
2615 * @param computer_name name of the machine
2616 * @param site_name Pointer to the sitename
2617 * @return status of search
2619 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
2621 ADS_STATUS status;
2622 void *res;
2623 const char *parent, *config_context, *filter;
2624 const char *attrs[] = { "configurationNamingContext", NULL };
2625 char *dn;
2627 /* shortcut a query */
2628 if (strequal(computer_name, ads->config.ldap_server_name)) {
2629 return ads_site_dn(ads, mem_ctx, site_dn);
2632 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2633 if (!ADS_ERR_OK(status)) {
2634 return status;
2637 config_context = ads_pull_string(ads, mem_ctx, res, "configurationNamingContext");
2638 if (config_context == NULL) {
2639 return ADS_ERROR(LDAP_NO_MEMORY);
2642 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
2643 if (filter == NULL) {
2644 return ADS_ERROR(LDAP_NO_MEMORY);
2647 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE, filter, NULL, &res);
2648 if (!ADS_ERR_OK(status)) {
2649 return status;
2652 if (ads_count_replies(ads, res) != 1) {
2653 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2656 dn = ads_get_dn(ads, res);
2657 if (dn == NULL) {
2658 return ADS_ERROR(LDAP_NO_MEMORY);
2661 /* go up three levels */
2662 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
2663 if (parent == NULL) {
2664 ads_memfree(ads, dn);
2665 return ADS_ERROR(LDAP_NO_MEMORY);
2668 *site_dn = talloc_strdup(mem_ctx, parent);
2669 if (*site_dn == NULL) {
2670 ads_memfree(ads, dn);
2671 ADS_ERROR(LDAP_NO_MEMORY);
2674 ads_memfree(ads, dn);
2675 ads_msgfree(ads, res);
2677 return status;
2681 * get the upn suffixes for a domain
2682 * @param ads connection to ads server
2683 * @param mem_ctx Pointer to talloc context
2684 * @param suffixes Pointer to an array of suffixes
2685 * @param site_name Pointer to the number of suffixes
2686 * @return status of search
2688 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char **suffixes, size_t *num_suffixes)
2690 ADS_STATUS status;
2691 void *res;
2692 const char *config_context, *base;
2693 const char *attrs[] = { "configurationNamingContext", NULL };
2694 const char *attrs2[] = { "uPNSuffixes", NULL };
2696 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2697 if (!ADS_ERR_OK(status)) {
2698 return status;
2701 config_context = ads_pull_string(ads, mem_ctx, res, "configurationNamingContext");
2702 if (config_context == NULL) {
2703 return ADS_ERROR(LDAP_NO_MEMORY);
2706 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
2707 if (base == NULL) {
2708 return ADS_ERROR(LDAP_NO_MEMORY);
2711 status = ads_search_dn(ads, &res, base, attrs2);
2712 if (!ADS_ERR_OK(status)) {
2713 return status;
2716 if (ads_count_replies(ads, res) != 1) {
2717 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2720 suffixes = ads_pull_strings(ads, mem_ctx, &res, "uPNSuffixes", num_suffixes);
2721 if (suffixes == NULL) {
2722 ads_msgfree(ads, res);
2723 return ADS_ERROR(LDAP_NO_MEMORY);
2726 ads_msgfree(ads, res);
2728 return status;
2731 #endif