test: not only pretend to call smbconftort - really do it :-}
[Samba.git] / source / libads / ldap.c
bloba9eff48b3ea40895788e2ba42dfb64e8a81e6e6d
1 /*
2 Unix SMB/CIFS implementation.
3 ads (active directory) utility library
4 Copyright (C) Andrew Tridgell 2001
5 Copyright (C) Remus Koos 2001
6 Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002
7 Copyright (C) Guenther Deschner 2005
8 Copyright (C) Gerald Carter 2006
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "includes.h"
25 #include "lib/ldb/include/includes.h"
27 #ifdef HAVE_LDAP
29 /**
30 * @file ldap.c
31 * @brief basic ldap client-side routines for ads server communications
33 * The routines contained here should do the necessary ldap calls for
34 * ads setups.
36 * Important note: attribute names passed into ads_ routines must
37 * already be in UTF-8 format. We do not convert them because in almost
38 * all cases, they are just ascii (which is represented with the same
39 * codepoints in UTF-8). This may have to change at some point
40 **/
43 #define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"
45 static SIG_ATOMIC_T gotalarm;
47 /***************************************************************
48 Signal function to tell us we timed out.
49 ****************************************************************/
51 static void gotalarm_sig(void)
53 gotalarm = 1;
56 LDAP *ldap_open_with_timeout(const char *server, int port, unsigned int to)
58 LDAP *ldp = NULL;
61 DEBUG(10, ("Opening connection to LDAP server '%s:%d', timeout "
62 "%u seconds\n", server, port, to));
64 /* Setup timeout */
65 gotalarm = 0;
66 CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
67 alarm(to);
68 /* End setup timeout. */
70 ldp = ldap_open(server, port);
72 if (ldp == NULL) {
73 DEBUG(2,("Could not open connection to LDAP server %s:%d: %s\n",
74 server, port, strerror(errno)));
75 } else {
76 DEBUG(10, ("Connected to LDAP server '%s:%d'\n", server, port));
79 /* Teardown timeout. */
80 CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
81 alarm(0);
83 return ldp;
86 static int ldap_search_with_timeout(LDAP *ld,
87 LDAP_CONST char *base,
88 int scope,
89 LDAP_CONST char *filter,
90 char **attrs,
91 int attrsonly,
92 LDAPControl **sctrls,
93 LDAPControl **cctrls,
94 int sizelimit,
95 LDAPMessage **res )
97 struct timeval timeout;
98 int result;
100 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
101 timeout.tv_sec = lp_ldap_timeout();
102 timeout.tv_usec = 0;
104 /* Setup alarm timeout.... Do we need both of these ? JRA. */
105 gotalarm = 0;
106 CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
107 alarm(lp_ldap_timeout());
108 /* End setup timeout. */
110 result = ldap_search_ext_s(ld, base, scope, filter, attrs,
111 attrsonly, sctrls, cctrls, &timeout,
112 sizelimit, res);
114 /* Teardown timeout. */
115 CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
116 alarm(0);
118 if (gotalarm != 0)
119 return LDAP_TIMELIMIT_EXCEEDED;
121 return result;
124 /**********************************************
125 Do client and server sitename match ?
126 **********************************************/
128 bool ads_sitename_match(ADS_STRUCT *ads)
130 if (ads->config.server_site_name == NULL &&
131 ads->config.client_site_name == NULL ) {
132 DEBUG(10,("ads_sitename_match: both null\n"));
133 return True;
135 if (ads->config.server_site_name &&
136 ads->config.client_site_name &&
137 strequal(ads->config.server_site_name,
138 ads->config.client_site_name)) {
139 DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
140 return True;
142 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
143 ads->config.server_site_name ? ads->config.server_site_name : "NULL",
144 ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
145 return False;
148 /**********************************************
149 Is this the closest DC ?
150 **********************************************/
152 bool ads_closest_dc(ADS_STRUCT *ads)
154 if (ads->config.flags & ADS_CLOSEST) {
155 DEBUG(10,("ads_closest_dc: ADS_CLOSEST flag set\n"));
156 return True;
159 /* not sure if this can ever happen */
160 if (ads_sitename_match(ads)) {
161 DEBUG(10,("ads_closest_dc: ADS_CLOSEST flag not set but sites match\n"));
162 return True;
165 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
166 ads->config.ldap_server_name));
168 return False;
173 try a connection to a given ldap server, returning True and setting the servers IP
174 in the ads struct if successful
176 bool ads_try_connect(ADS_STRUCT *ads, const char *server )
178 char *srv;
179 struct cldap_netlogon_reply cldap_reply;
181 if (!server || !*server) {
182 return False;
185 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
186 server, ads->server.realm));
188 /* this copes with inet_ntoa brokenness */
190 srv = SMB_STRDUP(server);
192 ZERO_STRUCT( cldap_reply );
194 if ( !ads_cldap_netlogon( srv, ads->server.realm, &cldap_reply ) ) {
195 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", srv));
196 SAFE_FREE( srv );
197 return False;
200 /* Check the CLDAP reply flags */
202 if ( !(cldap_reply.flags & ADS_LDAP) ) {
203 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
204 srv));
205 SAFE_FREE( srv );
206 return False;
209 /* Fill in the ads->config values */
211 SAFE_FREE(ads->config.realm);
212 SAFE_FREE(ads->config.bind_path);
213 SAFE_FREE(ads->config.ldap_server_name);
214 SAFE_FREE(ads->config.server_site_name);
215 SAFE_FREE(ads->config.client_site_name);
216 SAFE_FREE(ads->server.workgroup);
218 ads->config.flags = cldap_reply.flags;
219 ads->config.ldap_server_name = SMB_STRDUP(cldap_reply.hostname);
220 strupper_m(cldap_reply.domain);
221 ads->config.realm = SMB_STRDUP(cldap_reply.domain);
222 ads->config.bind_path = ads_build_dn(ads->config.realm);
223 if (*cldap_reply.server_site_name) {
224 ads->config.server_site_name =
225 SMB_STRDUP(cldap_reply.server_site_name);
227 if (*cldap_reply.client_site_name) {
228 ads->config.client_site_name =
229 SMB_STRDUP(cldap_reply.client_site_name);
231 ads->server.workgroup = SMB_STRDUP(cldap_reply.netbios_domain);
233 ads->ldap.port = LDAP_PORT;
234 if (!interpret_string_addr(&ads->ldap.ss, srv, 0)) {
235 DEBUG(1,("ads_try_connect: unable to convert %s "
236 "to an address\n",
237 srv));
238 SAFE_FREE( srv );
239 return False;
242 SAFE_FREE(srv);
244 /* Store our site name. */
245 sitename_store( cldap_reply.domain, cldap_reply.client_site_name );
247 return True;
250 /**********************************************************************
251 Try to find an AD dc using our internal name resolution routines
252 Try the realm first and then then workgroup name if netbios is not
253 disabled
254 **********************************************************************/
256 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
258 const char *c_realm;
259 int count, i=0;
260 struct ip_service *ip_list;
261 const char *realm;
262 bool got_realm = False;
263 bool use_own_domain = False;
264 char *sitename;
265 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
267 /* if the realm and workgroup are both empty, assume they are ours */
269 /* realm */
270 c_realm = ads->server.realm;
272 if ( !c_realm || !*c_realm ) {
273 /* special case where no realm and no workgroup means our own */
274 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
275 use_own_domain = True;
276 c_realm = lp_realm();
280 if (c_realm && *c_realm)
281 got_realm = True;
283 /* we need to try once with the realm name and fallback to the
284 netbios domain name if we fail (if netbios has not been disabled */
286 if ( !got_realm && !lp_disable_netbios() ) {
287 c_realm = ads->server.workgroup;
288 if (!c_realm || !*c_realm) {
289 if ( use_own_domain )
290 c_realm = lp_workgroup();
293 if ( !c_realm || !*c_realm ) {
294 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
295 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
299 realm = c_realm;
301 sitename = sitename_fetch(realm);
303 again:
305 DEBUG(6,("ads_find_dc: looking for %s '%s'\n",
306 (got_realm ? "realm" : "domain"), realm));
308 status = get_sorted_dc_list(realm, sitename, &ip_list, &count, got_realm);
309 if (!NT_STATUS_IS_OK(status)) {
310 /* fall back to netbios if we can */
311 if ( got_realm && !lp_disable_netbios() ) {
312 got_realm = False;
313 goto again;
316 SAFE_FREE(sitename);
317 return status;
320 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
321 for ( i=0; i<count; i++ ) {
322 char server[INET6_ADDRSTRLEN];
324 print_sockaddr(server, sizeof(server), &ip_list[i].ss);
326 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
327 continue;
329 if (!got_realm) {
330 /* realm in this case is a workgroup name. We need
331 to ignore any IP addresses in the negative connection
332 cache that match ip addresses returned in the ad realm
333 case. It sucks that I have to reproduce the logic above... */
334 c_realm = ads->server.realm;
335 if ( !c_realm || !*c_realm ) {
336 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
337 c_realm = lp_realm();
340 if (c_realm && *c_realm &&
341 !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm, server))) {
342 /* Ensure we add the workgroup name for this
343 IP address as negative too. */
344 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
345 continue;
349 if ( ads_try_connect(ads, server) ) {
350 SAFE_FREE(ip_list);
351 SAFE_FREE(sitename);
352 return NT_STATUS_OK;
355 /* keep track of failures */
356 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
359 SAFE_FREE(ip_list);
361 /* In case we failed to contact one of our closest DC on our site we
362 * need to try to find another DC, retry with a site-less SRV DNS query
363 * - Guenther */
365 if (sitename) {
366 DEBUG(1,("ads_find_dc: failed to find a valid DC on our site (%s), "
367 "trying to find another DC\n", sitename));
368 SAFE_FREE(sitename);
369 namecache_delete(realm, 0x1C);
370 goto again;
373 return NT_STATUS_NO_LOGON_SERVERS;
378 * Connect to the LDAP server
379 * @param ads Pointer to an existing ADS_STRUCT
380 * @return status of connection
382 ADS_STATUS ads_connect(ADS_STRUCT *ads)
384 int version = LDAP_VERSION3;
385 ADS_STATUS status;
386 NTSTATUS ntstatus;
387 char addr[INET6_ADDRSTRLEN];
389 ZERO_STRUCT(ads->ldap);
390 ads->ldap.last_attempt = time(NULL);
391 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
393 /* try with a user specified server */
395 if (DEBUGLEVEL >= 11) {
396 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
397 DEBUG(11,("ads_connect: entering\n"));
398 DEBUGADD(11,("%s\n", s));
399 TALLOC_FREE(s);
402 if (ads->server.ldap_server &&
403 ads_try_connect(ads, ads->server.ldap_server)) {
404 goto got_connection;
407 ntstatus = ads_find_dc(ads);
408 if (NT_STATUS_IS_OK(ntstatus)) {
409 goto got_connection;
412 status = ADS_ERROR_NT(ntstatus);
413 goto out;
415 got_connection:
417 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
418 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
420 if (!ads->auth.user_name) {
421 /* Must use the userPrincipalName value here or sAMAccountName
422 and not servicePrincipalName; found by Guenther Deschner */
424 asprintf(&ads->auth.user_name, "%s$", global_myname() );
427 if (!ads->auth.realm) {
428 ads->auth.realm = SMB_STRDUP(ads->config.realm);
431 if (!ads->auth.kdc_server) {
432 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
433 ads->auth.kdc_server = SMB_STRDUP(addr);
436 #if KRB5_DNS_HACK
437 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
438 to MIT kerberos to work (tridge) */
440 char *env;
441 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
442 setenv(env, ads->auth.kdc_server, 1);
443 free(env);
445 #endif
447 /* If the caller() requested no LDAP bind, then we are done */
449 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
450 status = ADS_SUCCESS;
451 goto out;
454 ads->ldap.mem_ctx = talloc_init("ads LDAP connection memory");
455 if (!ads->ldap.mem_ctx) {
456 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
457 goto out;
460 /* Otherwise setup the TCP LDAP session */
462 ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name,
463 LDAP_PORT, lp_ldap_timeout());
464 if (ads->ldap.ld == NULL) {
465 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
466 goto out;
468 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
470 /* cache the successful connection for workgroup and realm */
471 if (ads_closest_dc(ads)) {
472 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
473 saf_store( ads->server.workgroup, addr);
474 saf_store( ads->server.realm, addr);
477 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
479 status = ADS_ERROR(smb_ldap_start_tls(ads->ldap.ld, version));
480 if (!ADS_ERR_OK(status)) {
481 goto out;
484 /* fill in the current time and offsets */
486 status = ads_current_time( ads );
487 if ( !ADS_ERR_OK(status) ) {
488 goto out;
491 /* Now do the bind */
493 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
494 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
495 goto out;
498 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
499 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
500 goto out;
503 status = ads_sasl_bind(ads);
505 out:
506 if (DEBUGLEVEL >= 11) {
507 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
508 DEBUG(11,("ads_connect: leaving with: %s\n",
509 ads_errstr(status)));
510 DEBUGADD(11,("%s\n", s));
511 TALLOC_FREE(s);
514 return status;
518 * Disconnect the LDAP server
519 * @param ads Pointer to an existing ADS_STRUCT
521 void ads_disconnect(ADS_STRUCT *ads)
523 if (ads->ldap.ld) {
524 ldap_unbind(ads->ldap.ld);
525 ads->ldap.ld = NULL;
527 if (ads->ldap.wrap_ops && ads->ldap.wrap_ops->disconnect) {
528 ads->ldap.wrap_ops->disconnect(ads);
530 if (ads->ldap.mem_ctx) {
531 talloc_free(ads->ldap.mem_ctx);
533 ZERO_STRUCT(ads->ldap);
537 Duplicate a struct berval into talloc'ed memory
539 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
541 struct berval *value;
543 if (!in_val) return NULL;
545 value = TALLOC_ZERO_P(ctx, struct berval);
546 if (value == NULL)
547 return NULL;
548 if (in_val->bv_len == 0) return value;
550 value->bv_len = in_val->bv_len;
551 value->bv_val = (char *)TALLOC_MEMDUP(ctx, in_val->bv_val,
552 in_val->bv_len);
553 return value;
557 Make a values list out of an array of (struct berval *)
559 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
560 const struct berval **in_vals)
562 struct berval **values;
563 int i;
565 if (!in_vals) return NULL;
566 for (i=0; in_vals[i]; i++)
567 ; /* count values */
568 values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
569 if (!values) return NULL;
571 for (i=0; in_vals[i]; i++) {
572 values[i] = dup_berval(ctx, in_vals[i]);
574 return values;
578 UTF8-encode a values list out of an array of (char *)
580 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
582 char **values;
583 int i;
585 if (!in_vals) return NULL;
586 for (i=0; in_vals[i]; i++)
587 ; /* count values */
588 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
589 if (!values) return NULL;
591 for (i=0; in_vals[i]; i++) {
592 if (push_utf8_talloc(ctx, &values[i], in_vals[i]) == (size_t) -1) {
593 TALLOC_FREE(values);
594 return NULL;
597 return values;
601 Pull a (char *) array out of a UTF8-encoded values list
603 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
605 char **values;
606 int i;
608 if (!in_vals) return NULL;
609 for (i=0; in_vals[i]; i++)
610 ; /* count values */
611 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
612 if (!values) return NULL;
614 for (i=0; in_vals[i]; i++) {
615 pull_utf8_talloc(ctx, &values[i], in_vals[i]);
617 return values;
621 * Do a search with paged results. cookie must be null on the first
622 * call, and then returned on each subsequent call. It will be null
623 * again when the entire search is complete
624 * @param ads connection to ads server
625 * @param bind_path Base dn for the search
626 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
627 * @param expr Search expression - specified in local charset
628 * @param attrs Attributes to retrieve - specified in utf8 or ascii
629 * @param res ** which will contain results - free res* with ads_msgfree()
630 * @param count Number of entries retrieved on this page
631 * @param cookie The paged results cookie to be returned on subsequent calls
632 * @return status of search
634 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
635 const char *bind_path,
636 int scope, const char *expr,
637 const char **attrs, void *args,
638 LDAPMessage **res,
639 int *count, struct berval **cookie)
641 int rc, i, version;
642 char *utf8_expr, *utf8_path, **search_attrs;
643 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
644 BerElement *cookie_be = NULL;
645 struct berval *cookie_bv= NULL;
646 BerElement *ext_be = NULL;
647 struct berval *ext_bv= NULL;
649 TALLOC_CTX *ctx;
650 ads_control *external_control = (ads_control *) args;
652 *res = NULL;
654 if (!(ctx = talloc_init("ads_do_paged_search_args")))
655 return ADS_ERROR(LDAP_NO_MEMORY);
657 /* 0 means the conversion worked but the result was empty
658 so we only fail if it's -1. In any case, it always
659 at least nulls out the dest */
660 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
661 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
662 rc = LDAP_NO_MEMORY;
663 goto done;
666 if (!attrs || !(*attrs))
667 search_attrs = NULL;
668 else {
669 /* This would be the utf8-encoded version...*/
670 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
671 if (!(str_list_copy(talloc_tos(), &search_attrs, attrs))) {
672 rc = LDAP_NO_MEMORY;
673 goto done;
677 /* Paged results only available on ldap v3 or later */
678 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
679 if (version < LDAP_VERSION3) {
680 rc = LDAP_NOT_SUPPORTED;
681 goto done;
684 cookie_be = ber_alloc_t(LBER_USE_DER);
685 if (*cookie) {
686 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
687 ber_bvfree(*cookie); /* don't need it from last time */
688 *cookie = NULL;
689 } else {
690 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
692 ber_flatten(cookie_be, &cookie_bv);
693 PagedResults.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID);
694 PagedResults.ldctl_iscritical = (char) 1;
695 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
696 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
698 NoReferrals.ldctl_oid = CONST_DISCARD(char *, ADS_NO_REFERRALS_OID);
699 NoReferrals.ldctl_iscritical = (char) 0;
700 NoReferrals.ldctl_value.bv_len = 0;
701 NoReferrals.ldctl_value.bv_val = CONST_DISCARD(char *, "");
703 if (external_control &&
704 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
705 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
707 ExternalCtrl.ldctl_oid = CONST_DISCARD(char *, external_control->control);
708 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
710 /* win2k does not accept a ldctl_value beeing passed in */
712 if (external_control->val != 0) {
714 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
715 rc = LDAP_NO_MEMORY;
716 goto done;
719 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
720 rc = LDAP_NO_MEMORY;
721 goto done;
723 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
724 rc = LDAP_NO_MEMORY;
725 goto done;
728 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
729 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
731 } else {
732 ExternalCtrl.ldctl_value.bv_len = 0;
733 ExternalCtrl.ldctl_value.bv_val = NULL;
736 controls[0] = &NoReferrals;
737 controls[1] = &PagedResults;
738 controls[2] = &ExternalCtrl;
739 controls[3] = NULL;
741 } else {
742 controls[0] = &NoReferrals;
743 controls[1] = &PagedResults;
744 controls[2] = NULL;
747 /* we need to disable referrals as the openldap libs don't
748 handle them and paged results at the same time. Using them
749 together results in the result record containing the server
750 page control being removed from the result list (tridge/jmcd)
752 leaving this in despite the control that says don't generate
753 referrals, in case the server doesn't support it (jmcd)
755 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
757 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
758 search_attrs, 0, controls,
759 NULL, LDAP_NO_LIMIT,
760 (LDAPMessage **)res);
762 ber_free(cookie_be, 1);
763 ber_bvfree(cookie_bv);
765 if (rc) {
766 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
767 ldap_err2string(rc)));
768 goto done;
771 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
772 NULL, &rcontrols, 0);
774 if (!rcontrols) {
775 goto done;
778 for (i=0; rcontrols[i]; i++) {
779 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
780 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
781 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
782 &cookie_bv);
783 /* the berval is the cookie, but must be freed when
784 it is all done */
785 if (cookie_bv->bv_len) /* still more to do */
786 *cookie=ber_bvdup(cookie_bv);
787 else
788 *cookie=NULL;
789 ber_bvfree(cookie_bv);
790 ber_free(cookie_be, 1);
791 break;
794 ldap_controls_free(rcontrols);
796 done:
797 talloc_destroy(ctx);
799 if (ext_be) {
800 ber_free(ext_be, 1);
803 if (ext_bv) {
804 ber_bvfree(ext_bv);
807 /* if/when we decide to utf8-encode attrs, take out this next line */
808 TALLOC_FREE(search_attrs);
810 return ADS_ERROR(rc);
813 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
814 int scope, const char *expr,
815 const char **attrs, LDAPMessage **res,
816 int *count, struct berval **cookie)
818 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
823 * Get all results for a search. This uses ads_do_paged_search() to return
824 * all entries in a large search.
825 * @param ads connection to ads server
826 * @param bind_path Base dn for the search
827 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
828 * @param expr Search expression
829 * @param attrs Attributes to retrieve
830 * @param res ** which will contain results - free res* with ads_msgfree()
831 * @return status of search
833 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
834 int scope, const char *expr,
835 const char **attrs, void *args,
836 LDAPMessage **res)
838 struct berval *cookie = NULL;
839 int count = 0;
840 ADS_STATUS status;
842 *res = NULL;
843 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
844 &count, &cookie);
846 if (!ADS_ERR_OK(status))
847 return status;
849 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
850 while (cookie) {
851 LDAPMessage *res2 = NULL;
852 ADS_STATUS status2;
853 LDAPMessage *msg, *next;
855 status2 = ads_do_paged_search_args(ads, bind_path, scope, expr,
856 attrs, args, &res2, &count, &cookie);
858 if (!ADS_ERR_OK(status2)) break;
860 /* this relies on the way that ldap_add_result_entry() works internally. I hope
861 that this works on all ldap libs, but I have only tested with openldap */
862 for (msg = ads_first_entry(ads, res2); msg; msg = next) {
863 next = ads_next_entry(ads, msg);
864 ldap_add_result_entry((LDAPMessage **)res, msg);
866 /* note that we do not free res2, as the memory is now
867 part of the main returned list */
869 #else
870 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
871 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
872 #endif
874 return status;
877 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
878 int scope, const char *expr,
879 const char **attrs, LDAPMessage **res)
881 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
884 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
885 int scope, const char *expr,
886 const char **attrs, uint32 sd_flags,
887 LDAPMessage **res)
889 ads_control args;
891 args.control = ADS_SD_FLAGS_OID;
892 args.val = sd_flags;
893 args.critical = True;
895 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
900 * Run a function on all results for a search. Uses ads_do_paged_search() and
901 * runs the function as each page is returned, using ads_process_results()
902 * @param ads connection to ads server
903 * @param bind_path Base dn for the search
904 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
905 * @param expr Search expression - specified in local charset
906 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
907 * @param fn Function which takes attr name, values list, and data_area
908 * @param data_area Pointer which is passed to function on each call
909 * @return status of search
911 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
912 int scope, const char *expr, const char **attrs,
913 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
914 void *data_area)
916 struct berval *cookie = NULL;
917 int count = 0;
918 ADS_STATUS status;
919 LDAPMessage *res;
921 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
922 &count, &cookie);
924 if (!ADS_ERR_OK(status)) return status;
926 ads_process_results(ads, res, fn, data_area);
927 ads_msgfree(ads, res);
929 while (cookie) {
930 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
931 &res, &count, &cookie);
933 if (!ADS_ERR_OK(status)) break;
935 ads_process_results(ads, res, fn, data_area);
936 ads_msgfree(ads, res);
939 return status;
943 * Do a search with a timeout.
944 * @param ads connection to ads server
945 * @param bind_path Base dn for the search
946 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
947 * @param expr Search expression
948 * @param attrs Attributes to retrieve
949 * @param res ** which will contain results - free res* with ads_msgfree()
950 * @return status of search
952 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
953 const char *expr,
954 const char **attrs, LDAPMessage **res)
956 int rc;
957 char *utf8_expr, *utf8_path, **search_attrs = NULL;
958 TALLOC_CTX *ctx;
960 *res = NULL;
961 if (!(ctx = talloc_init("ads_do_search"))) {
962 DEBUG(1,("ads_do_search: talloc_init() failed!"));
963 return ADS_ERROR(LDAP_NO_MEMORY);
966 /* 0 means the conversion worked but the result was empty
967 so we only fail if it's negative. In any case, it always
968 at least nulls out the dest */
969 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
970 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
971 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
972 rc = LDAP_NO_MEMORY;
973 goto done;
976 if (!attrs || !(*attrs))
977 search_attrs = NULL;
978 else {
979 /* This would be the utf8-encoded version...*/
980 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
981 if (!(str_list_copy(talloc_tos(), &search_attrs, attrs)))
983 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
984 rc = LDAP_NO_MEMORY;
985 goto done;
989 /* see the note in ads_do_paged_search - we *must* disable referrals */
990 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
992 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
993 search_attrs, 0, NULL, NULL,
994 LDAP_NO_LIMIT,
995 (LDAPMessage **)res);
997 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
998 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
999 rc = 0;
1002 done:
1003 talloc_destroy(ctx);
1004 /* if/when we decide to utf8-encode attrs, take out this next line */
1005 TALLOC_FREE(search_attrs);
1006 return ADS_ERROR(rc);
1009 * Do a general ADS search
1010 * @param ads connection to ads server
1011 * @param res ** which will contain results - free res* with ads_msgfree()
1012 * @param expr Search expression
1013 * @param attrs Attributes to retrieve
1014 * @return status of search
1016 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1017 const char *expr, const char **attrs)
1019 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1020 expr, attrs, res);
1024 * Do a search on a specific DistinguishedName
1025 * @param ads connection to ads server
1026 * @param res ** which will contain results - free res* with ads_msgfree()
1027 * @param dn DistinguishName to search
1028 * @param attrs Attributes to retrieve
1029 * @return status of search
1031 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1032 const char *dn, const char **attrs)
1034 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1035 attrs, res);
1039 * Free up memory from a ads_search
1040 * @param ads connection to ads server
1041 * @param msg Search results to free
1043 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1045 if (!msg) return;
1046 ldap_msgfree(msg);
1050 * Free up memory from various ads requests
1051 * @param ads connection to ads server
1052 * @param mem Area to free
1054 void ads_memfree(ADS_STRUCT *ads, void *mem)
1056 SAFE_FREE(mem);
1060 * Get a dn from search results
1061 * @param ads connection to ads server
1062 * @param msg Search result
1063 * @return dn string
1065 char *ads_get_dn(ADS_STRUCT *ads, LDAPMessage *msg)
1067 char *utf8_dn, *unix_dn;
1069 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1071 if (!utf8_dn) {
1072 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1073 return NULL;
1076 if (pull_utf8_allocate(&unix_dn, utf8_dn) == (size_t)-1) {
1077 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1078 utf8_dn ));
1079 return NULL;
1081 ldap_memfree(utf8_dn);
1082 return unix_dn;
1086 * Get the parent from a dn
1087 * @param dn the dn to return the parent from
1088 * @return parent dn string
1090 char *ads_parent_dn(const char *dn)
1092 char *p;
1094 if (dn == NULL) {
1095 return NULL;
1098 p = strchr(dn, ',');
1100 if (p == NULL) {
1101 return NULL;
1104 return p+1;
1108 * Find a machine account given a hostname
1109 * @param ads connection to ads server
1110 * @param res ** which will contain results - free res* with ads_msgfree()
1111 * @param host Hostname to search for
1112 * @return status of search
1114 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1115 const char *machine)
1117 ADS_STATUS status;
1118 char *expr;
1119 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1121 *res = NULL;
1123 /* the easiest way to find a machine account anywhere in the tree
1124 is to look for hostname$ */
1125 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1126 DEBUG(1, ("asprintf failed!\n"));
1127 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1130 status = ads_search(ads, res, expr, attrs);
1131 SAFE_FREE(expr);
1132 return status;
1136 * Initialize a list of mods to be used in a modify request
1137 * @param ctx An initialized TALLOC_CTX
1138 * @return allocated ADS_MODLIST
1140 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1142 #define ADS_MODLIST_ALLOC_SIZE 10
1143 LDAPMod **mods;
1145 if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1146 /* -1 is safety to make sure we don't go over the end.
1147 need to reset it to NULL before doing ldap modify */
1148 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1150 return (ADS_MODLIST)mods;
1155 add an attribute to the list, with values list already constructed
1157 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1158 int mod_op, const char *name,
1159 const void *_invals)
1161 const void **invals = (const void **)_invals;
1162 int curmod;
1163 LDAPMod **modlist = (LDAPMod **) *mods;
1164 struct berval **ber_values = NULL;
1165 char **char_values = NULL;
1167 if (!invals) {
1168 mod_op = LDAP_MOD_DELETE;
1169 } else {
1170 if (mod_op & LDAP_MOD_BVALUES)
1171 ber_values = ads_dup_values(ctx,
1172 (const struct berval **)invals);
1173 else
1174 char_values = ads_push_strvals(ctx,
1175 (const char **) invals);
1178 /* find the first empty slot */
1179 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1180 curmod++);
1181 if (modlist[curmod] == (LDAPMod *) -1) {
1182 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
1183 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1184 return ADS_ERROR(LDAP_NO_MEMORY);
1185 memset(&modlist[curmod], 0,
1186 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1187 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1188 *mods = (ADS_MODLIST)modlist;
1191 if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
1192 return ADS_ERROR(LDAP_NO_MEMORY);
1193 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1194 if (mod_op & LDAP_MOD_BVALUES) {
1195 modlist[curmod]->mod_bvalues = ber_values;
1196 } else if (mod_op & LDAP_MOD_DELETE) {
1197 modlist[curmod]->mod_values = NULL;
1198 } else {
1199 modlist[curmod]->mod_values = char_values;
1202 modlist[curmod]->mod_op = mod_op;
1203 return ADS_ERROR(LDAP_SUCCESS);
1207 * Add a single string value to a mod list
1208 * @param ctx An initialized TALLOC_CTX
1209 * @param mods An initialized ADS_MODLIST
1210 * @param name The attribute name to add
1211 * @param val The value to add - NULL means DELETE
1212 * @return ADS STATUS indicating success of add
1214 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1215 const char *name, const char *val)
1217 const char *values[2];
1219 values[0] = val;
1220 values[1] = NULL;
1222 if (!val)
1223 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1224 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1228 * Add an array of string values to a mod list
1229 * @param ctx An initialized TALLOC_CTX
1230 * @param mods An initialized ADS_MODLIST
1231 * @param name The attribute name to add
1232 * @param vals The array of string values to add - NULL means DELETE
1233 * @return ADS STATUS indicating success of add
1235 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1236 const char *name, const char **vals)
1238 if (!vals)
1239 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1240 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1241 name, (const void **) vals);
1244 #if 0
1246 * Add a single ber-encoded value to a mod list
1247 * @param ctx An initialized TALLOC_CTX
1248 * @param mods An initialized ADS_MODLIST
1249 * @param name The attribute name to add
1250 * @param val The value to add - NULL means DELETE
1251 * @return ADS STATUS indicating success of add
1253 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1254 const char *name, const struct berval *val)
1256 const struct berval *values[2];
1258 values[0] = val;
1259 values[1] = NULL;
1260 if (!val)
1261 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1262 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1263 name, (const void **) values);
1265 #endif
1268 * Perform an ldap modify
1269 * @param ads connection to ads server
1270 * @param mod_dn DistinguishedName to modify
1271 * @param mods list of modifications to perform
1272 * @return status of modify
1274 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1276 int ret,i;
1277 char *utf8_dn = NULL;
1279 this control is needed to modify that contains a currently
1280 non-existent attribute (but allowable for the object) to run
1282 LDAPControl PermitModify = {
1283 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
1284 {0, NULL},
1285 (char) 1};
1286 LDAPControl *controls[2];
1288 controls[0] = &PermitModify;
1289 controls[1] = NULL;
1291 if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) {
1292 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1295 /* find the end of the list, marked by NULL or -1 */
1296 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1297 /* make sure the end of the list is NULL */
1298 mods[i] = NULL;
1299 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1300 (LDAPMod **) mods, controls, NULL);
1301 SAFE_FREE(utf8_dn);
1302 return ADS_ERROR(ret);
1306 * Perform an ldap add
1307 * @param ads connection to ads server
1308 * @param new_dn DistinguishedName to add
1309 * @param mods list of attributes and values for DN
1310 * @return status of add
1312 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1314 int ret, i;
1315 char *utf8_dn = NULL;
1317 if (push_utf8_allocate(&utf8_dn, new_dn) == -1) {
1318 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1319 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1322 /* find the end of the list, marked by NULL or -1 */
1323 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1324 /* make sure the end of the list is NULL */
1325 mods[i] = NULL;
1327 ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1328 SAFE_FREE(utf8_dn);
1329 return ADS_ERROR(ret);
1333 * Delete a DistinguishedName
1334 * @param ads connection to ads server
1335 * @param new_dn DistinguishedName to delete
1336 * @return status of delete
1338 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1340 int ret;
1341 char *utf8_dn = NULL;
1342 if (push_utf8_allocate(&utf8_dn, del_dn) == -1) {
1343 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1344 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1347 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1348 SAFE_FREE(utf8_dn);
1349 return ADS_ERROR(ret);
1353 * Build an org unit string
1354 * if org unit is Computers or blank then assume a container, otherwise
1355 * assume a / separated list of organisational units.
1356 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1357 * @param ads connection to ads server
1358 * @param org_unit Organizational unit
1359 * @return org unit string - caller must free
1361 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1363 char *ret = NULL;
1365 if (!org_unit || !*org_unit) {
1367 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1369 /* samba4 might not yet respond to a wellknownobject-query */
1370 return ret ? ret : SMB_STRDUP("cn=Computers");
1373 if (strequal(org_unit, "Computers")) {
1374 return SMB_STRDUP("cn=Computers");
1377 /* jmcd: removed "\\" from the separation chars, because it is
1378 needed as an escape for chars like '#' which are valid in an
1379 OU name */
1380 return ads_build_path(org_unit, "/", "ou=", 1);
1384 * Get a org unit string for a well-known GUID
1385 * @param ads connection to ads server
1386 * @param wknguid Well known GUID
1387 * @return org unit string - caller must free
1389 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1391 ADS_STATUS status;
1392 LDAPMessage *res = NULL;
1393 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1394 **bind_dn_exp = NULL;
1395 const char *attrs[] = {"distinguishedName", NULL};
1396 int new_ln, wkn_ln, bind_ln, i;
1398 if (wknguid == NULL) {
1399 return NULL;
1402 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1403 DEBUG(1, ("asprintf failed!\n"));
1404 return NULL;
1407 status = ads_search_dn(ads, &res, base, attrs);
1408 if (!ADS_ERR_OK(status)) {
1409 DEBUG(1,("Failed while searching for: %s\n", base));
1410 goto out;
1413 if (ads_count_replies(ads, res) != 1) {
1414 goto out;
1417 /* substitute the bind-path from the well-known-guid-search result */
1418 wkn_dn = ads_get_dn(ads, res);
1419 if (!wkn_dn) {
1420 goto out;
1423 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1424 if (!wkn_dn_exp) {
1425 goto out;
1428 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1429 if (!bind_dn_exp) {
1430 goto out;
1433 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1435 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1438 new_ln = wkn_ln - bind_ln;
1440 ret = SMB_STRDUP(wkn_dn_exp[0]);
1441 if (!ret) {
1442 goto out;
1445 for (i=1; i < new_ln; i++) {
1446 char *s = NULL;
1448 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1449 SAFE_FREE(ret);
1450 goto out;
1453 SAFE_FREE(ret);
1454 ret = SMB_STRDUP(s);
1455 free(s);
1456 if (!ret) {
1457 goto out;
1461 out:
1462 SAFE_FREE(base);
1463 ads_msgfree(ads, res);
1464 ads_memfree(ads, wkn_dn);
1465 if (wkn_dn_exp) {
1466 ldap_value_free(wkn_dn_exp);
1468 if (bind_dn_exp) {
1469 ldap_value_free(bind_dn_exp);
1472 return ret;
1476 * Adds (appends) an item to an attribute array, rather then
1477 * replacing the whole list
1478 * @param ctx An initialized TALLOC_CTX
1479 * @param mods An initialized ADS_MODLIST
1480 * @param name name of the ldap attribute to append to
1481 * @param vals an array of values to add
1482 * @return status of addition
1485 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1486 const char *name, const char **vals)
1488 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1489 (const void *) vals);
1493 * Determines the computer account's current KVNO via an LDAP lookup
1494 * @param ads An initialized ADS_STRUCT
1495 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1496 * @return the kvno for the computer account, or -1 in case of a failure.
1499 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *machine_name)
1501 LDAPMessage *res = NULL;
1502 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1503 char *filter;
1504 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1505 char *dn_string = NULL;
1506 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1508 DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name));
1509 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1510 return kvno;
1512 ret = ads_search(ads, &res, filter, attrs);
1513 SAFE_FREE(filter);
1514 if (!ADS_ERR_OK(ret) && ads_count_replies(ads, res)) {
1515 DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name));
1516 ads_msgfree(ads, res);
1517 return kvno;
1520 dn_string = ads_get_dn(ads, res);
1521 if (!dn_string) {
1522 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1523 ads_msgfree(ads, res);
1524 return kvno;
1526 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1527 ads_memfree(ads, dn_string);
1529 /* ---------------------------------------------------------
1530 * 0 is returned as a default KVNO from this point on...
1531 * This is done because Windows 2000 does not support key
1532 * version numbers. Chances are that a failure in the next
1533 * step is simply due to Windows 2000 being used for a
1534 * domain controller. */
1535 kvno = 0;
1537 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1538 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1539 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1540 ads_msgfree(ads, res);
1541 return kvno;
1544 /* Success */
1545 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1546 ads_msgfree(ads, res);
1547 return kvno;
1551 * This clears out all registered spn's for a given hostname
1552 * @param ads An initilaized ADS_STRUCT
1553 * @param machine_name the NetBIOS name of the computer.
1554 * @return 0 upon success, non-zero otherwise.
1557 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1559 TALLOC_CTX *ctx;
1560 LDAPMessage *res = NULL;
1561 ADS_MODLIST mods;
1562 const char *servicePrincipalName[1] = {NULL};
1563 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1564 char *dn_string = NULL;
1566 ret = ads_find_machine_acct(ads, &res, machine_name);
1567 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1568 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1569 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1570 ads_msgfree(ads, res);
1571 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1574 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1575 ctx = talloc_init("ads_clear_service_principal_names");
1576 if (!ctx) {
1577 ads_msgfree(ads, res);
1578 return ADS_ERROR(LDAP_NO_MEMORY);
1581 if (!(mods = ads_init_mods(ctx))) {
1582 talloc_destroy(ctx);
1583 ads_msgfree(ads, res);
1584 return ADS_ERROR(LDAP_NO_MEMORY);
1586 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1587 if (!ADS_ERR_OK(ret)) {
1588 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1589 ads_msgfree(ads, res);
1590 talloc_destroy(ctx);
1591 return ret;
1593 dn_string = ads_get_dn(ads, res);
1594 if (!dn_string) {
1595 talloc_destroy(ctx);
1596 ads_msgfree(ads, res);
1597 return ADS_ERROR(LDAP_NO_MEMORY);
1599 ret = ads_gen_mod(ads, dn_string, mods);
1600 ads_memfree(ads,dn_string);
1601 if (!ADS_ERR_OK(ret)) {
1602 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1603 machine_name));
1604 ads_msgfree(ads, res);
1605 talloc_destroy(ctx);
1606 return ret;
1609 ads_msgfree(ads, res);
1610 talloc_destroy(ctx);
1611 return ret;
1615 * This adds a service principal name to an existing computer account
1616 * (found by hostname) in AD.
1617 * @param ads An initialized ADS_STRUCT
1618 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1619 * @param my_fqdn The fully qualified DNS name of the machine
1620 * @param spn A string of the service principal to add, i.e. 'host'
1621 * @return 0 upon sucess, or non-zero if a failure occurs
1624 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1625 const char *my_fqdn, const char *spn)
1627 ADS_STATUS ret;
1628 TALLOC_CTX *ctx;
1629 LDAPMessage *res = NULL;
1630 char *psp1, *psp2;
1631 ADS_MODLIST mods;
1632 char *dn_string = NULL;
1633 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1635 ret = ads_find_machine_acct(ads, &res, machine_name);
1636 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1637 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1638 machine_name));
1639 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1640 spn, machine_name, ads->config.realm));
1641 ads_msgfree(ads, res);
1642 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1645 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1646 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1647 ads_msgfree(ads, res);
1648 return ADS_ERROR(LDAP_NO_MEMORY);
1651 /* add short name spn */
1653 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1654 talloc_destroy(ctx);
1655 ads_msgfree(ads, res);
1656 return ADS_ERROR(LDAP_NO_MEMORY);
1658 strupper_m(psp1);
1659 strlower_m(&psp1[strlen(spn)]);
1660 servicePrincipalName[0] = psp1;
1662 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1663 psp1, machine_name));
1666 /* add fully qualified spn */
1668 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1669 ret = ADS_ERROR(LDAP_NO_MEMORY);
1670 goto out;
1672 strupper_m(psp2);
1673 strlower_m(&psp2[strlen(spn)]);
1674 servicePrincipalName[1] = psp2;
1676 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1677 psp2, machine_name));
1679 if ( (mods = ads_init_mods(ctx)) == NULL ) {
1680 ret = ADS_ERROR(LDAP_NO_MEMORY);
1681 goto out;
1684 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1685 if (!ADS_ERR_OK(ret)) {
1686 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1687 goto out;
1690 if ( (dn_string = ads_get_dn(ads, res)) == NULL ) {
1691 ret = ADS_ERROR(LDAP_NO_MEMORY);
1692 goto out;
1695 ret = ads_gen_mod(ads, dn_string, mods);
1696 ads_memfree(ads,dn_string);
1697 if (!ADS_ERR_OK(ret)) {
1698 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1699 goto out;
1702 out:
1703 TALLOC_FREE( ctx );
1704 ads_msgfree(ads, res);
1705 return ret;
1709 * adds a machine account to the ADS server
1710 * @param ads An intialized ADS_STRUCT
1711 * @param machine_name - the NetBIOS machine name of this account.
1712 * @param account_type A number indicating the type of account to create
1713 * @param org_unit The LDAP path in which to place this account
1714 * @return 0 upon success, or non-zero otherwise
1717 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1718 const char *org_unit)
1720 ADS_STATUS ret;
1721 char *samAccountName, *controlstr;
1722 TALLOC_CTX *ctx;
1723 ADS_MODLIST mods;
1724 char *machine_escaped = NULL;
1725 char *new_dn;
1726 const char *objectClass[] = {"top", "person", "organizationalPerson",
1727 "user", "computer", NULL};
1728 LDAPMessage *res = NULL;
1729 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
1730 UF_DONT_EXPIRE_PASSWD |\
1731 UF_ACCOUNTDISABLE );
1733 if (!(ctx = talloc_init("ads_add_machine_acct")))
1734 return ADS_ERROR(LDAP_NO_MEMORY);
1736 ret = ADS_ERROR(LDAP_NO_MEMORY);
1738 machine_escaped = escape_rdn_val_string_alloc(machine_name);
1739 if (!machine_escaped) {
1740 goto done;
1743 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
1744 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
1746 if ( !new_dn || !samAccountName ) {
1747 goto done;
1750 #ifndef ENCTYPE_ARCFOUR_HMAC
1751 acct_control |= UF_USE_DES_KEY_ONLY;
1752 #endif
1754 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
1755 goto done;
1758 if (!(mods = ads_init_mods(ctx))) {
1759 goto done;
1762 ads_mod_str(ctx, &mods, "cn", machine_name);
1763 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1764 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1765 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1767 ret = ads_gen_add(ads, new_dn, mods);
1769 done:
1770 SAFE_FREE(machine_escaped);
1771 ads_msgfree(ads, res);
1772 talloc_destroy(ctx);
1774 return ret;
1778 * move a machine account to another OU on the ADS server
1779 * @param ads - An intialized ADS_STRUCT
1780 * @param machine_name - the NetBIOS machine name of this account.
1781 * @param org_unit - The LDAP path in which to place this account
1782 * @param moved - whether we moved the machine account (optional)
1783 * @return 0 upon success, or non-zero otherwise
1786 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1787 const char *org_unit, bool *moved)
1789 ADS_STATUS rc;
1790 int ldap_status;
1791 LDAPMessage *res = NULL;
1792 char *filter = NULL;
1793 char *computer_dn = NULL;
1794 char *parent_dn;
1795 char *computer_rdn = NULL;
1796 bool need_move = False;
1798 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1799 rc = ADS_ERROR(LDAP_NO_MEMORY);
1800 goto done;
1803 /* Find pre-existing machine */
1804 rc = ads_search(ads, &res, filter, NULL);
1805 if (!ADS_ERR_OK(rc)) {
1806 goto done;
1809 computer_dn = ads_get_dn(ads, res);
1810 if (!computer_dn) {
1811 rc = ADS_ERROR(LDAP_NO_MEMORY);
1812 goto done;
1815 parent_dn = ads_parent_dn(computer_dn);
1816 if (strequal(parent_dn, org_unit)) {
1817 goto done;
1820 need_move = True;
1822 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
1823 rc = ADS_ERROR(LDAP_NO_MEMORY);
1824 goto done;
1827 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
1828 org_unit, 1, NULL, NULL);
1829 rc = ADS_ERROR(ldap_status);
1831 done:
1832 ads_msgfree(ads, res);
1833 SAFE_FREE(filter);
1834 SAFE_FREE(computer_dn);
1835 SAFE_FREE(computer_rdn);
1837 if (!ADS_ERR_OK(rc)) {
1838 need_move = False;
1841 if (moved) {
1842 *moved = need_move;
1845 return rc;
1849 dump a binary result from ldap
1851 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
1853 int i, j;
1854 for (i=0; values[i]; i++) {
1855 printf("%s: ", field);
1856 for (j=0; j<values[i]->bv_len; j++) {
1857 printf("%02X", (unsigned char)values[i]->bv_val[j]);
1859 printf("\n");
1863 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
1865 int i;
1866 for (i=0; values[i]; i++) {
1868 UUID_FLAT guid;
1869 struct GUID tmp;
1871 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1872 smb_uuid_unpack(guid, &tmp);
1873 printf("%s: %s\n", field, smb_uuid_string(talloc_tos(), tmp));
1878 dump a sid result from ldap
1880 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
1882 int i;
1883 for (i=0; values[i]; i++) {
1884 DOM_SID sid;
1885 fstring tmp;
1886 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1887 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
1892 dump ntSecurityDescriptor
1894 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
1896 TALLOC_CTX *frame = talloc_stackframe();
1897 struct security_descriptor *psd;
1898 NTSTATUS status;
1900 status = unmarshall_sec_desc(talloc_tos(), (uint8 *)values[0]->bv_val,
1901 values[0]->bv_len, &psd);
1902 if (!NT_STATUS_IS_OK(status)) {
1903 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
1904 nt_errstr(status)));
1905 TALLOC_FREE(frame);
1906 return;
1909 if (psd) {
1910 ads_disp_sd(ads, talloc_tos(), psd);
1913 TALLOC_FREE(frame);
1917 dump a string result from ldap
1919 static void dump_string(const char *field, char **values)
1921 int i;
1922 for (i=0; values[i]; i++) {
1923 printf("%s: %s\n", field, values[i]);
1928 dump a field from LDAP on stdout
1929 used for debugging
1932 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
1934 const struct {
1935 const char *name;
1936 bool string;
1937 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
1938 } handlers[] = {
1939 {"objectGUID", False, dump_guid},
1940 {"netbootGUID", False, dump_guid},
1941 {"nTSecurityDescriptor", False, dump_sd},
1942 {"dnsRecord", False, dump_binary},
1943 {"objectSid", False, dump_sid},
1944 {"tokenGroups", False, dump_sid},
1945 {"tokenGroupsNoGCAcceptable", False, dump_sid},
1946 {"tokengroupsGlobalandUniversal", False, dump_sid},
1947 {"mS-DS-CreatorSID", False, dump_sid},
1948 {"msExchMailboxGuid", False, dump_guid},
1949 {NULL, True, NULL}
1951 int i;
1953 if (!field) { /* must be end of an entry */
1954 printf("\n");
1955 return False;
1958 for (i=0; handlers[i].name; i++) {
1959 if (StrCaseCmp(handlers[i].name, field) == 0) {
1960 if (!values) /* first time, indicate string or not */
1961 return handlers[i].string;
1962 handlers[i].handler(ads, field, (struct berval **) values);
1963 break;
1966 if (!handlers[i].name) {
1967 if (!values) /* first time, indicate string conversion */
1968 return True;
1969 dump_string(field, (char **)values);
1971 return False;
1975 * Dump a result from LDAP on stdout
1976 * used for debugging
1977 * @param ads connection to ads server
1978 * @param res Results to dump
1981 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
1983 ads_process_results(ads, res, ads_dump_field, NULL);
1987 * Walk through results, calling a function for each entry found.
1988 * The function receives a field name, a berval * array of values,
1989 * and a data area passed through from the start. The function is
1990 * called once with null for field and values at the end of each
1991 * entry.
1992 * @param ads connection to ads server
1993 * @param res Results to process
1994 * @param fn Function for processing each result
1995 * @param data_area user-defined area to pass to function
1997 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
1998 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1999 void *data_area)
2001 LDAPMessage *msg;
2002 TALLOC_CTX *ctx;
2004 if (!(ctx = talloc_init("ads_process_results")))
2005 return;
2007 for (msg = ads_first_entry(ads, res); msg;
2008 msg = ads_next_entry(ads, msg)) {
2009 char *utf8_field;
2010 BerElement *b;
2012 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2013 (LDAPMessage *)msg,&b);
2014 utf8_field;
2015 utf8_field=ldap_next_attribute(ads->ldap.ld,
2016 (LDAPMessage *)msg,b)) {
2017 struct berval **ber_vals;
2018 char **str_vals, **utf8_vals;
2019 char *field;
2020 bool string;
2022 pull_utf8_talloc(ctx, &field, utf8_field);
2023 string = fn(ads, field, NULL, data_area);
2025 if (string) {
2026 utf8_vals = ldap_get_values(ads->ldap.ld,
2027 (LDAPMessage *)msg, field);
2028 str_vals = ads_pull_strvals(ctx,
2029 (const char **) utf8_vals);
2030 fn(ads, field, (void **) str_vals, data_area);
2031 ldap_value_free(utf8_vals);
2032 } else {
2033 ber_vals = ldap_get_values_len(ads->ldap.ld,
2034 (LDAPMessage *)msg, field);
2035 fn(ads, field, (void **) ber_vals, data_area);
2037 ldap_value_free_len(ber_vals);
2039 ldap_memfree(utf8_field);
2041 ber_free(b, 0);
2042 talloc_free_children(ctx);
2043 fn(ads, NULL, NULL, data_area); /* completed an entry */
2046 talloc_destroy(ctx);
2050 * count how many replies are in a LDAPMessage
2051 * @param ads connection to ads server
2052 * @param res Results to count
2053 * @return number of replies
2055 int ads_count_replies(ADS_STRUCT *ads, void *res)
2057 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2061 * pull the first entry from a ADS result
2062 * @param ads connection to ads server
2063 * @param res Results of search
2064 * @return first entry from result
2066 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2068 return ldap_first_entry(ads->ldap.ld, res);
2072 * pull the next entry from a ADS result
2073 * @param ads connection to ads server
2074 * @param res Results of search
2075 * @return next entry from result
2077 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2079 return ldap_next_entry(ads->ldap.ld, res);
2083 * pull a single string from a ADS result
2084 * @param ads connection to ads server
2085 * @param mem_ctx TALLOC_CTX to use for allocating result string
2086 * @param msg Results of search
2087 * @param field Attribute to retrieve
2088 * @return Result string in talloc context
2090 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2091 const char *field)
2093 char **values;
2094 char *ret = NULL;
2095 char *ux_string;
2096 size_t rc;
2098 values = ldap_get_values(ads->ldap.ld, msg, field);
2099 if (!values)
2100 return NULL;
2102 if (values[0]) {
2103 rc = pull_utf8_talloc(mem_ctx, &ux_string,
2104 values[0]);
2105 if (rc != (size_t)-1)
2106 ret = ux_string;
2109 ldap_value_free(values);
2110 return ret;
2114 * pull an array of strings from a ADS result
2115 * @param ads connection to ads server
2116 * @param mem_ctx TALLOC_CTX to use for allocating result string
2117 * @param msg Results of search
2118 * @param field Attribute to retrieve
2119 * @return Result strings in talloc context
2121 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2122 LDAPMessage *msg, const char *field,
2123 size_t *num_values)
2125 char **values;
2126 char **ret = NULL;
2127 int i;
2129 values = ldap_get_values(ads->ldap.ld, msg, field);
2130 if (!values)
2131 return NULL;
2133 *num_values = ldap_count_values(values);
2135 ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
2136 if (!ret) {
2137 ldap_value_free(values);
2138 return NULL;
2141 for (i=0;i<*num_values;i++) {
2142 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
2143 ldap_value_free(values);
2144 return NULL;
2147 ret[i] = NULL;
2149 ldap_value_free(values);
2150 return ret;
2154 * pull an array of strings from a ADS result
2155 * (handle large multivalue attributes with range retrieval)
2156 * @param ads connection to ads server
2157 * @param mem_ctx TALLOC_CTX to use for allocating result string
2158 * @param msg Results of search
2159 * @param field Attribute to retrieve
2160 * @param current_strings strings returned by a previous call to this function
2161 * @param next_attribute The next query should ask for this attribute
2162 * @param num_values How many values did we get this time?
2163 * @param more_values Are there more values to get?
2164 * @return Result strings in talloc context
2166 char **ads_pull_strings_range(ADS_STRUCT *ads,
2167 TALLOC_CTX *mem_ctx,
2168 LDAPMessage *msg, const char *field,
2169 char **current_strings,
2170 const char **next_attribute,
2171 size_t *num_strings,
2172 bool *more_strings)
2174 char *attr;
2175 char *expected_range_attrib, *range_attr;
2176 BerElement *ptr = NULL;
2177 char **strings;
2178 char **new_strings;
2179 size_t num_new_strings;
2180 unsigned long int range_start;
2181 unsigned long int range_end;
2183 /* we might have been given the whole lot anyway */
2184 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2185 *more_strings = False;
2186 return strings;
2189 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2191 /* look for Range result */
2192 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2193 attr;
2194 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2195 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2196 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2197 range_attr = attr;
2198 break;
2200 ldap_memfree(attr);
2202 if (!attr) {
2203 ber_free(ptr, 0);
2204 /* nothing here - this field is just empty */
2205 *more_strings = False;
2206 return NULL;
2209 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2210 &range_start, &range_end) == 2) {
2211 *more_strings = True;
2212 } else {
2213 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2214 &range_start) == 1) {
2215 *more_strings = False;
2216 } else {
2217 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2218 range_attr));
2219 ldap_memfree(range_attr);
2220 *more_strings = False;
2221 return NULL;
2225 if ((*num_strings) != range_start) {
2226 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2227 " - aborting range retreival\n",
2228 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2229 ldap_memfree(range_attr);
2230 *more_strings = False;
2231 return NULL;
2234 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2236 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2237 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2238 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2239 range_attr, (unsigned long int)range_end - range_start + 1,
2240 (unsigned long int)num_new_strings));
2241 ldap_memfree(range_attr);
2242 *more_strings = False;
2243 return NULL;
2246 strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2247 *num_strings + num_new_strings);
2249 if (strings == NULL) {
2250 ldap_memfree(range_attr);
2251 *more_strings = False;
2252 return NULL;
2255 if (new_strings && num_new_strings) {
2256 memcpy(&strings[*num_strings], new_strings,
2257 sizeof(*new_strings) * num_new_strings);
2260 (*num_strings) += num_new_strings;
2262 if (*more_strings) {
2263 *next_attribute = talloc_asprintf(mem_ctx,
2264 "%s;range=%d-*",
2265 field,
2266 (int)*num_strings);
2268 if (!*next_attribute) {
2269 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2270 ldap_memfree(range_attr);
2271 *more_strings = False;
2272 return NULL;
2276 ldap_memfree(range_attr);
2278 return strings;
2282 * pull a single uint32 from a ADS result
2283 * @param ads connection to ads server
2284 * @param msg Results of search
2285 * @param field Attribute to retrieve
2286 * @param v Pointer to int to store result
2287 * @return boolean inidicating success
2289 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2290 uint32 *v)
2292 char **values;
2294 values = ldap_get_values(ads->ldap.ld, msg, field);
2295 if (!values)
2296 return False;
2297 if (!values[0]) {
2298 ldap_value_free(values);
2299 return False;
2302 *v = atoi(values[0]);
2303 ldap_value_free(values);
2304 return True;
2308 * pull a single objectGUID from an ADS result
2309 * @param ads connection to ADS server
2310 * @param msg results of search
2311 * @param guid 37-byte area to receive text guid
2312 * @return boolean indicating success
2314 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2316 char **values;
2317 UUID_FLAT flat_guid;
2319 values = ldap_get_values(ads->ldap.ld, msg, "objectGUID");
2320 if (!values)
2321 return False;
2323 if (values[0]) {
2324 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2325 smb_uuid_unpack(flat_guid, guid);
2326 ldap_value_free(values);
2327 return True;
2329 ldap_value_free(values);
2330 return False;
2336 * pull a single DOM_SID from a ADS result
2337 * @param ads connection to ads server
2338 * @param msg Results of search
2339 * @param field Attribute to retrieve
2340 * @param sid Pointer to sid to store result
2341 * @return boolean inidicating success
2343 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2344 DOM_SID *sid)
2346 struct berval **values;
2347 bool ret = False;
2349 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2351 if (!values)
2352 return False;
2354 if (values[0])
2355 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2357 ldap_value_free_len(values);
2358 return ret;
2362 * pull an array of DOM_SIDs from a ADS result
2363 * @param ads connection to ads server
2364 * @param mem_ctx TALLOC_CTX for allocating sid array
2365 * @param msg Results of search
2366 * @param field Attribute to retrieve
2367 * @param sids pointer to sid array to allocate
2368 * @return the count of SIDs pulled
2370 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2371 LDAPMessage *msg, const char *field, DOM_SID **sids)
2373 struct berval **values;
2374 bool ret;
2375 int count, i;
2377 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2379 if (!values)
2380 return 0;
2382 for (i=0; values[i]; i++)
2383 /* nop */ ;
2385 if (i) {
2386 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2387 if (!(*sids)) {
2388 ldap_value_free_len(values);
2389 return 0;
2391 } else {
2392 (*sids) = NULL;
2395 count = 0;
2396 for (i=0; values[i]; i++) {
2397 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2398 if (ret) {
2399 DEBUG(10, ("pulling SID: %s\n",
2400 sid_string_dbg(&(*sids)[count])));
2401 count++;
2405 ldap_value_free_len(values);
2406 return count;
2410 * pull a SEC_DESC from a ADS result
2411 * @param ads connection to ads server
2412 * @param mem_ctx TALLOC_CTX for allocating sid array
2413 * @param msg Results of search
2414 * @param field Attribute to retrieve
2415 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2416 * @return boolean inidicating success
2418 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2419 LDAPMessage *msg, const char *field, SEC_DESC **sd)
2421 struct berval **values;
2422 bool ret = true;
2424 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2426 if (!values) return false;
2428 if (values[0]) {
2429 NTSTATUS status;
2430 status = unmarshall_sec_desc(mem_ctx,
2431 (uint8 *)values[0]->bv_val,
2432 values[0]->bv_len, sd);
2433 if (!NT_STATUS_IS_OK(status)) {
2434 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2435 nt_errstr(status)));
2436 ret = false;
2440 ldap_value_free_len(values);
2441 return ret;
2445 * in order to support usernames longer than 21 characters we need to
2446 * use both the sAMAccountName and the userPrincipalName attributes
2447 * It seems that not all users have the userPrincipalName attribute set
2449 * @param ads connection to ads server
2450 * @param mem_ctx TALLOC_CTX for allocating sid array
2451 * @param msg Results of search
2452 * @return the username
2454 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2455 LDAPMessage *msg)
2457 #if 0 /* JERRY */
2458 char *ret, *p;
2460 /* lookup_name() only works on the sAMAccountName to
2461 returning the username portion of userPrincipalName
2462 breaks winbindd_getpwnam() */
2464 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2465 if (ret && (p = strchr_m(ret, '@'))) {
2466 *p = 0;
2467 return ret;
2469 #endif
2470 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2475 * find the update serial number - this is the core of the ldap cache
2476 * @param ads connection to ads server
2477 * @param ads connection to ADS server
2478 * @param usn Pointer to retrieved update serial number
2479 * @return status of search
2481 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2483 const char *attrs[] = {"highestCommittedUSN", NULL};
2484 ADS_STATUS status;
2485 LDAPMessage *res;
2487 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2488 if (!ADS_ERR_OK(status))
2489 return status;
2491 if (ads_count_replies(ads, res) != 1) {
2492 ads_msgfree(ads, res);
2493 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2496 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2497 ads_msgfree(ads, res);
2498 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2501 ads_msgfree(ads, res);
2502 return ADS_SUCCESS;
2505 /* parse a ADS timestring - typical string is
2506 '20020917091222.0Z0' which means 09:12.22 17th September
2507 2002, timezone 0 */
2508 static time_t ads_parse_time(const char *str)
2510 struct tm tm;
2512 ZERO_STRUCT(tm);
2514 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2515 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2516 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2517 return 0;
2519 tm.tm_year -= 1900;
2520 tm.tm_mon -= 1;
2522 return timegm(&tm);
2525 /********************************************************************
2526 ********************************************************************/
2528 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2530 const char *attrs[] = {"currentTime", NULL};
2531 ADS_STATUS status;
2532 LDAPMessage *res;
2533 char *timestr;
2534 TALLOC_CTX *ctx;
2535 ADS_STRUCT *ads_s = ads;
2537 if (!(ctx = talloc_init("ads_current_time"))) {
2538 return ADS_ERROR(LDAP_NO_MEMORY);
2541 /* establish a new ldap tcp session if necessary */
2543 if ( !ads->ldap.ld ) {
2544 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2545 ads->server.ldap_server )) == NULL )
2547 goto done;
2549 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2550 status = ads_connect( ads_s );
2551 if ( !ADS_ERR_OK(status))
2552 goto done;
2555 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2556 if (!ADS_ERR_OK(status)) {
2557 goto done;
2560 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2561 if (!timestr) {
2562 ads_msgfree(ads_s, res);
2563 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2564 goto done;
2567 /* but save the time and offset in the original ADS_STRUCT */
2569 ads->config.current_time = ads_parse_time(timestr);
2571 if (ads->config.current_time != 0) {
2572 ads->auth.time_offset = ads->config.current_time - time(NULL);
2573 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2576 ads_msgfree(ads, res);
2578 status = ADS_SUCCESS;
2580 done:
2581 /* free any temporary ads connections */
2582 if ( ads_s != ads ) {
2583 ads_destroy( &ads_s );
2585 talloc_destroy(ctx);
2587 return status;
2590 /********************************************************************
2591 ********************************************************************/
2593 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2595 const char *attrs[] = {"domainFunctionality", NULL};
2596 ADS_STATUS status;
2597 LDAPMessage *res;
2598 ADS_STRUCT *ads_s = ads;
2600 *val = DS_DOMAIN_FUNCTION_2000;
2602 /* establish a new ldap tcp session if necessary */
2604 if ( !ads->ldap.ld ) {
2605 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2606 ads->server.ldap_server )) == NULL )
2608 goto done;
2610 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2611 status = ads_connect( ads_s );
2612 if ( !ADS_ERR_OK(status))
2613 goto done;
2616 /* If the attribute does not exist assume it is a Windows 2000
2617 functional domain */
2619 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2620 if (!ADS_ERR_OK(status)) {
2621 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2622 status = ADS_SUCCESS;
2624 goto done;
2627 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2628 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2630 DEBUG(3,("ads_domain_func_level: %d\n", *val));
2633 ads_msgfree(ads, res);
2635 done:
2636 /* free any temporary ads connections */
2637 if ( ads_s != ads ) {
2638 ads_destroy( &ads_s );
2641 return status;
2645 * find the domain sid for our domain
2646 * @param ads connection to ads server
2647 * @param sid Pointer to domain sid
2648 * @return status of search
2650 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2652 const char *attrs[] = {"objectSid", NULL};
2653 LDAPMessage *res;
2654 ADS_STATUS rc;
2656 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2657 attrs, &res);
2658 if (!ADS_ERR_OK(rc)) return rc;
2659 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2660 ads_msgfree(ads, res);
2661 return ADS_ERROR_SYSTEM(ENOENT);
2663 ads_msgfree(ads, res);
2665 return ADS_SUCCESS;
2669 * find our site name
2670 * @param ads connection to ads server
2671 * @param mem_ctx Pointer to talloc context
2672 * @param site_name Pointer to the sitename
2673 * @return status of search
2675 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2677 ADS_STATUS status;
2678 LDAPMessage *res;
2679 const char *dn, *service_name;
2680 const char *attrs[] = { "dsServiceName", NULL };
2682 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2683 if (!ADS_ERR_OK(status)) {
2684 return status;
2687 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2688 if (service_name == NULL) {
2689 ads_msgfree(ads, res);
2690 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2693 ads_msgfree(ads, res);
2695 /* go up three levels */
2696 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2697 if (dn == NULL) {
2698 return ADS_ERROR(LDAP_NO_MEMORY);
2701 *site_name = talloc_strdup(mem_ctx, dn);
2702 if (*site_name == NULL) {
2703 return ADS_ERROR(LDAP_NO_MEMORY);
2706 return status;
2708 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2713 * find the site dn where a machine resides
2714 * @param ads connection to ads server
2715 * @param mem_ctx Pointer to talloc context
2716 * @param computer_name name of the machine
2717 * @param site_name Pointer to the sitename
2718 * @return status of search
2720 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
2722 ADS_STATUS status;
2723 LDAPMessage *res;
2724 const char *parent, *filter;
2725 char *config_context = NULL;
2726 char *dn;
2728 /* shortcut a query */
2729 if (strequal(computer_name, ads->config.ldap_server_name)) {
2730 return ads_site_dn(ads, mem_ctx, site_dn);
2733 status = ads_config_path(ads, mem_ctx, &config_context);
2734 if (!ADS_ERR_OK(status)) {
2735 return status;
2738 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
2739 if (filter == NULL) {
2740 return ADS_ERROR(LDAP_NO_MEMORY);
2743 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
2744 filter, NULL, &res);
2745 if (!ADS_ERR_OK(status)) {
2746 return status;
2749 if (ads_count_replies(ads, res) != 1) {
2750 ads_msgfree(ads, res);
2751 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2754 dn = ads_get_dn(ads, res);
2755 if (dn == NULL) {
2756 ads_msgfree(ads, res);
2757 return ADS_ERROR(LDAP_NO_MEMORY);
2760 /* go up three levels */
2761 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
2762 if (parent == NULL) {
2763 ads_msgfree(ads, res);
2764 ads_memfree(ads, dn);
2765 return ADS_ERROR(LDAP_NO_MEMORY);
2768 *site_dn = talloc_strdup(mem_ctx, parent);
2769 if (*site_dn == NULL) {
2770 ads_msgfree(ads, res);
2771 ads_memfree(ads, dn);
2772 return ADS_ERROR(LDAP_NO_MEMORY);
2775 ads_memfree(ads, dn);
2776 ads_msgfree(ads, res);
2778 return status;
2782 * get the upn suffixes for a domain
2783 * @param ads connection to ads server
2784 * @param mem_ctx Pointer to talloc context
2785 * @param suffixes Pointer to an array of suffixes
2786 * @param num_suffixes Pointer to the number of suffixes
2787 * @return status of search
2789 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
2791 ADS_STATUS status;
2792 LDAPMessage *res;
2793 const char *base;
2794 char *config_context = NULL;
2795 const char *attrs[] = { "uPNSuffixes", NULL };
2797 status = ads_config_path(ads, mem_ctx, &config_context);
2798 if (!ADS_ERR_OK(status)) {
2799 return status;
2802 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
2803 if (base == NULL) {
2804 return ADS_ERROR(LDAP_NO_MEMORY);
2807 status = ads_search_dn(ads, &res, base, attrs);
2808 if (!ADS_ERR_OK(status)) {
2809 return status;
2812 if (ads_count_replies(ads, res) != 1) {
2813 ads_msgfree(ads, res);
2814 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2817 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
2818 if ((*suffixes) == NULL) {
2819 ads_msgfree(ads, res);
2820 return ADS_ERROR(LDAP_NO_MEMORY);
2823 ads_msgfree(ads, res);
2825 return status;
2829 * get the joinable ous for a domain
2830 * @param ads connection to ads server
2831 * @param mem_ctx Pointer to talloc context
2832 * @param ous Pointer to an array of ous
2833 * @param num_ous Pointer to the number of ous
2834 * @return status of search
2836 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
2837 TALLOC_CTX *mem_ctx,
2838 char ***ous,
2839 size_t *num_ous)
2841 ADS_STATUS status;
2842 LDAPMessage *res = NULL;
2843 LDAPMessage *msg = NULL;
2844 const char *attrs[] = { "dn", NULL };
2845 int count = 0;
2847 status = ads_search(ads, &res,
2848 "(|(objectClass=domain)(objectclass=organizationalUnit))",
2849 attrs);
2850 if (!ADS_ERR_OK(status)) {
2851 return status;
2854 count = ads_count_replies(ads, res);
2855 if (count < 1) {
2856 ads_msgfree(ads, res);
2857 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2860 for (msg = ads_first_entry(ads, res); msg;
2861 msg = ads_next_entry(ads, msg)) {
2863 char *dn = NULL;
2865 dn = ads_get_dn(ads, msg);
2866 if (!dn) {
2867 ads_msgfree(ads, res);
2868 return ADS_ERROR(LDAP_NO_MEMORY);
2871 if (!add_string_to_array(mem_ctx, dn,
2872 (const char ***)ous,
2873 (int *)num_ous)) {
2874 ads_memfree(ads, dn);
2875 ads_msgfree(ads, res);
2876 return ADS_ERROR(LDAP_NO_MEMORY);
2879 ads_memfree(ads, dn);
2882 ads_msgfree(ads, res);
2884 return status;
2889 * pull a DOM_SID from an extended dn string
2890 * @param mem_ctx TALLOC_CTX
2891 * @param extended_dn string
2892 * @param flags string type of extended_dn
2893 * @param sid pointer to a DOM_SID
2894 * @return boolean inidicating success
2896 bool ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
2897 const char *extended_dn,
2898 enum ads_extended_dn_flags flags,
2899 DOM_SID *sid)
2901 char *p, *q, *dn;
2903 if (!extended_dn) {
2904 return False;
2907 /* otherwise extended_dn gets stripped off */
2908 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
2909 return False;
2912 * ADS_EXTENDED_DN_HEX_STRING:
2913 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
2915 * ADS_EXTENDED_DN_STRING (only with w2k3):
2916 <GUID=63198e23-39cb-4b0f-b032-ba0105525a29>;<SID=S-1-5-21-4257769659-666132843-1169174103-1110>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
2919 p = strchr(dn, ';');
2920 if (!p) {
2921 return False;
2924 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
2925 return False;
2928 p += strlen(";<SID=");
2930 q = strchr(p, '>');
2931 if (!q) {
2932 return False;
2935 *q = '\0';
2937 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
2939 switch (flags) {
2941 case ADS_EXTENDED_DN_STRING:
2942 if (!string_to_sid(sid, p)) {
2943 return False;
2945 break;
2946 case ADS_EXTENDED_DN_HEX_STRING: {
2947 fstring buf;
2948 size_t buf_len;
2950 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
2951 if (buf_len == 0) {
2952 return False;
2955 if (!sid_parse(buf, buf_len, sid)) {
2956 DEBUG(10,("failed to parse sid\n"));
2957 return False;
2959 break;
2961 default:
2962 DEBUG(10,("unknown extended dn format\n"));
2963 return False;
2966 return True;
2970 * pull an array of DOM_SIDs from a ADS result
2971 * @param ads connection to ads server
2972 * @param mem_ctx TALLOC_CTX for allocating sid array
2973 * @param msg Results of search
2974 * @param field Attribute to retrieve
2975 * @param flags string type of extended_dn
2976 * @param sids pointer to sid array to allocate
2977 * @return the count of SIDs pulled
2979 int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads,
2980 TALLOC_CTX *mem_ctx,
2981 LDAPMessage *msg,
2982 const char *field,
2983 enum ads_extended_dn_flags flags,
2984 DOM_SID **sids)
2986 int i;
2987 size_t dn_count;
2988 char **dn_strings;
2990 if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field,
2991 &dn_count)) == NULL) {
2992 return 0;
2995 (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, dn_count + 1);
2996 if (!(*sids)) {
2997 TALLOC_FREE(dn_strings);
2998 return 0;
3001 for (i=0; i<dn_count; i++) {
3003 if (!ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i],
3004 flags, &(*sids)[i])) {
3005 TALLOC_FREE(*sids);
3006 TALLOC_FREE(dn_strings);
3007 return 0;
3011 TALLOC_FREE(dn_strings);
3013 return dn_count;
3016 /********************************************************************
3017 ********************************************************************/
3019 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3021 LDAPMessage *res = NULL;
3022 ADS_STATUS status;
3023 int count = 0;
3024 char *name = NULL;
3026 status = ads_find_machine_acct(ads, &res, global_myname());
3027 if (!ADS_ERR_OK(status)) {
3028 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3029 global_myname()));
3030 goto out;
3033 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3034 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3035 goto out;
3038 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3039 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3042 out:
3043 ads_msgfree(ads, res);
3045 return name;
3048 /********************************************************************
3049 ********************************************************************/
3051 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3053 LDAPMessage *res = NULL;
3054 ADS_STATUS status;
3055 int count = 0;
3056 char *name = NULL;
3058 status = ads_find_machine_acct(ads, &res, machine_name);
3059 if (!ADS_ERR_OK(status)) {
3060 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3061 global_myname()));
3062 goto out;
3065 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3066 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3067 goto out;
3070 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3071 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3074 out:
3075 ads_msgfree(ads, res);
3077 return name;
3080 /********************************************************************
3081 ********************************************************************/
3083 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3085 LDAPMessage *res = NULL;
3086 ADS_STATUS status;
3087 int count = 0;
3088 char *name = NULL;
3090 status = ads_find_machine_acct(ads, &res, global_myname());
3091 if (!ADS_ERR_OK(status)) {
3092 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3093 global_myname()));
3094 goto out;
3097 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3098 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3099 goto out;
3102 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3103 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3106 out:
3107 ads_msgfree(ads, res);
3109 return name;
3112 #if 0
3114 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3117 * Join a machine to a realm
3118 * Creates the machine account and sets the machine password
3119 * @param ads connection to ads server
3120 * @param machine name of host to add
3121 * @param org_unit Organizational unit to place machine in
3122 * @return status of join
3124 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3125 uint32 account_type, const char *org_unit)
3127 ADS_STATUS status;
3128 LDAPMessage *res = NULL;
3129 char *machine;
3131 /* machine name must be lowercase */
3132 machine = SMB_STRDUP(machine_name);
3133 strlower_m(machine);
3136 status = ads_find_machine_acct(ads, (void **)&res, machine);
3137 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3138 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3139 status = ads_leave_realm(ads, machine);
3140 if (!ADS_ERR_OK(status)) {
3141 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3142 machine, ads->config.realm));
3143 return status;
3147 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3148 if (!ADS_ERR_OK(status)) {
3149 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3150 SAFE_FREE(machine);
3151 return status;
3154 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3155 if (!ADS_ERR_OK(status)) {
3156 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3157 SAFE_FREE(machine);
3158 return status;
3161 SAFE_FREE(machine);
3162 ads_msgfree(ads, res);
3164 return status;
3166 #endif
3169 * Delete a machine from the realm
3170 * @param ads connection to ads server
3171 * @param hostname Machine to remove
3172 * @return status of delete
3174 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3176 ADS_STATUS status;
3177 void *msg;
3178 LDAPMessage *res;
3179 char *hostnameDN, *host;
3180 int rc;
3181 LDAPControl ldap_control;
3182 LDAPControl * pldap_control[2] = {NULL, NULL};
3184 pldap_control[0] = &ldap_control;
3185 memset(&ldap_control, 0, sizeof(LDAPControl));
3186 ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
3188 /* hostname must be lowercase */
3189 host = SMB_STRDUP(hostname);
3190 strlower_m(host);
3192 status = ads_find_machine_acct(ads, &res, host);
3193 if (!ADS_ERR_OK(status)) {
3194 DEBUG(0, ("Host account for %s does not exist.\n", host));
3195 SAFE_FREE(host);
3196 return status;
3199 msg = ads_first_entry(ads, res);
3200 if (!msg) {
3201 SAFE_FREE(host);
3202 return ADS_ERROR_SYSTEM(ENOENT);
3205 hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
3207 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3208 if (rc) {
3209 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3210 }else {
3211 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3214 if (rc != LDAP_SUCCESS) {
3215 const char *attrs[] = { "cn", NULL };
3216 LDAPMessage *msg_sub;
3218 /* we only search with scope ONE, we do not expect any further
3219 * objects to be created deeper */
3221 status = ads_do_search_retry(ads, hostnameDN,
3222 LDAP_SCOPE_ONELEVEL,
3223 "(objectclass=*)", attrs, &res);
3225 if (!ADS_ERR_OK(status)) {
3226 SAFE_FREE(host);
3227 ads_memfree(ads, hostnameDN);
3228 return status;
3231 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3232 msg_sub = ads_next_entry(ads, msg_sub)) {
3234 char *dn = NULL;
3236 if ((dn = ads_get_dn(ads, msg_sub)) == NULL) {
3237 SAFE_FREE(host);
3238 ads_memfree(ads, hostnameDN);
3239 return ADS_ERROR(LDAP_NO_MEMORY);
3242 status = ads_del_dn(ads, dn);
3243 if (!ADS_ERR_OK(status)) {
3244 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3245 SAFE_FREE(host);
3246 ads_memfree(ads, dn);
3247 ads_memfree(ads, hostnameDN);
3248 return status;
3251 ads_memfree(ads, dn);
3254 /* there should be no subordinate objects anymore */
3255 status = ads_do_search_retry(ads, hostnameDN,
3256 LDAP_SCOPE_ONELEVEL,
3257 "(objectclass=*)", attrs, &res);
3259 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3260 SAFE_FREE(host);
3261 ads_memfree(ads, hostnameDN);
3262 return status;
3265 /* delete hostnameDN now */
3266 status = ads_del_dn(ads, hostnameDN);
3267 if (!ADS_ERR_OK(status)) {
3268 SAFE_FREE(host);
3269 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3270 ads_memfree(ads, hostnameDN);
3271 return status;
3275 ads_memfree(ads, hostnameDN);
3277 status = ads_find_machine_acct(ads, &res, host);
3278 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3279 DEBUG(3, ("Failed to remove host account.\n"));
3280 SAFE_FREE(host);
3281 return status;
3284 SAFE_FREE(host);
3285 return status;
3289 * pull all token-sids from an LDAP dn
3290 * @param ads connection to ads server
3291 * @param mem_ctx TALLOC_CTX for allocating sid array
3292 * @param dn of LDAP object
3293 * @param user_sid pointer to DOM_SID (objectSid)
3294 * @param primary_group_sid pointer to DOM_SID (self composed)
3295 * @param sids pointer to sid array to allocate
3296 * @param num_sids counter of SIDs pulled
3297 * @return status of token query
3299 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3300 TALLOC_CTX *mem_ctx,
3301 const char *dn,
3302 DOM_SID *user_sid,
3303 DOM_SID *primary_group_sid,
3304 DOM_SID **sids,
3305 size_t *num_sids)
3307 ADS_STATUS status;
3308 LDAPMessage *res = NULL;
3309 int count = 0;
3310 size_t tmp_num_sids;
3311 DOM_SID *tmp_sids;
3312 DOM_SID tmp_user_sid;
3313 DOM_SID tmp_primary_group_sid;
3314 uint32 pgid;
3315 const char *attrs[] = {
3316 "objectSid",
3317 "tokenGroups",
3318 "primaryGroupID",
3319 NULL
3322 status = ads_search_retry_dn(ads, &res, dn, attrs);
3323 if (!ADS_ERR_OK(status)) {
3324 return status;
3327 count = ads_count_replies(ads, res);
3328 if (count != 1) {
3329 ads_msgfree(ads, res);
3330 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3333 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3334 ads_msgfree(ads, res);
3335 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3338 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3339 ads_msgfree(ads, res);
3340 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3344 /* hack to compose the primary group sid without knowing the
3345 * domsid */
3347 DOM_SID domsid;
3348 uint32 dummy_rid;
3350 sid_copy(&domsid, &tmp_user_sid);
3352 if (!sid_split_rid(&domsid, &dummy_rid)) {
3353 ads_msgfree(ads, res);
3354 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3357 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3358 ads_msgfree(ads, res);
3359 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3363 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3365 if (tmp_num_sids == 0 || !tmp_sids) {
3366 ads_msgfree(ads, res);
3367 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3370 if (num_sids) {
3371 *num_sids = tmp_num_sids;
3374 if (sids) {
3375 *sids = tmp_sids;
3378 if (user_sid) {
3379 *user_sid = tmp_user_sid;
3382 if (primary_group_sid) {
3383 *primary_group_sid = tmp_primary_group_sid;
3386 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3388 ads_msgfree(ads, res);
3389 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3393 * Find a sAMAccoutName in LDAP
3394 * @param ads connection to ads server
3395 * @param mem_ctx TALLOC_CTX for allocating sid array
3396 * @param samaccountname to search
3397 * @param uac_ret uint32 pointer userAccountControl attribute value
3398 * @param dn_ret pointer to dn
3399 * @return status of token query
3401 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3402 TALLOC_CTX *mem_ctx,
3403 const char *samaccountname,
3404 uint32 *uac_ret,
3405 const char **dn_ret)
3407 ADS_STATUS status;
3408 const char *attrs[] = { "userAccountControl", NULL };
3409 const char *filter;
3410 LDAPMessage *res = NULL;
3411 char *dn = NULL;
3412 uint32 uac = 0;
3414 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3415 samaccountname);
3416 if (filter == NULL) {
3417 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3418 goto out;
3421 status = ads_do_search_all(ads, ads->config.bind_path,
3422 LDAP_SCOPE_SUBTREE,
3423 filter, attrs, &res);
3425 if (!ADS_ERR_OK(status)) {
3426 goto out;
3429 if (ads_count_replies(ads, res) != 1) {
3430 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3431 goto out;
3434 dn = ads_get_dn(ads, res);
3435 if (dn == NULL) {
3436 status = ADS_ERROR(LDAP_NO_MEMORY);
3437 goto out;
3440 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3441 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3442 goto out;
3445 if (uac_ret) {
3446 *uac_ret = uac;
3449 if (dn_ret) {
3450 *dn_ret = talloc_strdup(mem_ctx, dn);
3451 if (!*dn_ret) {
3452 status = ADS_ERROR(LDAP_NO_MEMORY);
3453 goto out;
3456 out:
3457 ads_memfree(ads, dn);
3458 ads_msgfree(ads, res);
3460 return status;
3464 * find our configuration path
3465 * @param ads connection to ads server
3466 * @param mem_ctx Pointer to talloc context
3467 * @param config_path Pointer to the config path
3468 * @return status of search
3470 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3471 TALLOC_CTX *mem_ctx,
3472 char **config_path)
3474 ADS_STATUS status;
3475 LDAPMessage *res = NULL;
3476 const char *config_context = NULL;
3477 const char *attrs[] = { "configurationNamingContext", NULL };
3479 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3480 "(objectclass=*)", attrs, &res);
3481 if (!ADS_ERR_OK(status)) {
3482 return status;
3485 config_context = ads_pull_string(ads, mem_ctx, res,
3486 "configurationNamingContext");
3487 ads_msgfree(ads, res);
3488 if (!config_context) {
3489 return ADS_ERROR(LDAP_NO_MEMORY);
3492 if (config_path) {
3493 *config_path = talloc_strdup(mem_ctx, config_context);
3494 if (!*config_path) {
3495 return ADS_ERROR(LDAP_NO_MEMORY);
3499 return ADS_ERROR(LDAP_SUCCESS);
3503 * find the displayName of an extended right
3504 * @param ads connection to ads server
3505 * @param config_path The config path
3506 * @param mem_ctx Pointer to talloc context
3507 * @param GUID struct of the rightsGUID
3508 * @return status of search
3510 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3511 const char *config_path,
3512 TALLOC_CTX *mem_ctx,
3513 const struct GUID *rights_guid)
3515 ADS_STATUS rc;
3516 LDAPMessage *res = NULL;
3517 char *expr = NULL;
3518 const char *attrs[] = { "displayName", NULL };
3519 const char *result = NULL;
3520 const char *path;
3522 if (!ads || !mem_ctx || !rights_guid) {
3523 goto done;
3526 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3527 smb_uuid_string(mem_ctx, *rights_guid));
3528 if (!expr) {
3529 goto done;
3532 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3533 if (!path) {
3534 goto done;
3537 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3538 expr, attrs, &res);
3539 if (!ADS_ERR_OK(rc)) {
3540 goto done;
3543 if (ads_count_replies(ads, res) != 1) {
3544 goto done;
3547 result = ads_pull_string(ads, mem_ctx, res, "displayName");
3549 done:
3550 ads_msgfree(ads, res);
3551 return result;
3556 * verify or build and verify an account ou
3557 * @param mem_ctx Pointer to talloc context
3558 * @param ads connection to ads server
3559 * @param account_ou
3560 * @return status of search
3563 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3564 ADS_STRUCT *ads,
3565 const char *account_ou)
3567 struct ldb_dn *name_dn = NULL;
3568 const char *name = NULL;
3569 char *ou_string = NULL;
3571 name_dn = ldb_dn_explode(mem_ctx, account_ou);
3572 if (name_dn) {
3573 return ADS_SUCCESS;
3576 ou_string = ads_ou_string(ads, account_ou);
3577 if (!ou_string) {
3578 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3581 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3582 ads->config.bind_path);
3583 SAFE_FREE(ou_string);
3584 if (!name) {
3585 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3588 name_dn = ldb_dn_explode(mem_ctx, name);
3589 if (!name_dn) {
3590 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3593 account_ou = talloc_strdup(mem_ctx, name);
3594 if (!account_ou) {
3595 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3598 return ADS_SUCCESS;
3601 #endif