r3773: more low-risk or critical changes from 3.0
[Samba.git] / source / libads / ldap.c
blob252cd718c89660938617c13954376f003c6a5b4b
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
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 #include "includes.h"
25 #ifdef HAVE_LDAP
27 /**
28 * @file ldap.c
29 * @brief basic ldap client-side routines for ads server communications
31 * The routines contained here should do the necessary ldap calls for
32 * ads setups.
34 * Important note: attribute names passed into ads_ routines must
35 * already be in UTF-8 format. We do not convert them because in almost
36 * all cases, they are just ascii (which is represented with the same
37 * codepoints in UTF-8). This may have to change at some point
38 **/
40 static SIG_ATOMIC_T gotalarm;
42 /***************************************************************
43 Signal function to tell us we timed out.
44 ****************************************************************/
46 static void gotalarm_sig(void)
48 gotalarm = 1;
51 LDAP *ldap_open_with_timeout(const char *server, int port, unsigned int to)
53 LDAP *ldp = NULL;
55 /* Setup timeout */
56 gotalarm = 0;
57 CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
58 alarm(to);
59 /* End setup timeout. */
61 ldp = ldap_open(server, port);
63 /* Teardown timeout. */
64 CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
65 alarm(0);
67 return ldp;
71 try a connection to a given ldap server, returning True and setting the servers IP
72 in the ads struct if successful
74 TODO : add a negative connection cache in here leveraged off of the one
75 found in the rpc code. --jerry
77 static BOOL ads_try_connect(ADS_STRUCT *ads, const char *server, unsigned port)
79 char *srv;
81 if (!server || !*server) {
82 return False;
85 DEBUG(5,("ads_try_connect: trying ldap server '%s' port %u\n", server, port));
87 /* this copes with inet_ntoa brokenness */
88 srv = strdup(server);
90 ads->ld = ldap_open_with_timeout(srv, port, lp_ldap_timeout());
91 if (!ads->ld) {
92 free(srv);
93 return False;
95 ads->ldap_port = port;
96 ads->ldap_ip = *interpret_addr2(srv);
97 free(srv);
99 return True;
103 try a connection to a given ldap server, based on URL, returning True if successful
105 static BOOL ads_try_connect_uri(ADS_STRUCT *ads)
107 #if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
108 DEBUG(5,("ads_try_connect: trying ldap server at URI '%s'\n",
109 ads->server.ldap_uri));
112 if (ldap_initialize((LDAP**)&(ads->ld), ads->server.ldap_uri) == LDAP_SUCCESS) {
113 return True;
115 DEBUG(0, ("ldap_initialize: %s\n", strerror(errno)));
117 #else
119 DEBUG(1, ("no URL support in LDAP libs!\n"));
120 #endif
122 return False;
125 /**********************************************************************
126 Try to find an AD dc using our internal name resolution routines
127 Try the realm first and then then workgroup name if netbios is not
128 disabled
129 **********************************************************************/
131 static BOOL ads_find_dc(ADS_STRUCT *ads)
133 const char *c_realm;
134 int count, i=0;
135 struct ip_service *ip_list;
136 pstring realm;
137 BOOL got_realm = False;
138 BOOL use_own_domain = False;
140 /* if the realm and workgroup are both empty, assume they are ours */
142 /* realm */
143 c_realm = ads->server.realm;
145 if ( !c_realm || !*c_realm ) {
146 /* special case where no realm and no workgroup means our own */
147 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
148 use_own_domain = True;
149 c_realm = lp_realm();
153 if (c_realm && *c_realm)
154 got_realm = True;
156 again:
157 /* we need to try once with the realm name and fallback to the
158 netbios domain name if we fail (if netbios has not been disabled */
160 if ( !got_realm && !lp_disable_netbios() ) {
161 c_realm = ads->server.workgroup;
162 if (!c_realm || !*c_realm) {
163 if ( use_own_domain )
164 c_realm = lp_workgroup();
167 if ( !c_realm || !*c_realm ) {
168 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
169 return False;
173 pstrcpy( realm, c_realm );
175 DEBUG(6,("ads_find_dc: looking for %s '%s'\n",
176 (got_realm ? "realm" : "domain"), realm));
178 if ( !get_sorted_dc_list(realm, &ip_list, &count, got_realm) ) {
179 /* fall back to netbios if we can */
180 if ( got_realm && !lp_disable_netbios() ) {
181 got_realm = False;
182 goto again;
185 return False;
188 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
189 for ( i=0; i<count; i++ ) {
190 /* since this is an ads conection request, default to LDAP_PORT is not set */
191 int port = (ip_list[i].port!=PORT_NONE) ? ip_list[i].port : LDAP_PORT;
192 fstring server;
194 fstrcpy( server, inet_ntoa(ip_list[i].ip) );
196 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
197 continue;
199 if ( ads_try_connect(ads, server, port) ) {
200 SAFE_FREE(ip_list);
201 return True;
204 /* keep track of failures */
205 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
208 SAFE_FREE(ip_list);
210 return False;
215 * Connect to the LDAP server
216 * @param ads Pointer to an existing ADS_STRUCT
217 * @return status of connection
219 ADS_STATUS ads_connect(ADS_STRUCT *ads)
221 int version = LDAP_VERSION3;
222 ADS_STATUS status;
224 ads->last_attempt = time(NULL);
225 ads->ld = NULL;
227 /* try with a URL based server */
229 if (ads->server.ldap_uri &&
230 ads_try_connect_uri(ads)) {
231 goto got_connection;
234 /* try with a user specified server */
235 if (ads->server.ldap_server &&
236 ads_try_connect(ads, ads->server.ldap_server, LDAP_PORT)) {
237 goto got_connection;
240 if (ads_find_dc(ads)) {
241 goto got_connection;
244 return ADS_ERROR_SYSTEM(errno?errno:ENOENT);
246 got_connection:
247 DEBUG(3,("Connected to LDAP server %s\n", inet_ntoa(ads->ldap_ip)));
249 status = ads_server_info(ads);
250 if (!ADS_ERR_OK(status)) {
251 DEBUG(1,("Failed to get ldap server info\n"));
252 return status;
255 ldap_set_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
257 if (!ads->auth.user_name) {
258 /* have to use the userPrincipalName value here and
259 not servicePrincipalName; found by Guenther Deschner @ Sernet */
261 asprintf(&ads->auth.user_name, "host/%s", global_myname() );
264 if (!ads->auth.realm) {
265 ads->auth.realm = strdup(ads->config.realm);
268 if (!ads->auth.kdc_server) {
269 ads->auth.kdc_server = strdup(inet_ntoa(ads->ldap_ip));
272 #if KRB5_DNS_HACK
273 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
274 to MIT kerberos to work (tridge) */
276 char *env;
277 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
278 setenv(env, ads->auth.kdc_server, 1);
279 free(env);
281 #endif
283 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
284 return ADS_SUCCESS;
287 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
288 return ADS_ERROR(ldap_simple_bind_s( ads->ld, NULL, NULL));
291 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
292 return ADS_ERROR(ldap_simple_bind_s( ads->ld, ads->auth.user_name, ads->auth.password));
295 return ads_sasl_bind(ads);
299 Duplicate a struct berval into talloc'ed memory
301 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
303 struct berval *value;
305 if (!in_val) return NULL;
307 value = talloc_zero(ctx, sizeof(struct berval));
308 if (value == NULL)
309 return NULL;
310 if (in_val->bv_len == 0) return value;
312 value->bv_len = in_val->bv_len;
313 value->bv_val = talloc_memdup(ctx, in_val->bv_val, in_val->bv_len);
314 return value;
318 Make a values list out of an array of (struct berval *)
320 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
321 const struct berval **in_vals)
323 struct berval **values;
324 int i;
326 if (!in_vals) return NULL;
327 for (i=0; in_vals[i]; i++); /* count values */
328 values = (struct berval **) talloc_zero(ctx,
329 (i+1)*sizeof(struct berval *));
330 if (!values) return NULL;
332 for (i=0; in_vals[i]; i++) {
333 values[i] = dup_berval(ctx, in_vals[i]);
335 return values;
339 UTF8-encode a values list out of an array of (char *)
341 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
343 char **values;
344 int i;
346 if (!in_vals) return NULL;
347 for (i=0; in_vals[i]; i++); /* count values */
348 values = (char ** ) talloc_zero(ctx, (i+1)*sizeof(char *));
349 if (!values) return NULL;
351 for (i=0; in_vals[i]; i++) {
352 push_utf8_talloc(ctx, &values[i], in_vals[i]);
354 return values;
358 Pull a (char *) array out of a UTF8-encoded values list
360 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
362 char **values;
363 int i;
365 if (!in_vals) return NULL;
366 for (i=0; in_vals[i]; i++); /* count values */
367 values = (char **) talloc_zero(ctx, (i+1)*sizeof(char *));
368 if (!values) return NULL;
370 for (i=0; in_vals[i]; i++) {
371 pull_utf8_talloc(ctx, &values[i], in_vals[i]);
373 return values;
377 * Do a search with paged results. cookie must be null on the first
378 * call, and then returned on each subsequent call. It will be null
379 * again when the entire search is complete
380 * @param ads connection to ads server
381 * @param bind_path Base dn for the search
382 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
383 * @param expr Search expression - specified in local charset
384 * @param attrs Attributes to retrieve - specified in utf8 or ascii
385 * @param res ** which will contain results - free res* with ads_msgfree()
386 * @param count Number of entries retrieved on this page
387 * @param cookie The paged results cookie to be returned on subsequent calls
388 * @return status of search
390 ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
391 int scope, const char *expr,
392 const char **attrs, void **res,
393 int *count, void **cookie)
395 int rc, i, version;
396 char *utf8_expr, *utf8_path, **search_attrs;
397 LDAPControl PagedResults, NoReferrals, *controls[3], **rcontrols;
398 BerElement *cookie_be = NULL;
399 struct berval *cookie_bv= NULL;
400 TALLOC_CTX *ctx;
402 *res = NULL;
404 if (!(ctx = talloc_init("ads_do_paged_search")))
405 return ADS_ERROR(LDAP_NO_MEMORY);
407 /* 0 means the conversion worked but the result was empty
408 so we only fail if it's -1. In any case, it always
409 at least nulls out the dest */
410 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
411 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
412 rc = LDAP_NO_MEMORY;
413 goto done;
416 if (!attrs || !(*attrs))
417 search_attrs = NULL;
418 else {
419 /* This would be the utf8-encoded version...*/
420 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
421 if (!(str_list_copy(&search_attrs, attrs))) {
422 rc = LDAP_NO_MEMORY;
423 goto done;
428 /* Paged results only available on ldap v3 or later */
429 ldap_get_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
430 if (version < LDAP_VERSION3) {
431 rc = LDAP_NOT_SUPPORTED;
432 goto done;
435 cookie_be = ber_alloc_t(LBER_USE_DER);
436 if (cookie && *cookie) {
437 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
438 ber_bvfree(*cookie); /* don't need it from last time */
439 *cookie = NULL;
440 } else {
441 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
443 ber_flatten(cookie_be, &cookie_bv);
444 PagedResults.ldctl_oid = ADS_PAGE_CTL_OID;
445 PagedResults.ldctl_iscritical = (char) 1;
446 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
447 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
449 NoReferrals.ldctl_oid = ADS_NO_REFERRALS_OID;
450 NoReferrals.ldctl_iscritical = (char) 0;
451 NoReferrals.ldctl_value.bv_len = 0;
452 NoReferrals.ldctl_value.bv_val = "";
455 controls[0] = &NoReferrals;
456 controls[1] = &PagedResults;
457 controls[2] = NULL;
459 /* we need to disable referrals as the openldap libs don't
460 handle them and paged results at the same time. Using them
461 together results in the result record containing the server
462 page control being removed from the result list (tridge/jmcd)
464 leaving this in despite the control that says don't generate
465 referrals, in case the server doesn't support it (jmcd)
467 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
469 rc = ldap_search_ext_s(ads->ld, utf8_path, scope, utf8_expr,
470 search_attrs, 0, controls,
471 NULL, NULL, LDAP_NO_LIMIT, (LDAPMessage **)res);
473 ber_free(cookie_be, 1);
474 ber_bvfree(cookie_bv);
476 if (rc) {
477 DEBUG(3,("ldap_search_ext_s(%s) -> %s\n", expr, ldap_err2string(rc)));
478 goto done;
481 rc = ldap_parse_result(ads->ld, *res, NULL, NULL, NULL,
482 NULL, &rcontrols, 0);
484 if (!rcontrols) {
485 goto done;
488 for (i=0; rcontrols[i]; i++) {
489 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
490 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
491 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
492 &cookie_bv);
493 /* the berval is the cookie, but must be freed when
494 it is all done */
495 if (cookie_bv->bv_len) /* still more to do */
496 *cookie=ber_bvdup(cookie_bv);
497 else
498 *cookie=NULL;
499 ber_bvfree(cookie_bv);
500 ber_free(cookie_be, 1);
501 break;
504 ldap_controls_free(rcontrols);
506 done:
507 talloc_destroy(ctx);
508 /* if/when we decide to utf8-encode attrs, take out this next line */
509 str_list_free(&search_attrs);
511 return ADS_ERROR(rc);
516 * Get all results for a search. This uses ads_do_paged_search() to return
517 * all entries in a large search.
518 * @param ads connection to ads server
519 * @param bind_path Base dn for the search
520 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
521 * @param expr Search expression
522 * @param attrs Attributes to retrieve
523 * @param res ** which will contain results - free res* with ads_msgfree()
524 * @return status of search
526 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
527 int scope, const char *expr,
528 const char **attrs, void **res)
530 void *cookie = NULL;
531 int count = 0;
532 ADS_STATUS status;
534 *res = NULL;
535 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, res,
536 &count, &cookie);
538 if (!ADS_ERR_OK(status)) return status;
540 while (cookie) {
541 void *res2 = NULL;
542 ADS_STATUS status2;
543 LDAPMessage *msg, *next;
545 status2 = ads_do_paged_search(ads, bind_path, scope, expr,
546 attrs, &res2, &count, &cookie);
548 if (!ADS_ERR_OK(status2)) break;
550 /* this relies on the way that ldap_add_result_entry() works internally. I hope
551 that this works on all ldap libs, but I have only tested with openldap */
552 for (msg = ads_first_entry(ads, res2); msg; msg = next) {
553 next = ads_next_entry(ads, msg);
554 ldap_add_result_entry((LDAPMessage **)res, msg);
556 /* note that we do not free res2, as the memory is now
557 part of the main returned list */
560 return status;
564 * Run a function on all results for a search. Uses ads_do_paged_search() and
565 * runs the function as each page is returned, using ads_process_results()
566 * @param ads connection to ads server
567 * @param bind_path Base dn for the search
568 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
569 * @param expr Search expression - specified in local charset
570 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
571 * @param fn Function which takes attr name, values list, and data_area
572 * @param data_area Pointer which is passed to function on each call
573 * @return status of search
575 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
576 int scope, const char *expr, const char **attrs,
577 BOOL(*fn)(char *, void **, void *),
578 void *data_area)
580 void *cookie = NULL;
581 int count = 0;
582 ADS_STATUS status;
583 void *res;
585 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
586 &count, &cookie);
588 if (!ADS_ERR_OK(status)) return status;
590 ads_process_results(ads, res, fn, data_area);
591 ads_msgfree(ads, res);
593 while (cookie) {
594 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
595 &res, &count, &cookie);
597 if (!ADS_ERR_OK(status)) break;
599 ads_process_results(ads, res, fn, data_area);
600 ads_msgfree(ads, res);
603 return status;
607 * Do a search with a timeout.
608 * @param ads connection to ads server
609 * @param bind_path Base dn for the search
610 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
611 * @param expr Search expression
612 * @param attrs Attributes to retrieve
613 * @param res ** which will contain results - free res* with ads_msgfree()
614 * @return status of search
616 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
617 const char *expr,
618 const char **attrs, void **res)
620 struct timeval timeout;
621 int rc;
622 char *utf8_expr, *utf8_path, **search_attrs = NULL;
623 TALLOC_CTX *ctx;
625 *res = NULL;
626 if (!(ctx = talloc_init("ads_do_search"))) {
627 DEBUG(1,("ads_do_search: talloc_init() failed!"));
628 return ADS_ERROR(LDAP_NO_MEMORY);
631 /* 0 means the conversion worked but the result was empty
632 so we only fail if it's negative. In any case, it always
633 at least nulls out the dest */
634 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
635 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
636 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
637 rc = LDAP_NO_MEMORY;
638 goto done;
641 if (!attrs || !(*attrs))
642 search_attrs = NULL;
643 else {
644 /* This would be the utf8-encoded version...*/
645 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
646 if (!(str_list_copy(&search_attrs, attrs)))
648 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
649 rc = LDAP_NO_MEMORY;
650 goto done;
654 timeout.tv_sec = ADS_SEARCH_TIMEOUT;
655 timeout.tv_usec = 0;
657 /* see the note in ads_do_paged_search - we *must* disable referrals */
658 ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
660 rc = ldap_search_ext_s(ads->ld, utf8_path, scope, utf8_expr,
661 search_attrs, 0, NULL, NULL,
662 &timeout, LDAP_NO_LIMIT, (LDAPMessage **)res);
664 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
665 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
666 rc = 0;
669 done:
670 talloc_destroy(ctx);
671 /* if/when we decide to utf8-encode attrs, take out this next line */
672 str_list_free(&search_attrs);
673 return ADS_ERROR(rc);
676 * Do a general ADS search
677 * @param ads connection to ads server
678 * @param res ** which will contain results - free res* with ads_msgfree()
679 * @param expr Search expression
680 * @param attrs Attributes to retrieve
681 * @return status of search
683 ADS_STATUS ads_search(ADS_STRUCT *ads, void **res,
684 const char *expr,
685 const char **attrs)
687 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
688 expr, attrs, res);
692 * Do a search on a specific DistinguishedName
693 * @param ads connection to ads server
694 * @param res ** which will contain results - free res* with ads_msgfree()
695 * @param dn DistinguishName to search
696 * @param attrs Attributes to retrieve
697 * @return status of search
699 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, void **res,
700 const char *dn,
701 const char **attrs)
703 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)", attrs, res);
707 * Free up memory from a ads_search
708 * @param ads connection to ads server
709 * @param msg Search results to free
711 void ads_msgfree(ADS_STRUCT *ads, void *msg)
713 if (!msg) return;
714 ldap_msgfree(msg);
718 * Free up memory from various ads requests
719 * @param ads connection to ads server
720 * @param mem Area to free
722 void ads_memfree(ADS_STRUCT *ads, void *mem)
724 SAFE_FREE(mem);
728 * Get a dn from search results
729 * @param ads connection to ads server
730 * @param msg Search result
731 * @return dn string
733 char *ads_get_dn(ADS_STRUCT *ads, void *msg)
735 char *utf8_dn, *unix_dn;
737 utf8_dn = ldap_get_dn(ads->ld, msg);
739 if (!utf8_dn) {
740 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
741 return NULL;
744 if (pull_utf8_allocate(&unix_dn, utf8_dn) == (size_t)-1) {
745 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
746 utf8_dn ));
747 return NULL;
749 ldap_memfree(utf8_dn);
750 return unix_dn;
754 * Find a machine account given a hostname
755 * @param ads connection to ads server
756 * @param res ** which will contain results - free res* with ads_msgfree()
757 * @param host Hostname to search for
758 * @return status of search
760 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, void **res, const char *machine)
762 ADS_STATUS status;
763 char *expr;
764 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
766 *res = NULL;
768 /* the easiest way to find a machine account anywhere in the tree
769 is to look for hostname$ */
770 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
771 DEBUG(1, ("asprintf failed!\n"));
772 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
775 status = ads_search(ads, res, expr, attrs);
776 SAFE_FREE(expr);
777 return status;
781 * Initialize a list of mods to be used in a modify request
782 * @param ctx An initialized TALLOC_CTX
783 * @return allocated ADS_MODLIST
785 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
787 #define ADS_MODLIST_ALLOC_SIZE 10
788 LDAPMod **mods;
790 if ((mods = (LDAPMod **) talloc_zero(ctx, sizeof(LDAPMod *) *
791 (ADS_MODLIST_ALLOC_SIZE + 1))))
792 /* -1 is safety to make sure we don't go over the end.
793 need to reset it to NULL before doing ldap modify */
794 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
796 return mods;
801 add an attribute to the list, with values list already constructed
803 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
804 int mod_op, const char *name,
805 const void **invals)
807 int curmod;
808 LDAPMod **modlist = (LDAPMod **) *mods;
809 struct berval **ber_values = NULL;
810 char **char_values = NULL;
812 if (!invals) {
813 mod_op = LDAP_MOD_DELETE;
814 } else {
815 if (mod_op & LDAP_MOD_BVALUES)
816 ber_values = ads_dup_values(ctx,
817 (const struct berval **)invals);
818 else
819 char_values = ads_push_strvals(ctx,
820 (const char **) invals);
823 /* find the first empty slot */
824 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
825 curmod++);
826 if (modlist[curmod] == (LDAPMod *) -1) {
827 if (!(modlist = talloc_realloc(ctx, modlist,
828 (curmod+ADS_MODLIST_ALLOC_SIZE+1)*sizeof(LDAPMod *))))
829 return ADS_ERROR(LDAP_NO_MEMORY);
830 memset(&modlist[curmod], 0,
831 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
832 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
833 *mods = modlist;
836 if (!(modlist[curmod] = talloc_zero(ctx, sizeof(LDAPMod))))
837 return ADS_ERROR(LDAP_NO_MEMORY);
838 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
839 if (mod_op & LDAP_MOD_BVALUES) {
840 modlist[curmod]->mod_bvalues = ber_values;
841 } else if (mod_op & LDAP_MOD_DELETE) {
842 modlist[curmod]->mod_values = NULL;
843 } else {
844 modlist[curmod]->mod_values = char_values;
847 modlist[curmod]->mod_op = mod_op;
848 return ADS_ERROR(LDAP_SUCCESS);
852 * Add a single string value to a mod list
853 * @param ctx An initialized TALLOC_CTX
854 * @param mods An initialized ADS_MODLIST
855 * @param name The attribute name to add
856 * @param val The value to add - NULL means DELETE
857 * @return ADS STATUS indicating success of add
859 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
860 const char *name, const char *val)
862 const char *values[2];
864 values[0] = val;
865 values[1] = NULL;
867 if (!val)
868 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
869 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name,
870 (const void **) values);
874 * Add an array of string values to a mod list
875 * @param ctx An initialized TALLOC_CTX
876 * @param mods An initialized ADS_MODLIST
877 * @param name The attribute name to add
878 * @param vals The array of string values to add - NULL means DELETE
879 * @return ADS STATUS indicating success of add
881 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
882 const char *name, const char **vals)
884 if (!vals)
885 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
886 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
887 name, (const void **) vals);
891 * Add a single ber-encoded value to a mod list
892 * @param ctx An initialized TALLOC_CTX
893 * @param mods An initialized ADS_MODLIST
894 * @param name The attribute name to add
895 * @param val The value to add - NULL means DELETE
896 * @return ADS STATUS indicating success of add
898 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
899 const char *name, const struct berval *val)
901 const struct berval *values[2];
903 values[0] = val;
904 values[1] = NULL;
905 if (!val)
906 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
907 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
908 name, (const void **) values);
912 * Perform an ldap modify
913 * @param ads connection to ads server
914 * @param mod_dn DistinguishedName to modify
915 * @param mods list of modifications to perform
916 * @return status of modify
918 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
920 int ret,i;
921 char *utf8_dn = NULL;
923 this control is needed to modify that contains a currently
924 non-existent attribute (but allowable for the object) to run
926 LDAPControl PermitModify = {
927 ADS_PERMIT_MODIFY_OID,
928 {0, NULL},
929 (char) 1};
930 LDAPControl *controls[2];
932 controls[0] = &PermitModify;
933 controls[1] = NULL;
935 if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) {
936 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
939 /* find the end of the list, marked by NULL or -1 */
940 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
941 /* make sure the end of the list is NULL */
942 mods[i] = NULL;
943 ret = ldap_modify_ext_s(ads->ld, utf8_dn,
944 (LDAPMod **) mods, controls, NULL);
945 SAFE_FREE(utf8_dn);
946 return ADS_ERROR(ret);
950 * Perform an ldap add
951 * @param ads connection to ads server
952 * @param new_dn DistinguishedName to add
953 * @param mods list of attributes and values for DN
954 * @return status of add
956 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
958 int ret, i;
959 char *utf8_dn = NULL;
961 if (push_utf8_allocate(&utf8_dn, new_dn) == -1) {
962 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
963 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
966 /* find the end of the list, marked by NULL or -1 */
967 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
968 /* make sure the end of the list is NULL */
969 mods[i] = NULL;
971 ret = ldap_add_s(ads->ld, utf8_dn, mods);
972 SAFE_FREE(utf8_dn);
973 return ADS_ERROR(ret);
977 * Delete a DistinguishedName
978 * @param ads connection to ads server
979 * @param new_dn DistinguishedName to delete
980 * @return status of delete
982 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
984 int ret;
985 char *utf8_dn = NULL;
986 if (push_utf8_allocate(&utf8_dn, del_dn) == -1) {
987 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
988 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
991 ret = ldap_delete_s(ads->ld, utf8_dn);
992 return ADS_ERROR(ret);
996 * Build an org unit string
997 * if org unit is Computers or blank then assume a container, otherwise
998 * assume a \ separated list of organisational units
999 * @param ads connection to ads server
1000 * @param org_unit Organizational unit
1001 * @return org unit string - caller must free
1003 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1005 char *ret = NULL;
1007 if (!org_unit || !*org_unit) {
1009 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1011 /* samba4 might not yet respond to a wellknownobject-query */
1012 return ret ? ret : strdup("cn=Computers");
1015 if (strequal(org_unit, "Computers")) {
1016 return strdup("cn=Computers");
1019 return ads_build_path(org_unit, "\\/", "ou=", 1);
1023 * Get a org unit string for a well-known GUID
1024 * @param ads connection to ads server
1025 * @param wknguid Well known GUID
1026 * @return org unit string - caller must free
1028 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1030 ADS_STATUS status;
1031 void *res;
1032 char *base, *wkn_dn, *ret, **wkn_dn_exp, **bind_dn_exp;
1033 const char *attrs[] = {"distinguishedName", NULL};
1034 int new_ln, wkn_ln, bind_ln, i;
1036 if (wknguid == NULL) {
1037 return NULL;
1040 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1041 DEBUG(1, ("asprintf failed!\n"));
1042 return NULL;
1045 status = ads_search_dn(ads, &res, base, attrs);
1046 if (!ADS_ERR_OK(status)) {
1047 DEBUG(1,("Failed while searching for: %s\n", base));
1048 return NULL;
1050 free(base);
1052 if (ads_count_replies(ads, res) != 1) {
1053 return NULL;
1056 /* substitute the bind-path from the well-known-guid-search result */
1057 wkn_dn = ads_get_dn(ads, res);
1058 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1059 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1061 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1063 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1066 new_ln = wkn_ln - bind_ln;
1068 ret = wkn_dn_exp[0];
1070 for (i=1; i < new_ln; i++) {
1071 char *s;
1072 asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]);
1073 ret = strdup(s);
1074 free(s);
1077 return ret;
1081 * Adds (appends) an item to an attribute array, rather then
1082 * replacing the whole list
1083 * @param ctx An initialized TALLOC_CTX
1084 * @param mods An initialized ADS_MODLIST
1085 * @param name name of the ldap attribute to append to
1086 * @param vals an array of values to add
1087 * @return status of addition
1090 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1091 const char *name, const char **vals)
1093 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name, (const void **) vals);
1097 * Determines the computer account's current KVNO via an LDAP lookup
1098 * @param ads An initialized ADS_STRUCT
1099 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1100 * @return the kvno for the computer account, or -1 in case of a failure.
1103 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *machine_name)
1105 LDAPMessage *res = NULL;
1106 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1107 char *filter;
1108 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1109 char *dn_string = NULL;
1110 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1112 DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name));
1113 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1114 return kvno;
1116 ret = ads_search(ads, (void**) &res, filter, attrs);
1117 SAFE_FREE(filter);
1118 if (!ADS_ERR_OK(ret) && ads_count_replies(ads, res)) {
1119 DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name));
1120 ads_msgfree(ads, res);
1121 return kvno;
1124 dn_string = ads_get_dn(ads, res);
1125 if (!dn_string) {
1126 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1127 ads_msgfree(ads, res);
1128 return kvno;
1130 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1131 ads_memfree(ads, dn_string);
1133 /* ---------------------------------------------------------
1134 * 0 is returned as a default KVNO from this point on...
1135 * This is done because Windows 2000 does not support key
1136 * version numbers. Chances are that a failure in the next
1137 * step is simply due to Windows 2000 being used for a
1138 * domain controller. */
1139 kvno = 0;
1141 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1142 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1143 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1144 ads_msgfree(ads, res);
1145 return kvno;
1148 /* Success */
1149 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1150 ads_msgfree(ads, res);
1151 return kvno;
1155 * This clears out all registered spn's for a given hostname
1156 * @param ads An initilaized ADS_STRUCT
1157 * @param machine_name the NetBIOS name of the computer.
1158 * @return 0 upon success, non-zero otherwise.
1161 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1163 TALLOC_CTX *ctx;
1164 LDAPMessage *res = NULL;
1165 ADS_MODLIST mods;
1166 const char *servicePrincipalName[1] = {NULL};
1167 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1168 char *dn_string = NULL;
1170 ret = ads_find_machine_acct(ads, (void **)&res, machine_name);
1171 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1172 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1173 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1174 ads_msgfree(ads, res);
1175 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1178 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1179 ctx = talloc_init("ads_clear_service_principal_names");
1180 if (!ctx) {
1181 ads_msgfree(ads, res);
1182 return ADS_ERROR(LDAP_NO_MEMORY);
1185 if (!(mods = ads_init_mods(ctx))) {
1186 talloc_destroy(ctx);
1187 ads_msgfree(ads, res);
1188 return ADS_ERROR(LDAP_NO_MEMORY);
1190 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1191 if (!ADS_ERR_OK(ret)) {
1192 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1193 ads_msgfree(ads, res);
1194 talloc_destroy(ctx);
1195 return ret;
1197 dn_string = ads_get_dn(ads, res);
1198 if (!dn_string) {
1199 talloc_destroy(ctx);
1200 ads_msgfree(ads, res);
1201 return ADS_ERROR(LDAP_NO_MEMORY);
1203 ret = ads_gen_mod(ads, dn_string, mods);
1204 ads_memfree(ads,dn_string);
1205 if (!ADS_ERR_OK(ret)) {
1206 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1207 machine_name));
1208 ads_msgfree(ads, res);
1209 talloc_destroy(ctx);
1210 return ret;
1213 ads_msgfree(ads, res);
1214 talloc_destroy(ctx);
1215 return ret;
1219 * This adds a service principal name to an existing computer account
1220 * (found by hostname) in AD.
1221 * @param ads An initialized ADS_STRUCT
1222 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1223 * @param spn A string of the service principal to add, i.e. 'host'
1224 * @return 0 upon sucess, or non-zero if a failure occurs
1227 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name, const char *spn)
1229 ADS_STATUS ret;
1230 TALLOC_CTX *ctx;
1231 LDAPMessage *res = NULL;
1232 char *host_spn, *host_upn, *psp1, *psp2, *psp3;
1233 ADS_MODLIST mods;
1234 fstring my_fqdn;
1235 char *dn_string = NULL;
1236 const char *servicePrincipalName[4] = {NULL, NULL, NULL, NULL};
1238 ret = ads_find_machine_acct(ads, (void **)&res, machine_name);
1239 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1240 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1241 machine_name));
1242 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1243 spn, machine_name, ads->config.realm));
1244 ads_msgfree(ads, res);
1245 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1248 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1249 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1250 ads_msgfree(ads, res);
1251 return ADS_ERROR(LDAP_NO_MEMORY);
1254 name_to_fqdn(my_fqdn, machine_name);
1255 strlower_m(my_fqdn);
1257 if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", my_fqdn))) {
1258 talloc_destroy(ctx);
1259 ads_msgfree(ads, res);
1260 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1262 if (!(host_upn = talloc_asprintf(ctx, "%s@%s", host_spn, ads->config.realm))) {
1263 talloc_destroy(ctx);
1264 ads_msgfree(ads, res);
1265 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1268 /* Add the extra principal */
1269 psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name);
1270 strupper_m(psp1);
1271 strlower_m(&psp1[strlen(spn)]);
1272 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp1, machine_name));
1273 servicePrincipalName[0] = psp1;
1274 psp2 = talloc_asprintf(ctx, "%s/%s.%s", spn, machine_name, ads->config.realm);
1275 strupper_m(psp2);
1276 strlower_m(&psp2[strlen(spn)]);
1277 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp2, machine_name));
1278 servicePrincipalName[1] = psp2;
1280 /* Add another principal in case the realm != the DNS domain, so that
1281 * the KDC doesn't send "server principal unknown" errors to clients
1282 * which use the DNS name in determining service principal names. */
1283 psp3 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn);
1284 strupper_m(psp3);
1285 strlower_m(&psp3[strlen(spn)]);
1286 if (strcmp(psp2, psp3) != 0) {
1287 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp3, machine_name));
1288 servicePrincipalName[2] = psp3;
1291 if (!(mods = ads_init_mods(ctx))) {
1292 talloc_destroy(ctx);
1293 ads_msgfree(ads, res);
1294 return ADS_ERROR(LDAP_NO_MEMORY);
1296 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1297 if (!ADS_ERR_OK(ret)) {
1298 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1299 talloc_destroy(ctx);
1300 ads_msgfree(ads, res);
1301 return ret;
1303 dn_string = ads_get_dn(ads, res);
1304 if (!dn_string) {
1305 talloc_destroy(ctx);
1306 ads_msgfree(ads, res);
1307 return ADS_ERROR(LDAP_NO_MEMORY);
1309 ret = ads_gen_mod(ads, dn_string, mods);
1310 ads_memfree(ads,dn_string);
1311 if (!ADS_ERR_OK(ret)) {
1312 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1313 talloc_destroy(ctx);
1314 ads_msgfree(ads, res);
1315 return ret;
1318 talloc_destroy(ctx);
1319 ads_msgfree(ads, res);
1320 return ret;
1324 * adds a machine account to the ADS server
1325 * @param ads An intialized ADS_STRUCT
1326 * @param machine_name - the NetBIOS machine name of this account.
1327 * @param account_type A number indicating the type of account to create
1328 * @param org_unit The LDAP path in which to place this account
1329 * @return 0 upon success, or non-zero otherwise
1332 static ADS_STATUS ads_add_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1333 uint32 account_type,
1334 const char *org_unit)
1336 ADS_STATUS ret, status;
1337 char *host_spn, *host_upn, *new_dn, *samAccountName, *controlstr;
1338 TALLOC_CTX *ctx;
1339 ADS_MODLIST mods;
1340 const char *objectClass[] = {"top", "person", "organizationalPerson",
1341 "user", "computer", NULL};
1342 const char *servicePrincipalName[7] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
1343 char *psp, *psp2, *psp3, *psp4;
1344 unsigned acct_control;
1345 unsigned exists=0;
1346 fstring my_fqdn;
1347 LDAPMessage *res = NULL;
1348 int i, next_spn;
1350 if (!(ctx = talloc_init("ads_add_machine_acct")))
1351 return ADS_ERROR(LDAP_NO_MEMORY);
1353 ret = ADS_ERROR(LDAP_NO_MEMORY);
1355 name_to_fqdn(my_fqdn, machine_name);
1357 status = ads_find_machine_acct(ads, (void **)&res, machine_name);
1358 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1359 char *dn_string = ads_get_dn(ads, res);
1360 if (!dn_string) {
1361 DEBUG(1, ("ads_add_machine_acct: ads_get_dn returned NULL (malloc failure?)\n"));
1362 goto done;
1364 new_dn = talloc_strdup(ctx, dn_string);
1365 ads_memfree(ads,dn_string);
1366 DEBUG(0, ("ads_add_machine_acct: Host account for %s already exists - modifying old account\n",
1367 machine_name));
1368 exists=1;
1369 } else {
1370 char *ou_str = ads_ou_string(ads,org_unit);
1371 if (!ou_str) {
1372 DEBUG(1, ("ads_add_machine_acct: ads_ou_string returned NULL (malloc failure?)\n"));
1373 goto done;
1375 new_dn = talloc_asprintf(ctx, "cn=%s,%s,%s", machine_name, ou_str,
1376 ads->config.bind_path);
1378 SAFE_FREE(ou_str);
1381 if (!new_dn) {
1382 goto done;
1385 if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", machine_name)))
1386 goto done;
1387 if (!(host_upn = talloc_asprintf(ctx, "%s@%s", host_spn, ads->config.realm)))
1388 goto done;
1389 servicePrincipalName[0] = talloc_asprintf(ctx, "HOST/%s", machine_name);
1390 psp = talloc_asprintf(ctx, "HOST/%s.%s",
1391 machine_name,
1392 ads->config.realm);
1393 strlower_m(&psp[5]);
1394 servicePrincipalName[1] = psp;
1395 servicePrincipalName[2] = talloc_asprintf(ctx, "CIFS/%s", machine_name);
1396 psp2 = talloc_asprintf(ctx, "CIFS/%s.%s",
1397 machine_name,
1398 ads->config.realm);
1399 strlower_m(&psp2[5]);
1400 servicePrincipalName[3] = psp2;
1402 /* Ensure servicePrincipalName[4] and [5] are unique. */
1403 strlower_m(my_fqdn);
1404 psp3 = talloc_asprintf(ctx, "CIFS/%s", my_fqdn);
1405 strlower_m(&psp3[5]);
1407 next_spn = 4;
1408 for (i = 0; i < next_spn; i++) {
1409 if (strequal(servicePrincipalName[i], psp3))
1410 break;
1412 if (i == next_spn) {
1413 servicePrincipalName[next_spn++] = psp3;
1416 psp4 = talloc_asprintf(ctx, "HOST/%s", my_fqdn);
1417 strlower_m(&psp4[5]);
1418 for (i = 0; i < next_spn; i++) {
1419 if (strequal(servicePrincipalName[i], psp3))
1420 break;
1422 if (i == next_spn) {
1423 servicePrincipalName[next_spn++] = psp4;
1426 if (!(samAccountName = talloc_asprintf(ctx, "%s$", machine_name))) {
1427 goto done;
1430 acct_control = account_type | UF_DONT_EXPIRE_PASSWD;
1431 #ifndef ENCTYPE_ARCFOUR_HMAC
1432 acct_control |= UF_USE_DES_KEY_ONLY;
1433 #endif
1435 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
1436 goto done;
1439 if (!(mods = ads_init_mods(ctx))) {
1440 goto done;
1443 if (!exists) {
1444 ads_mod_str(ctx, &mods, "cn", machine_name);
1445 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1446 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1447 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1449 ads_mod_str(ctx, &mods, "dNSHostName", my_fqdn);
1450 ads_mod_str(ctx, &mods, "userPrincipalName", host_upn);
1451 ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1452 ads_mod_str(ctx, &mods, "operatingSystem", "Samba");
1453 ads_mod_str(ctx, &mods, "operatingSystemVersion", SAMBA_VERSION_STRING);
1455 if (!exists) {
1456 ret = ads_gen_add(ads, new_dn, mods);
1457 } else {
1458 ret = ads_gen_mod(ads, new_dn, mods);
1461 if (!ADS_ERR_OK(ret)) {
1462 goto done;
1465 /* Do not fail if we can't set security descriptor
1466 * it shouldn't be mandatory and probably we just
1467 * don't have enough rights to do it.
1469 if (!exists) {
1470 status = ads_set_machine_sd(ads, machine_name, new_dn);
1472 if (!ADS_ERR_OK(status)) {
1473 DEBUG(0, ("Warning: ads_set_machine_sd: %s\n",
1474 ads_errstr(status)));
1477 done:
1478 ads_msgfree(ads, res);
1479 talloc_destroy(ctx);
1480 return ret;
1484 dump a binary result from ldap
1486 static void dump_binary(const char *field, struct berval **values)
1488 int i, j;
1489 for (i=0; values[i]; i++) {
1490 printf("%s: ", field);
1491 for (j=0; j<values[i]->bv_len; j++) {
1492 printf("%02X", (unsigned char)values[i]->bv_val[j]);
1494 printf("\n");
1498 static void dump_guid(const char *field, struct berval **values)
1500 int i;
1501 UUID_FLAT guid;
1502 for (i=0; values[i]; i++) {
1503 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1504 printf("%s: %s\n", field,
1505 smb_uuid_string_static(smb_uuid_unpack_static(guid)));
1510 dump a sid result from ldap
1512 static void dump_sid(const char *field, struct berval **values)
1514 int i;
1515 for (i=0; values[i]; i++) {
1516 DOM_SID sid;
1517 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1518 printf("%s: %s\n", field, sid_string_static(&sid));
1523 dump ntSecurityDescriptor
1525 static void dump_sd(const char *filed, struct berval **values)
1527 prs_struct ps;
1529 SEC_DESC *psd = 0;
1530 TALLOC_CTX *ctx = 0;
1532 if (!(ctx = talloc_init("sec_io_desc")))
1533 return;
1535 /* prepare data */
1536 prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
1537 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
1538 prs_set_offset(&ps,0);
1540 /* parse secdesc */
1541 if (!sec_io_desc("sd", &psd, &ps, 1)) {
1542 prs_mem_free(&ps);
1543 talloc_destroy(ctx);
1544 return;
1546 if (psd) ads_disp_sd(psd);
1548 prs_mem_free(&ps);
1549 talloc_destroy(ctx);
1553 dump a string result from ldap
1555 static void dump_string(const char *field, char **values)
1557 int i;
1558 for (i=0; values[i]; i++) {
1559 printf("%s: %s\n", field, values[i]);
1564 dump a field from LDAP on stdout
1565 used for debugging
1568 static BOOL ads_dump_field(char *field, void **values, void *data_area)
1570 const struct {
1571 const char *name;
1572 BOOL string;
1573 void (*handler)(const char *, struct berval **);
1574 } handlers[] = {
1575 {"objectGUID", False, dump_guid},
1576 {"nTSecurityDescriptor", False, dump_sd},
1577 {"dnsRecord", False, dump_binary},
1578 {"objectSid", False, dump_sid},
1579 {"tokenGroups", False, dump_sid},
1580 {NULL, True, NULL}
1582 int i;
1584 if (!field) { /* must be end of an entry */
1585 printf("\n");
1586 return False;
1589 for (i=0; handlers[i].name; i++) {
1590 if (StrCaseCmp(handlers[i].name, field) == 0) {
1591 if (!values) /* first time, indicate string or not */
1592 return handlers[i].string;
1593 handlers[i].handler(field, (struct berval **) values);
1594 break;
1597 if (!handlers[i].name) {
1598 if (!values) /* first time, indicate string conversion */
1599 return True;
1600 dump_string(field, (char **)values);
1602 return False;
1606 * Dump a result from LDAP on stdout
1607 * used for debugging
1608 * @param ads connection to ads server
1609 * @param res Results to dump
1612 void ads_dump(ADS_STRUCT *ads, void *res)
1614 ads_process_results(ads, res, ads_dump_field, NULL);
1618 * Walk through results, calling a function for each entry found.
1619 * The function receives a field name, a berval * array of values,
1620 * and a data area passed through from the start. The function is
1621 * called once with null for field and values at the end of each
1622 * entry.
1623 * @param ads connection to ads server
1624 * @param res Results to process
1625 * @param fn Function for processing each result
1626 * @param data_area user-defined area to pass to function
1628 void ads_process_results(ADS_STRUCT *ads, void *res,
1629 BOOL(*fn)(char *, void **, void *),
1630 void *data_area)
1632 void *msg;
1633 TALLOC_CTX *ctx;
1635 if (!(ctx = talloc_init("ads_process_results")))
1636 return;
1638 for (msg = ads_first_entry(ads, res); msg;
1639 msg = ads_next_entry(ads, msg)) {
1640 char *utf8_field;
1641 BerElement *b;
1643 for (utf8_field=ldap_first_attribute(ads->ld,
1644 (LDAPMessage *)msg,&b);
1645 utf8_field;
1646 utf8_field=ldap_next_attribute(ads->ld,
1647 (LDAPMessage *)msg,b)) {
1648 struct berval **ber_vals;
1649 char **str_vals, **utf8_vals;
1650 char *field;
1651 BOOL string;
1653 pull_utf8_talloc(ctx, &field, utf8_field);
1654 string = fn(field, NULL, data_area);
1656 if (string) {
1657 utf8_vals = ldap_get_values(ads->ld,
1658 (LDAPMessage *)msg, field);
1659 str_vals = ads_pull_strvals(ctx,
1660 (const char **) utf8_vals);
1661 fn(field, (void **) str_vals, data_area);
1662 ldap_value_free(utf8_vals);
1663 } else {
1664 ber_vals = ldap_get_values_len(ads->ld,
1665 (LDAPMessage *)msg, field);
1666 fn(field, (void **) ber_vals, data_area);
1668 ldap_value_free_len(ber_vals);
1670 ldap_memfree(utf8_field);
1672 ber_free(b, 0);
1673 talloc_destroy_pool(ctx);
1674 fn(NULL, NULL, data_area); /* completed an entry */
1677 talloc_destroy(ctx);
1681 * count how many replies are in a LDAPMessage
1682 * @param ads connection to ads server
1683 * @param res Results to count
1684 * @return number of replies
1686 int ads_count_replies(ADS_STRUCT *ads, void *res)
1688 return ldap_count_entries(ads->ld, (LDAPMessage *)res);
1692 * Join a machine to a realm
1693 * Creates the machine account and sets the machine password
1694 * @param ads connection to ads server
1695 * @param machine name of host to add
1696 * @param org_unit Organizational unit to place machine in
1697 * @return status of join
1699 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
1700 uint32 account_type, const char *org_unit)
1702 ADS_STATUS status;
1703 LDAPMessage *res = NULL;
1704 char *machine;
1706 /* machine name must be lowercase */
1707 machine = strdup(machine_name);
1708 strlower_m(machine);
1711 status = ads_find_machine_acct(ads, (void **)&res, machine);
1712 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1713 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
1714 status = ads_leave_realm(ads, machine);
1715 if (!ADS_ERR_OK(status)) {
1716 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
1717 machine, ads->config.realm));
1718 return status;
1723 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
1724 if (!ADS_ERR_OK(status)) {
1725 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
1726 SAFE_FREE(machine);
1727 return status;
1730 status = ads_find_machine_acct(ads, (void **)&res, machine);
1731 if (!ADS_ERR_OK(status)) {
1732 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
1733 SAFE_FREE(machine);
1734 return status;
1737 SAFE_FREE(machine);
1738 ads_msgfree(ads, res);
1740 return status;
1744 * Delete a machine from the realm
1745 * @param ads connection to ads server
1746 * @param hostname Machine to remove
1747 * @return status of delete
1749 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
1751 ADS_STATUS status;
1752 void *res, *msg;
1753 char *hostnameDN, *host;
1754 int rc;
1756 /* hostname must be lowercase */
1757 host = strdup(hostname);
1758 strlower_m(host);
1760 status = ads_find_machine_acct(ads, &res, host);
1761 if (!ADS_ERR_OK(status)) {
1762 DEBUG(0, ("Host account for %s does not exist.\n", host));
1763 return status;
1766 msg = ads_first_entry(ads, res);
1767 if (!msg) {
1768 return ADS_ERROR_SYSTEM(ENOENT);
1771 hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
1772 rc = ldap_delete_s(ads->ld, hostnameDN);
1773 ads_memfree(ads, hostnameDN);
1774 if (rc != LDAP_SUCCESS) {
1775 return ADS_ERROR(rc);
1778 status = ads_find_machine_acct(ads, &res, host);
1779 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
1780 DEBUG(0, ("Failed to remove host account.\n"));
1781 return status;
1784 free(host);
1786 return status;
1790 * add machine account to existing security descriptor
1791 * @param ads connection to ads server
1792 * @param hostname machine to add
1793 * @param dn DN of security descriptor
1794 * @return status
1796 ADS_STATUS ads_set_machine_sd(ADS_STRUCT *ads, const char *hostname, char *dn)
1798 const char *attrs[] = {"nTSecurityDescriptor", "objectSid", 0};
1799 char *expr = 0;
1800 size_t sd_size = 0;
1801 struct berval bval = {0, NULL};
1802 prs_struct ps_wire;
1803 char *escaped_hostname = escape_ldap_string_alloc(hostname);
1805 LDAPMessage *res = 0;
1806 LDAPMessage *msg = 0;
1807 ADS_MODLIST mods = 0;
1809 NTSTATUS status;
1810 ADS_STATUS ret;
1811 DOM_SID sid;
1812 SEC_DESC *psd = NULL;
1813 TALLOC_CTX *ctx = NULL;
1815 /* Avoid segmentation fault in prs_mem_free if
1816 * we have to bail out before prs_init */
1817 ps_wire.is_dynamic = False;
1819 if (!ads) return ADS_ERROR(LDAP_SERVER_DOWN);
1821 ret = ADS_ERROR(LDAP_SUCCESS);
1823 if (!escaped_hostname) {
1824 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1827 if (asprintf(&expr, "(samAccountName=%s$)", escaped_hostname) == -1) {
1828 DEBUG(1, ("ads_set_machine_sd: asprintf failed!\n"));
1829 SAFE_FREE(escaped_hostname);
1830 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1833 SAFE_FREE(escaped_hostname);
1835 ret = ads_search(ads, (void *) &res, expr, attrs);
1837 if (!ADS_ERR_OK(ret)) return ret;
1839 if ( !(msg = ads_first_entry(ads, res) )) {
1840 ret = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
1841 goto ads_set_sd_error;
1844 if (!ads_pull_sid(ads, msg, attrs[1], &sid)) {
1845 ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
1846 goto ads_set_sd_error;
1849 if (!(ctx = talloc_init("sec_io_desc"))) {
1850 ret = ADS_ERROR(LDAP_NO_MEMORY);
1851 goto ads_set_sd_error;
1854 if (!ads_pull_sd(ads, ctx, msg, attrs[0], &psd)) {
1855 ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
1856 goto ads_set_sd_error;
1859 status = sec_desc_add_sid(ctx, &psd, &sid, SEC_RIGHTS_FULL_CTRL, &sd_size);
1861 if (!NT_STATUS_IS_OK(status)) {
1862 ret = ADS_ERROR_NT(status);
1863 goto ads_set_sd_error;
1866 if (!prs_init(&ps_wire, sd_size, ctx, MARSHALL)) {
1867 ret = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1870 if (!sec_io_desc("sd_wire", &psd, &ps_wire, 1)) {
1871 ret = ADS_ERROR(LDAP_NO_MEMORY);
1872 goto ads_set_sd_error;
1875 #if 0
1876 file_save("/tmp/sec_desc.new", ps_wire.data_p, sd_size);
1877 #endif
1878 if (!(mods = ads_init_mods(ctx))) return ADS_ERROR(LDAP_NO_MEMORY);
1880 bval.bv_len = prs_offset(&ps_wire);
1881 bval.bv_val = talloc(ctx, bval.bv_len);
1882 if (!bval.bv_val) {
1883 ret = ADS_ERROR(LDAP_NO_MEMORY);
1884 goto ads_set_sd_error;
1887 prs_set_offset(&ps_wire, 0);
1889 if (!prs_copy_data_out(bval.bv_val, &ps_wire, bval.bv_len)) {
1890 ret = ADS_ERROR(LDAP_NO_MEMORY);
1891 goto ads_set_sd_error;
1894 ret = ads_mod_ber(ctx, &mods, attrs[0], &bval);
1895 if (ADS_ERR_OK(ret)) {
1896 ret = ads_gen_mod(ads, dn, mods);
1899 ads_set_sd_error:
1900 ads_msgfree(ads, res);
1901 prs_mem_free(&ps_wire);
1902 talloc_destroy(ctx);
1903 return ret;
1907 * pull the first entry from a ADS result
1908 * @param ads connection to ads server
1909 * @param res Results of search
1910 * @return first entry from result
1912 void *ads_first_entry(ADS_STRUCT *ads, void *res)
1914 return (void *)ldap_first_entry(ads->ld, (LDAPMessage *)res);
1918 * pull the next entry from a ADS result
1919 * @param ads connection to ads server
1920 * @param res Results of search
1921 * @return next entry from result
1923 void *ads_next_entry(ADS_STRUCT *ads, void *res)
1925 return (void *)ldap_next_entry(ads->ld, (LDAPMessage *)res);
1929 * pull a single string from a ADS result
1930 * @param ads connection to ads server
1931 * @param mem_ctx TALLOC_CTX to use for allocating result string
1932 * @param msg Results of search
1933 * @param field Attribute to retrieve
1934 * @return Result string in talloc context
1936 char *ads_pull_string(ADS_STRUCT *ads,
1937 TALLOC_CTX *mem_ctx, void *msg, const char *field)
1939 char **values;
1940 char *ret = NULL;
1941 char *ux_string;
1942 size_t rc;
1944 values = ldap_get_values(ads->ld, msg, field);
1945 if (!values)
1946 return NULL;
1948 if (values[0]) {
1949 rc = pull_utf8_talloc(mem_ctx, &ux_string,
1950 values[0]);
1951 if (rc != (size_t)-1)
1952 ret = ux_string;
1955 ldap_value_free(values);
1956 return ret;
1960 * pull an array of strings from a ADS result
1961 * @param ads connection to ads server
1962 * @param mem_ctx TALLOC_CTX to use for allocating result string
1963 * @param msg Results of search
1964 * @param field Attribute to retrieve
1965 * @return Result strings in talloc context
1967 char **ads_pull_strings(ADS_STRUCT *ads,
1968 TALLOC_CTX *mem_ctx, void *msg, const char *field,
1969 size_t *num_values)
1971 char **values;
1972 char **ret = NULL;
1973 int i;
1975 values = ldap_get_values(ads->ld, msg, field);
1976 if (!values)
1977 return NULL;
1979 *num_values = ldap_count_values(values);
1981 ret = talloc(mem_ctx, sizeof(char *) * (*num_values+1));
1982 if (!ret) {
1983 ldap_value_free(values);
1984 return NULL;
1987 for (i=0;i<*num_values;i++) {
1988 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
1989 ldap_value_free(values);
1990 return NULL;
1993 ret[i] = NULL;
1995 ldap_value_free(values);
1996 return ret;
2000 * pull an array of strings from a ADS result
2001 * (handle large multivalue attributes with range retrieval)
2002 * @param ads connection to ads server
2003 * @param mem_ctx TALLOC_CTX to use for allocating result string
2004 * @param msg Results of search
2005 * @param field Attribute to retrieve
2006 * @param current_strings strings returned by a previous call to this function
2007 * @param next_attribute The next query should ask for this attribute
2008 * @param num_values How many values did we get this time?
2009 * @param more_values Are there more values to get?
2010 * @return Result strings in talloc context
2012 char **ads_pull_strings_range(ADS_STRUCT *ads,
2013 TALLOC_CTX *mem_ctx,
2014 void *msg, const char *field,
2015 char **current_strings,
2016 const char **next_attribute,
2017 size_t *num_strings,
2018 BOOL *more_strings)
2020 char *attr;
2021 char *expected_range_attrib, *range_attr;
2022 BerElement *ptr = NULL;
2023 char **strings;
2024 char **new_strings;
2025 size_t num_new_strings;
2026 unsigned long int range_start;
2027 unsigned long int range_end;
2029 /* we might have been given the whole lot anyway */
2030 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2031 *more_strings = False;
2032 return strings;
2035 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2037 /* look for Range result */
2038 for (attr = ldap_first_attribute(ads->ld, (LDAPMessage *)msg, &ptr);
2039 attr;
2040 attr = ldap_next_attribute(ads->ld, (LDAPMessage *)msg, ptr)) {
2041 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2042 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2043 range_attr = attr;
2044 break;
2046 ldap_memfree(attr);
2048 if (!attr) {
2049 ber_free(ptr, 0);
2050 /* nothing here - this field is just empty */
2051 *more_strings = False;
2052 return NULL;
2055 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2056 &range_start, &range_end) == 2) {
2057 *more_strings = True;
2058 } else {
2059 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2060 &range_start) == 1) {
2061 *more_strings = False;
2062 } else {
2063 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2064 range_attr));
2065 ldap_memfree(range_attr);
2066 *more_strings = False;
2067 return NULL;
2071 if ((*num_strings) != range_start) {
2072 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2073 " - aborting range retreival\n",
2074 range_attr, *num_strings + 1, range_start));
2075 ldap_memfree(range_attr);
2076 *more_strings = False;
2077 return NULL;
2080 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2082 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2083 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2084 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2085 range_attr, (unsigned long int)range_end - range_start + 1,
2086 (unsigned long int)num_new_strings));
2087 ldap_memfree(range_attr);
2088 *more_strings = False;
2089 return NULL;
2092 strings = talloc_realloc(mem_ctx, current_strings,
2093 sizeof(*current_strings) *
2094 (*num_strings + num_new_strings));
2096 if (strings == NULL) {
2097 ldap_memfree(range_attr);
2098 *more_strings = False;
2099 return NULL;
2102 memcpy(&strings[*num_strings], new_strings,
2103 sizeof(*new_strings) * num_new_strings);
2105 (*num_strings) += num_new_strings;
2107 if (*more_strings) {
2108 *next_attribute = talloc_asprintf(mem_ctx,
2109 "%s;range=%d-*",
2110 field,
2111 *num_strings);
2113 if (!*next_attribute) {
2114 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2115 ldap_memfree(range_attr);
2116 *more_strings = False;
2117 return NULL;
2121 ldap_memfree(range_attr);
2123 return strings;
2127 * pull a single uint32 from a ADS result
2128 * @param ads connection to ads server
2129 * @param msg Results of search
2130 * @param field Attribute to retrieve
2131 * @param v Pointer to int to store result
2132 * @return boolean inidicating success
2134 BOOL ads_pull_uint32(ADS_STRUCT *ads,
2135 void *msg, const char *field, uint32 *v)
2137 char **values;
2139 values = ldap_get_values(ads->ld, msg, field);
2140 if (!values)
2141 return False;
2142 if (!values[0]) {
2143 ldap_value_free(values);
2144 return False;
2147 *v = atoi(values[0]);
2148 ldap_value_free(values);
2149 return True;
2153 * pull a single objectGUID from an ADS result
2154 * @param ads connection to ADS server
2155 * @param msg results of search
2156 * @param guid 37-byte area to receive text guid
2157 * @return boolean indicating success
2159 BOOL ads_pull_guid(ADS_STRUCT *ads,
2160 void *msg, struct uuid *guid)
2162 char **values;
2163 UUID_FLAT flat_guid;
2165 values = ldap_get_values(ads->ld, msg, "objectGUID");
2166 if (!values)
2167 return False;
2169 if (values[0]) {
2170 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2171 smb_uuid_unpack(flat_guid, guid);
2172 ldap_value_free(values);
2173 return True;
2175 ldap_value_free(values);
2176 return False;
2182 * pull a single DOM_SID from a ADS result
2183 * @param ads connection to ads server
2184 * @param msg Results of search
2185 * @param field Attribute to retrieve
2186 * @param sid Pointer to sid to store result
2187 * @return boolean inidicating success
2189 BOOL ads_pull_sid(ADS_STRUCT *ads,
2190 void *msg, const char *field, DOM_SID *sid)
2192 struct berval **values;
2193 BOOL ret = False;
2195 values = ldap_get_values_len(ads->ld, msg, field);
2197 if (!values)
2198 return False;
2200 if (values[0])
2201 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2203 ldap_value_free_len(values);
2204 return ret;
2208 * pull an array of DOM_SIDs from a ADS result
2209 * @param ads connection to ads server
2210 * @param mem_ctx TALLOC_CTX for allocating sid array
2211 * @param msg Results of search
2212 * @param field Attribute to retrieve
2213 * @param sids pointer to sid array to allocate
2214 * @return the count of SIDs pulled
2216 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2217 void *msg, const char *field, DOM_SID **sids)
2219 struct berval **values;
2220 BOOL ret;
2221 int count, i;
2223 values = ldap_get_values_len(ads->ld, msg, field);
2225 if (!values)
2226 return 0;
2228 for (i=0; values[i]; i++)
2229 /* nop */ ;
2231 (*sids) = talloc(mem_ctx, sizeof(DOM_SID) * i);
2232 if (!(*sids)) {
2233 ldap_value_free_len(values);
2234 return 0;
2237 count = 0;
2238 for (i=0; values[i]; i++) {
2239 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2240 if (ret) {
2241 fstring sid;
2242 DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid, &(*sids)[count])));
2243 count++;
2247 ldap_value_free_len(values);
2248 return count;
2252 * pull a SEC_DESC from a ADS result
2253 * @param ads connection to ads server
2254 * @param mem_ctx TALLOC_CTX for allocating sid array
2255 * @param msg Results of search
2256 * @param field Attribute to retrieve
2257 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2258 * @return boolean inidicating success
2260 BOOL ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2261 void *msg, const char *field, SEC_DESC **sd)
2263 struct berval **values;
2264 prs_struct ps;
2265 BOOL ret = False;
2267 values = ldap_get_values_len(ads->ld, msg, field);
2269 if (!values) return False;
2271 if (values[0]) {
2272 prs_init(&ps, values[0]->bv_len, mem_ctx, UNMARSHALL);
2273 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
2274 prs_set_offset(&ps,0);
2276 ret = sec_io_desc("sd", sd, &ps, 1);
2279 ldap_value_free_len(values);
2280 return ret;
2284 * in order to support usernames longer than 21 characters we need to
2285 * use both the sAMAccountName and the userPrincipalName attributes
2286 * It seems that not all users have the userPrincipalName attribute set
2288 * @param ads connection to ads server
2289 * @param mem_ctx TALLOC_CTX for allocating sid array
2290 * @param msg Results of search
2291 * @return the username
2293 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, void *msg)
2295 #if 0 /* JERRY */
2296 char *ret, *p;
2298 /* lookup_name() only works on the sAMAccountName to
2299 returning the username portion of userPrincipalName
2300 breaks winbindd_getpwnam() */
2302 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2303 if (ret && (p = strchr_m(ret, '@'))) {
2304 *p = 0;
2305 return ret;
2307 #endif
2308 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2313 * find the update serial number - this is the core of the ldap cache
2314 * @param ads connection to ads server
2315 * @param ads connection to ADS server
2316 * @param usn Pointer to retrieved update serial number
2317 * @return status of search
2319 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2321 const char *attrs[] = {"highestCommittedUSN", NULL};
2322 ADS_STATUS status;
2323 void *res;
2325 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2326 if (!ADS_ERR_OK(status))
2327 return status;
2329 if (ads_count_replies(ads, res) != 1) {
2330 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2333 ads_pull_uint32(ads, res, "highestCommittedUSN", usn);
2334 ads_msgfree(ads, res);
2335 return ADS_SUCCESS;
2338 /* parse a ADS timestring - typical string is
2339 '20020917091222.0Z0' which means 09:12.22 17th September
2340 2002, timezone 0 */
2341 static time_t ads_parse_time(const char *str)
2343 struct tm tm;
2345 ZERO_STRUCT(tm);
2347 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2348 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2349 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2350 return 0;
2352 tm.tm_year -= 1900;
2353 tm.tm_mon -= 1;
2355 return timegm(&tm);
2360 * Find the servers name and realm - this can be done before authentication
2361 * The ldapServiceName field on w2k looks like this:
2362 * vnet3.home.samba.org:win2000-vnet3$@VNET3.HOME.SAMBA.ORG
2363 * @param ads connection to ads server
2364 * @return status of search
2366 ADS_STATUS ads_server_info(ADS_STRUCT *ads)
2368 const char *attrs[] = {"ldapServiceName", "currentTime", NULL};
2369 ADS_STATUS status;
2370 void *res;
2371 char *value;
2372 char *p;
2373 char *timestr;
2374 TALLOC_CTX *ctx;
2376 if (!(ctx = talloc_init("ads_server_info"))) {
2377 return ADS_ERROR(LDAP_NO_MEMORY);
2380 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2381 if (!ADS_ERR_OK(status)) {
2382 talloc_destroy(ctx);
2383 return status;
2386 value = ads_pull_string(ads, ctx, res, "ldapServiceName");
2387 if (!value) {
2388 ads_msgfree(ads, res);
2389 talloc_destroy(ctx);
2390 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2393 timestr = ads_pull_string(ads, ctx, res, "currentTime");
2394 if (!timestr) {
2395 ads_msgfree(ads, res);
2396 talloc_destroy(ctx);
2397 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2400 ads_msgfree(ads, res);
2402 p = strchr(value, ':');
2403 if (!p) {
2404 talloc_destroy(ctx);
2405 DEBUG(1, ("ads_server_info: returned ldap server name did not contain a ':' "
2406 "so was deemed invalid\n"));
2407 return ADS_ERROR(LDAP_DECODING_ERROR);
2410 SAFE_FREE(ads->config.ldap_server_name);
2412 ads->config.ldap_server_name = strdup(p+1);
2413 p = strchr(ads->config.ldap_server_name, '$');
2414 if (!p || p[1] != '@') {
2415 talloc_destroy(ctx);
2416 DEBUG(1, ("ads_server_info: returned ldap server name (%s) does not contain '$@'"
2417 " so was deemed invalid\n", ads->config.ldap_server_name));
2418 SAFE_FREE(ads->config.ldap_server_name);
2419 return ADS_ERROR(LDAP_DECODING_ERROR);
2422 *p = 0;
2424 SAFE_FREE(ads->config.realm);
2425 SAFE_FREE(ads->config.bind_path);
2427 ads->config.realm = strdup(p+2);
2428 ads->config.bind_path = ads_build_dn(ads->config.realm);
2430 DEBUG(3,("got ldap server name %s@%s, using bind path: %s\n",
2431 ads->config.ldap_server_name, ads->config.realm,
2432 ads->config.bind_path));
2434 ads->config.current_time = ads_parse_time(timestr);
2436 if (ads->config.current_time != 0) {
2437 ads->auth.time_offset = ads->config.current_time - time(NULL);
2438 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2441 talloc_destroy(ctx);
2443 return ADS_SUCCESS;
2447 * find the domain sid for our domain
2448 * @param ads connection to ads server
2449 * @param sid Pointer to domain sid
2450 * @return status of search
2452 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2454 const char *attrs[] = {"objectSid", NULL};
2455 void *res;
2456 ADS_STATUS rc;
2458 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2459 attrs, &res);
2460 if (!ADS_ERR_OK(rc)) return rc;
2461 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2462 ads_msgfree(ads, res);
2463 return ADS_ERROR_SYSTEM(ENOENT);
2465 ads_msgfree(ads, res);
2467 return ADS_SUCCESS;
2470 /* this is rather complex - we need to find the allternate (netbios) name
2471 for the domain, but there isn't a simple query to do this. Instead
2472 we look for the principle names on the DCs account and find one that has
2473 the right form, then extract the netbios name of the domain from that
2475 NOTE! better method is this:
2477 bin/net -Uadministrator%XXXXX ads search '(&(objectclass=crossref)(dnsroot=VNET3.HOME.SAMBA.ORG))' nETBIOSName
2479 but you need to force the bind path to match the configurationNamingContext from the rootDSE
2482 ADS_STATUS ads_workgroup_name(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **workgroup)
2484 char *expr;
2485 ADS_STATUS rc;
2486 char **principles;
2487 char *prefix;
2488 int prefix_length;
2489 int i;
2490 void *res;
2491 const char *attrs[] = {"servicePrincipalName", NULL};
2492 int num_principals;
2494 (*workgroup) = NULL;
2496 asprintf(&expr, "(&(objectclass=computer)(dnshostname=%s.%s))",
2497 ads->config.ldap_server_name, ads->config.realm);
2498 if (expr == NULL) {
2499 ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2502 rc = ads_search(ads, &res, expr, attrs);
2503 free(expr);
2505 if (!ADS_ERR_OK(rc)) {
2506 return rc;
2509 principles = ads_pull_strings(ads, mem_ctx, res,
2510 "servicePrincipalName", &num_principals);
2512 ads_msgfree(ads, res);
2514 if (!principles) {
2515 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2518 asprintf(&prefix, "HOST/%s.%s/",
2519 ads->config.ldap_server_name,
2520 ads->config.realm);
2522 prefix_length = strlen(prefix);
2524 for (i=0;principles[i]; i++) {
2525 if (strnequal(principles[i], prefix, prefix_length) &&
2526 !strequal(ads->config.realm, principles[i]+prefix_length) &&
2527 !strchr(principles[i]+prefix_length, '.')) {
2528 /* found an alternate (short) name for the domain. */
2529 DEBUG(3,("Found alternate name '%s' for realm '%s'\n",
2530 principles[i]+prefix_length,
2531 ads->config.realm));
2532 (*workgroup) = talloc_strdup(mem_ctx, principles[i]+prefix_length);
2533 break;
2536 free(prefix);
2538 if (!*workgroup) {
2539 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2542 return ADS_SUCCESS;
2545 #endif