cldap: avoid duplicate definitions so remove ads_cldap.h.
[Samba/gebeck_regimport.git] / source3 / libads / ldap.c
blobb0f27b598bbdf50d6aa2b1a8b8155ca48cfd038b
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 & NBT_SERVER_CLOSEST) {
155 DEBUG(10,("ads_closest_dc: NBT_SERVER_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: NBT_SERVER_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 nbt_cldap_netlogon_5 cldap_reply;
180 TALLOC_CTX *mem_ctx = NULL;
181 bool ret = false;
183 if (!server || !*server) {
184 return False;
187 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
188 server, ads->server.realm));
190 mem_ctx = talloc_init("ads_try_connect");
191 if (!mem_ctx) {
192 DEBUG(0,("out of memory\n"));
193 return false;
196 /* this copes with inet_ntoa brokenness */
198 srv = SMB_STRDUP(server);
200 ZERO_STRUCT( cldap_reply );
202 if ( !ads_cldap_netlogon(mem_ctx, srv, ads->server.realm, &cldap_reply ) ) {
203 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", srv));
204 ret = false;
205 goto out;
208 /* Check the CLDAP reply flags */
210 if ( !(cldap_reply.server_type & NBT_SERVER_LDAP) ) {
211 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
212 srv));
213 ret = false;
214 goto out;
217 /* Fill in the ads->config values */
219 SAFE_FREE(ads->config.realm);
220 SAFE_FREE(ads->config.bind_path);
221 SAFE_FREE(ads->config.ldap_server_name);
222 SAFE_FREE(ads->config.server_site_name);
223 SAFE_FREE(ads->config.client_site_name);
224 SAFE_FREE(ads->server.workgroup);
226 ads->config.flags = cldap_reply.server_type;
227 ads->config.ldap_server_name = SMB_STRDUP(cldap_reply.pdc_dns_name);
228 ads->config.realm = SMB_STRDUP(cldap_reply.dns_domain);
229 strupper_m(ads->config.realm);
230 ads->config.bind_path = ads_build_dn(ads->config.realm);
231 if (*cldap_reply.server_site) {
232 ads->config.server_site_name =
233 SMB_STRDUP(cldap_reply.server_site);
235 if (*cldap_reply.client_site) {
236 ads->config.client_site_name =
237 SMB_STRDUP(cldap_reply.client_site);
239 ads->server.workgroup = SMB_STRDUP(cldap_reply.domain);
241 ads->ldap.port = LDAP_PORT;
242 if (!interpret_string_addr(&ads->ldap.ss, srv, 0)) {
243 DEBUG(1,("ads_try_connect: unable to convert %s "
244 "to an address\n",
245 srv));
246 ret = false;
247 goto out;
250 /* Store our site name. */
251 sitename_store( cldap_reply.domain, cldap_reply.client_site);
253 ret = true;
254 out:
255 SAFE_FREE(srv);
256 TALLOC_FREE(mem_ctx);
258 return ret;
261 /**********************************************************************
262 Try to find an AD dc using our internal name resolution routines
263 Try the realm first and then then workgroup name if netbios is not
264 disabled
265 **********************************************************************/
267 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
269 const char *c_realm;
270 int count, i=0;
271 struct ip_service *ip_list;
272 const char *realm;
273 bool got_realm = False;
274 bool use_own_domain = False;
275 char *sitename;
276 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
278 /* if the realm and workgroup are both empty, assume they are ours */
280 /* realm */
281 c_realm = ads->server.realm;
283 if ( !c_realm || !*c_realm ) {
284 /* special case where no realm and no workgroup means our own */
285 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
286 use_own_domain = True;
287 c_realm = lp_realm();
291 if (c_realm && *c_realm)
292 got_realm = True;
294 /* we need to try once with the realm name and fallback to the
295 netbios domain name if we fail (if netbios has not been disabled */
297 if ( !got_realm && !lp_disable_netbios() ) {
298 c_realm = ads->server.workgroup;
299 if (!c_realm || !*c_realm) {
300 if ( use_own_domain )
301 c_realm = lp_workgroup();
304 if ( !c_realm || !*c_realm ) {
305 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
306 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
310 realm = c_realm;
312 sitename = sitename_fetch(realm);
314 again:
316 DEBUG(6,("ads_find_dc: looking for %s '%s'\n",
317 (got_realm ? "realm" : "domain"), realm));
319 status = get_sorted_dc_list(realm, sitename, &ip_list, &count, got_realm);
320 if (!NT_STATUS_IS_OK(status)) {
321 /* fall back to netbios if we can */
322 if ( got_realm && !lp_disable_netbios() ) {
323 got_realm = False;
324 goto again;
327 SAFE_FREE(sitename);
328 return status;
331 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
332 for ( i=0; i<count; i++ ) {
333 char server[INET6_ADDRSTRLEN];
335 print_sockaddr(server, sizeof(server), &ip_list[i].ss);
337 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
338 continue;
340 if (!got_realm) {
341 /* realm in this case is a workgroup name. We need
342 to ignore any IP addresses in the negative connection
343 cache that match ip addresses returned in the ad realm
344 case. It sucks that I have to reproduce the logic above... */
345 c_realm = ads->server.realm;
346 if ( !c_realm || !*c_realm ) {
347 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
348 c_realm = lp_realm();
351 if (c_realm && *c_realm &&
352 !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm, server))) {
353 /* Ensure we add the workgroup name for this
354 IP address as negative too. */
355 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
356 continue;
360 if ( ads_try_connect(ads, server) ) {
361 SAFE_FREE(ip_list);
362 SAFE_FREE(sitename);
363 return NT_STATUS_OK;
366 /* keep track of failures */
367 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
370 SAFE_FREE(ip_list);
372 /* In case we failed to contact one of our closest DC on our site we
373 * need to try to find another DC, retry with a site-less SRV DNS query
374 * - Guenther */
376 if (sitename) {
377 DEBUG(1,("ads_find_dc: failed to find a valid DC on our site (%s), "
378 "trying to find another DC\n", sitename));
379 SAFE_FREE(sitename);
380 namecache_delete(realm, 0x1C);
381 goto again;
384 return NT_STATUS_NO_LOGON_SERVERS;
389 * Connect to the LDAP server
390 * @param ads Pointer to an existing ADS_STRUCT
391 * @return status of connection
393 ADS_STATUS ads_connect(ADS_STRUCT *ads)
395 int version = LDAP_VERSION3;
396 ADS_STATUS status;
397 NTSTATUS ntstatus;
398 char addr[INET6_ADDRSTRLEN];
400 ZERO_STRUCT(ads->ldap);
401 ads->ldap.last_attempt = time(NULL);
402 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
404 /* try with a user specified server */
406 if (DEBUGLEVEL >= 11) {
407 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
408 DEBUG(11,("ads_connect: entering\n"));
409 DEBUGADD(11,("%s\n", s));
410 TALLOC_FREE(s);
413 if (ads->server.ldap_server &&
414 ads_try_connect(ads, ads->server.ldap_server)) {
415 goto got_connection;
418 ntstatus = ads_find_dc(ads);
419 if (NT_STATUS_IS_OK(ntstatus)) {
420 goto got_connection;
423 status = ADS_ERROR_NT(ntstatus);
424 goto out;
426 got_connection:
428 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
429 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
431 if (!ads->auth.user_name) {
432 /* Must use the userPrincipalName value here or sAMAccountName
433 and not servicePrincipalName; found by Guenther Deschner */
435 asprintf(&ads->auth.user_name, "%s$", global_myname() );
438 if (!ads->auth.realm) {
439 ads->auth.realm = SMB_STRDUP(ads->config.realm);
442 if (!ads->auth.kdc_server) {
443 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
444 ads->auth.kdc_server = SMB_STRDUP(addr);
447 #if KRB5_DNS_HACK
448 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
449 to MIT kerberos to work (tridge) */
451 char *env;
452 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
453 setenv(env, ads->auth.kdc_server, 1);
454 free(env);
456 #endif
458 /* If the caller() requested no LDAP bind, then we are done */
460 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
461 status = ADS_SUCCESS;
462 goto out;
465 ads->ldap.mem_ctx = talloc_init("ads LDAP connection memory");
466 if (!ads->ldap.mem_ctx) {
467 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
468 goto out;
471 /* Otherwise setup the TCP LDAP session */
473 ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name,
474 LDAP_PORT, lp_ldap_timeout());
475 if (ads->ldap.ld == NULL) {
476 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
477 goto out;
479 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
481 /* cache the successful connection for workgroup and realm */
482 if (ads_closest_dc(ads)) {
483 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
484 saf_store( ads->server.workgroup, addr);
485 saf_store( ads->server.realm, addr);
488 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
490 status = ADS_ERROR(smb_ldap_start_tls(ads->ldap.ld, version));
491 if (!ADS_ERR_OK(status)) {
492 goto out;
495 /* fill in the current time and offsets */
497 status = ads_current_time( ads );
498 if ( !ADS_ERR_OK(status) ) {
499 goto out;
502 /* Now do the bind */
504 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
505 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
506 goto out;
509 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
510 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
511 goto out;
514 status = ads_sasl_bind(ads);
516 out:
517 if (DEBUGLEVEL >= 11) {
518 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
519 DEBUG(11,("ads_connect: leaving with: %s\n",
520 ads_errstr(status)));
521 DEBUGADD(11,("%s\n", s));
522 TALLOC_FREE(s);
525 return status;
529 * Disconnect the LDAP server
530 * @param ads Pointer to an existing ADS_STRUCT
532 void ads_disconnect(ADS_STRUCT *ads)
534 if (ads->ldap.ld) {
535 ldap_unbind(ads->ldap.ld);
536 ads->ldap.ld = NULL;
538 if (ads->ldap.wrap_ops && ads->ldap.wrap_ops->disconnect) {
539 ads->ldap.wrap_ops->disconnect(ads);
541 if (ads->ldap.mem_ctx) {
542 talloc_free(ads->ldap.mem_ctx);
544 ZERO_STRUCT(ads->ldap);
548 Duplicate a struct berval into talloc'ed memory
550 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
552 struct berval *value;
554 if (!in_val) return NULL;
556 value = TALLOC_ZERO_P(ctx, struct berval);
557 if (value == NULL)
558 return NULL;
559 if (in_val->bv_len == 0) return value;
561 value->bv_len = in_val->bv_len;
562 value->bv_val = (char *)TALLOC_MEMDUP(ctx, in_val->bv_val,
563 in_val->bv_len);
564 return value;
568 Make a values list out of an array of (struct berval *)
570 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
571 const struct berval **in_vals)
573 struct berval **values;
574 int i;
576 if (!in_vals) return NULL;
577 for (i=0; in_vals[i]; i++)
578 ; /* count values */
579 values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
580 if (!values) return NULL;
582 for (i=0; in_vals[i]; i++) {
583 values[i] = dup_berval(ctx, in_vals[i]);
585 return values;
589 UTF8-encode a values list out of an array of (char *)
591 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
593 char **values;
594 int i;
596 if (!in_vals) return NULL;
597 for (i=0; in_vals[i]; i++)
598 ; /* count values */
599 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
600 if (!values) return NULL;
602 for (i=0; in_vals[i]; i++) {
603 if (push_utf8_talloc(ctx, &values[i], in_vals[i]) == (size_t) -1) {
604 TALLOC_FREE(values);
605 return NULL;
608 return values;
612 Pull a (char *) array out of a UTF8-encoded values list
614 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
616 char **values;
617 int i;
619 if (!in_vals) return NULL;
620 for (i=0; in_vals[i]; i++)
621 ; /* count values */
622 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
623 if (!values) return NULL;
625 for (i=0; in_vals[i]; i++) {
626 pull_utf8_talloc(ctx, &values[i], in_vals[i]);
628 return values;
632 * Do a search with paged results. cookie must be null on the first
633 * call, and then returned on each subsequent call. It will be null
634 * again when the entire search is complete
635 * @param ads connection to ads server
636 * @param bind_path Base dn for the search
637 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
638 * @param expr Search expression - specified in local charset
639 * @param attrs Attributes to retrieve - specified in utf8 or ascii
640 * @param res ** which will contain results - free res* with ads_msgfree()
641 * @param count Number of entries retrieved on this page
642 * @param cookie The paged results cookie to be returned on subsequent calls
643 * @return status of search
645 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
646 const char *bind_path,
647 int scope, const char *expr,
648 const char **attrs, void *args,
649 LDAPMessage **res,
650 int *count, struct berval **cookie)
652 int rc, i, version;
653 char *utf8_expr, *utf8_path, **search_attrs;
654 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
655 BerElement *cookie_be = NULL;
656 struct berval *cookie_bv= NULL;
657 BerElement *ext_be = NULL;
658 struct berval *ext_bv= NULL;
660 TALLOC_CTX *ctx;
661 ads_control *external_control = (ads_control *) args;
663 *res = NULL;
665 if (!(ctx = talloc_init("ads_do_paged_search_args")))
666 return ADS_ERROR(LDAP_NO_MEMORY);
668 /* 0 means the conversion worked but the result was empty
669 so we only fail if it's -1. In any case, it always
670 at least nulls out the dest */
671 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
672 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
673 rc = LDAP_NO_MEMORY;
674 goto done;
677 if (!attrs || !(*attrs))
678 search_attrs = NULL;
679 else {
680 /* This would be the utf8-encoded version...*/
681 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
682 if (!(str_list_copy(talloc_tos(), &search_attrs, attrs))) {
683 rc = LDAP_NO_MEMORY;
684 goto done;
688 /* Paged results only available on ldap v3 or later */
689 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
690 if (version < LDAP_VERSION3) {
691 rc = LDAP_NOT_SUPPORTED;
692 goto done;
695 cookie_be = ber_alloc_t(LBER_USE_DER);
696 if (*cookie) {
697 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
698 ber_bvfree(*cookie); /* don't need it from last time */
699 *cookie = NULL;
700 } else {
701 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
703 ber_flatten(cookie_be, &cookie_bv);
704 PagedResults.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID);
705 PagedResults.ldctl_iscritical = (char) 1;
706 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
707 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
709 NoReferrals.ldctl_oid = CONST_DISCARD(char *, ADS_NO_REFERRALS_OID);
710 NoReferrals.ldctl_iscritical = (char) 0;
711 NoReferrals.ldctl_value.bv_len = 0;
712 NoReferrals.ldctl_value.bv_val = CONST_DISCARD(char *, "");
714 if (external_control &&
715 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
716 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
718 ExternalCtrl.ldctl_oid = CONST_DISCARD(char *, external_control->control);
719 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
721 /* win2k does not accept a ldctl_value beeing passed in */
723 if (external_control->val != 0) {
725 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
726 rc = LDAP_NO_MEMORY;
727 goto done;
730 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
731 rc = LDAP_NO_MEMORY;
732 goto done;
734 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
735 rc = LDAP_NO_MEMORY;
736 goto done;
739 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
740 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
742 } else {
743 ExternalCtrl.ldctl_value.bv_len = 0;
744 ExternalCtrl.ldctl_value.bv_val = NULL;
747 controls[0] = &NoReferrals;
748 controls[1] = &PagedResults;
749 controls[2] = &ExternalCtrl;
750 controls[3] = NULL;
752 } else {
753 controls[0] = &NoReferrals;
754 controls[1] = &PagedResults;
755 controls[2] = NULL;
758 /* we need to disable referrals as the openldap libs don't
759 handle them and paged results at the same time. Using them
760 together results in the result record containing the server
761 page control being removed from the result list (tridge/jmcd)
763 leaving this in despite the control that says don't generate
764 referrals, in case the server doesn't support it (jmcd)
766 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
768 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
769 search_attrs, 0, controls,
770 NULL, LDAP_NO_LIMIT,
771 (LDAPMessage **)res);
773 ber_free(cookie_be, 1);
774 ber_bvfree(cookie_bv);
776 if (rc) {
777 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
778 ldap_err2string(rc)));
779 goto done;
782 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
783 NULL, &rcontrols, 0);
785 if (!rcontrols) {
786 goto done;
789 for (i=0; rcontrols[i]; i++) {
790 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
791 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
792 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
793 &cookie_bv);
794 /* the berval is the cookie, but must be freed when
795 it is all done */
796 if (cookie_bv->bv_len) /* still more to do */
797 *cookie=ber_bvdup(cookie_bv);
798 else
799 *cookie=NULL;
800 ber_bvfree(cookie_bv);
801 ber_free(cookie_be, 1);
802 break;
805 ldap_controls_free(rcontrols);
807 done:
808 talloc_destroy(ctx);
810 if (ext_be) {
811 ber_free(ext_be, 1);
814 if (ext_bv) {
815 ber_bvfree(ext_bv);
818 /* if/when we decide to utf8-encode attrs, take out this next line */
819 TALLOC_FREE(search_attrs);
821 return ADS_ERROR(rc);
824 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
825 int scope, const char *expr,
826 const char **attrs, LDAPMessage **res,
827 int *count, struct berval **cookie)
829 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
834 * Get all results for a search. This uses ads_do_paged_search() to return
835 * all entries in a large search.
836 * @param ads connection to ads server
837 * @param bind_path Base dn for the search
838 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
839 * @param expr Search expression
840 * @param attrs Attributes to retrieve
841 * @param res ** which will contain results - free res* with ads_msgfree()
842 * @return status of search
844 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
845 int scope, const char *expr,
846 const char **attrs, void *args,
847 LDAPMessage **res)
849 struct berval *cookie = NULL;
850 int count = 0;
851 ADS_STATUS status;
853 *res = NULL;
854 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
855 &count, &cookie);
857 if (!ADS_ERR_OK(status))
858 return status;
860 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
861 while (cookie) {
862 LDAPMessage *res2 = NULL;
863 ADS_STATUS status2;
864 LDAPMessage *msg, *next;
866 status2 = ads_do_paged_search_args(ads, bind_path, scope, expr,
867 attrs, args, &res2, &count, &cookie);
869 if (!ADS_ERR_OK(status2)) break;
871 /* this relies on the way that ldap_add_result_entry() works internally. I hope
872 that this works on all ldap libs, but I have only tested with openldap */
873 for (msg = ads_first_entry(ads, res2); msg; msg = next) {
874 next = ads_next_entry(ads, msg);
875 ldap_add_result_entry((LDAPMessage **)res, msg);
877 /* note that we do not free res2, as the memory is now
878 part of the main returned list */
880 #else
881 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
882 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
883 #endif
885 return status;
888 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
889 int scope, const char *expr,
890 const char **attrs, LDAPMessage **res)
892 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
895 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
896 int scope, const char *expr,
897 const char **attrs, uint32 sd_flags,
898 LDAPMessage **res)
900 ads_control args;
902 args.control = ADS_SD_FLAGS_OID;
903 args.val = sd_flags;
904 args.critical = True;
906 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
911 * Run a function on all results for a search. Uses ads_do_paged_search() and
912 * runs the function as each page is returned, using ads_process_results()
913 * @param ads connection to ads server
914 * @param bind_path Base dn for the search
915 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
916 * @param expr Search expression - specified in local charset
917 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
918 * @param fn Function which takes attr name, values list, and data_area
919 * @param data_area Pointer which is passed to function on each call
920 * @return status of search
922 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
923 int scope, const char *expr, const char **attrs,
924 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
925 void *data_area)
927 struct berval *cookie = NULL;
928 int count = 0;
929 ADS_STATUS status;
930 LDAPMessage *res;
932 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
933 &count, &cookie);
935 if (!ADS_ERR_OK(status)) return status;
937 ads_process_results(ads, res, fn, data_area);
938 ads_msgfree(ads, res);
940 while (cookie) {
941 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
942 &res, &count, &cookie);
944 if (!ADS_ERR_OK(status)) break;
946 ads_process_results(ads, res, fn, data_area);
947 ads_msgfree(ads, res);
950 return status;
954 * Do a search with a timeout.
955 * @param ads connection to ads server
956 * @param bind_path Base dn for the search
957 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
958 * @param expr Search expression
959 * @param attrs Attributes to retrieve
960 * @param res ** which will contain results - free res* with ads_msgfree()
961 * @return status of search
963 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
964 const char *expr,
965 const char **attrs, LDAPMessage **res)
967 int rc;
968 char *utf8_expr, *utf8_path, **search_attrs = NULL;
969 TALLOC_CTX *ctx;
971 *res = NULL;
972 if (!(ctx = talloc_init("ads_do_search"))) {
973 DEBUG(1,("ads_do_search: talloc_init() failed!"));
974 return ADS_ERROR(LDAP_NO_MEMORY);
977 /* 0 means the conversion worked but the result was empty
978 so we only fail if it's negative. In any case, it always
979 at least nulls out the dest */
980 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
981 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
982 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
983 rc = LDAP_NO_MEMORY;
984 goto done;
987 if (!attrs || !(*attrs))
988 search_attrs = NULL;
989 else {
990 /* This would be the utf8-encoded version...*/
991 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
992 if (!(str_list_copy(talloc_tos(), &search_attrs, attrs)))
994 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
995 rc = LDAP_NO_MEMORY;
996 goto done;
1000 /* see the note in ads_do_paged_search - we *must* disable referrals */
1001 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1003 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1004 search_attrs, 0, NULL, NULL,
1005 LDAP_NO_LIMIT,
1006 (LDAPMessage **)res);
1008 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1009 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1010 rc = 0;
1013 done:
1014 talloc_destroy(ctx);
1015 /* if/when we decide to utf8-encode attrs, take out this next line */
1016 TALLOC_FREE(search_attrs);
1017 return ADS_ERROR(rc);
1020 * Do a general ADS search
1021 * @param ads connection to ads server
1022 * @param res ** which will contain results - free res* with ads_msgfree()
1023 * @param expr Search expression
1024 * @param attrs Attributes to retrieve
1025 * @return status of search
1027 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1028 const char *expr, const char **attrs)
1030 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1031 expr, attrs, res);
1035 * Do a search on a specific DistinguishedName
1036 * @param ads connection to ads server
1037 * @param res ** which will contain results - free res* with ads_msgfree()
1038 * @param dn DistinguishName to search
1039 * @param attrs Attributes to retrieve
1040 * @return status of search
1042 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1043 const char *dn, const char **attrs)
1045 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1046 attrs, res);
1050 * Free up memory from a ads_search
1051 * @param ads connection to ads server
1052 * @param msg Search results to free
1054 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1056 if (!msg) return;
1057 ldap_msgfree(msg);
1061 * Free up memory from various ads requests
1062 * @param ads connection to ads server
1063 * @param mem Area to free
1065 void ads_memfree(ADS_STRUCT *ads, void *mem)
1067 SAFE_FREE(mem);
1071 * Get a dn from search results
1072 * @param ads connection to ads server
1073 * @param msg Search result
1074 * @return dn string
1076 char *ads_get_dn(ADS_STRUCT *ads, LDAPMessage *msg)
1078 char *utf8_dn, *unix_dn;
1080 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1082 if (!utf8_dn) {
1083 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1084 return NULL;
1087 if (pull_utf8_allocate(&unix_dn, utf8_dn) == (size_t)-1) {
1088 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1089 utf8_dn ));
1090 return NULL;
1092 ldap_memfree(utf8_dn);
1093 return unix_dn;
1097 * Get the parent from a dn
1098 * @param dn the dn to return the parent from
1099 * @return parent dn string
1101 char *ads_parent_dn(const char *dn)
1103 char *p;
1105 if (dn == NULL) {
1106 return NULL;
1109 p = strchr(dn, ',');
1111 if (p == NULL) {
1112 return NULL;
1115 return p+1;
1119 * Find a machine account given a hostname
1120 * @param ads connection to ads server
1121 * @param res ** which will contain results - free res* with ads_msgfree()
1122 * @param host Hostname to search for
1123 * @return status of search
1125 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1126 const char *machine)
1128 ADS_STATUS status;
1129 char *expr;
1130 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1132 *res = NULL;
1134 /* the easiest way to find a machine account anywhere in the tree
1135 is to look for hostname$ */
1136 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1137 DEBUG(1, ("asprintf failed!\n"));
1138 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1141 status = ads_search(ads, res, expr, attrs);
1142 SAFE_FREE(expr);
1143 return status;
1147 * Initialize a list of mods to be used in a modify request
1148 * @param ctx An initialized TALLOC_CTX
1149 * @return allocated ADS_MODLIST
1151 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1153 #define ADS_MODLIST_ALLOC_SIZE 10
1154 LDAPMod **mods;
1156 if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1157 /* -1 is safety to make sure we don't go over the end.
1158 need to reset it to NULL before doing ldap modify */
1159 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1161 return (ADS_MODLIST)mods;
1166 add an attribute to the list, with values list already constructed
1168 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1169 int mod_op, const char *name,
1170 const void *_invals)
1172 const void **invals = (const void **)_invals;
1173 int curmod;
1174 LDAPMod **modlist = (LDAPMod **) *mods;
1175 struct berval **ber_values = NULL;
1176 char **char_values = NULL;
1178 if (!invals) {
1179 mod_op = LDAP_MOD_DELETE;
1180 } else {
1181 if (mod_op & LDAP_MOD_BVALUES)
1182 ber_values = ads_dup_values(ctx,
1183 (const struct berval **)invals);
1184 else
1185 char_values = ads_push_strvals(ctx,
1186 (const char **) invals);
1189 /* find the first empty slot */
1190 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1191 curmod++);
1192 if (modlist[curmod] == (LDAPMod *) -1) {
1193 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
1194 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1195 return ADS_ERROR(LDAP_NO_MEMORY);
1196 memset(&modlist[curmod], 0,
1197 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1198 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1199 *mods = (ADS_MODLIST)modlist;
1202 if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
1203 return ADS_ERROR(LDAP_NO_MEMORY);
1204 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1205 if (mod_op & LDAP_MOD_BVALUES) {
1206 modlist[curmod]->mod_bvalues = ber_values;
1207 } else if (mod_op & LDAP_MOD_DELETE) {
1208 modlist[curmod]->mod_values = NULL;
1209 } else {
1210 modlist[curmod]->mod_values = char_values;
1213 modlist[curmod]->mod_op = mod_op;
1214 return ADS_ERROR(LDAP_SUCCESS);
1218 * Add a single string value to a mod list
1219 * @param ctx An initialized TALLOC_CTX
1220 * @param mods An initialized ADS_MODLIST
1221 * @param name The attribute name to add
1222 * @param val The value to add - NULL means DELETE
1223 * @return ADS STATUS indicating success of add
1225 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1226 const char *name, const char *val)
1228 const char *values[2];
1230 values[0] = val;
1231 values[1] = NULL;
1233 if (!val)
1234 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1235 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1239 * Add an array of string values to a mod list
1240 * @param ctx An initialized TALLOC_CTX
1241 * @param mods An initialized ADS_MODLIST
1242 * @param name The attribute name to add
1243 * @param vals The array of string values to add - NULL means DELETE
1244 * @return ADS STATUS indicating success of add
1246 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1247 const char *name, const char **vals)
1249 if (!vals)
1250 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1251 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1252 name, (const void **) vals);
1255 #if 0
1257 * Add a single ber-encoded value to a mod list
1258 * @param ctx An initialized TALLOC_CTX
1259 * @param mods An initialized ADS_MODLIST
1260 * @param name The attribute name to add
1261 * @param val The value to add - NULL means DELETE
1262 * @return ADS STATUS indicating success of add
1264 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1265 const char *name, const struct berval *val)
1267 const struct berval *values[2];
1269 values[0] = val;
1270 values[1] = NULL;
1271 if (!val)
1272 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1273 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1274 name, (const void **) values);
1276 #endif
1279 * Perform an ldap modify
1280 * @param ads connection to ads server
1281 * @param mod_dn DistinguishedName to modify
1282 * @param mods list of modifications to perform
1283 * @return status of modify
1285 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1287 int ret,i;
1288 char *utf8_dn = NULL;
1290 this control is needed to modify that contains a currently
1291 non-existent attribute (but allowable for the object) to run
1293 LDAPControl PermitModify = {
1294 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
1295 {0, NULL},
1296 (char) 1};
1297 LDAPControl *controls[2];
1299 controls[0] = &PermitModify;
1300 controls[1] = NULL;
1302 if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) {
1303 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1306 /* find the end of the list, marked by NULL or -1 */
1307 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1308 /* make sure the end of the list is NULL */
1309 mods[i] = NULL;
1310 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1311 (LDAPMod **) mods, controls, NULL);
1312 SAFE_FREE(utf8_dn);
1313 return ADS_ERROR(ret);
1317 * Perform an ldap add
1318 * @param ads connection to ads server
1319 * @param new_dn DistinguishedName to add
1320 * @param mods list of attributes and values for DN
1321 * @return status of add
1323 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1325 int ret, i;
1326 char *utf8_dn = NULL;
1328 if (push_utf8_allocate(&utf8_dn, new_dn) == -1) {
1329 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1330 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1333 /* find the end of the list, marked by NULL or -1 */
1334 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1335 /* make sure the end of the list is NULL */
1336 mods[i] = NULL;
1338 ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1339 SAFE_FREE(utf8_dn);
1340 return ADS_ERROR(ret);
1344 * Delete a DistinguishedName
1345 * @param ads connection to ads server
1346 * @param new_dn DistinguishedName to delete
1347 * @return status of delete
1349 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1351 int ret;
1352 char *utf8_dn = NULL;
1353 if (push_utf8_allocate(&utf8_dn, del_dn) == -1) {
1354 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1355 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1358 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1359 SAFE_FREE(utf8_dn);
1360 return ADS_ERROR(ret);
1364 * Build an org unit string
1365 * if org unit is Computers or blank then assume a container, otherwise
1366 * assume a / separated list of organisational units.
1367 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1368 * @param ads connection to ads server
1369 * @param org_unit Organizational unit
1370 * @return org unit string - caller must free
1372 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1374 char *ret = NULL;
1376 if (!org_unit || !*org_unit) {
1378 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1380 /* samba4 might not yet respond to a wellknownobject-query */
1381 return ret ? ret : SMB_STRDUP("cn=Computers");
1384 if (strequal(org_unit, "Computers")) {
1385 return SMB_STRDUP("cn=Computers");
1388 /* jmcd: removed "\\" from the separation chars, because it is
1389 needed as an escape for chars like '#' which are valid in an
1390 OU name */
1391 return ads_build_path(org_unit, "/", "ou=", 1);
1395 * Get a org unit string for a well-known GUID
1396 * @param ads connection to ads server
1397 * @param wknguid Well known GUID
1398 * @return org unit string - caller must free
1400 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1402 ADS_STATUS status;
1403 LDAPMessage *res = NULL;
1404 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1405 **bind_dn_exp = NULL;
1406 const char *attrs[] = {"distinguishedName", NULL};
1407 int new_ln, wkn_ln, bind_ln, i;
1409 if (wknguid == NULL) {
1410 return NULL;
1413 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1414 DEBUG(1, ("asprintf failed!\n"));
1415 return NULL;
1418 status = ads_search_dn(ads, &res, base, attrs);
1419 if (!ADS_ERR_OK(status)) {
1420 DEBUG(1,("Failed while searching for: %s\n", base));
1421 goto out;
1424 if (ads_count_replies(ads, res) != 1) {
1425 goto out;
1428 /* substitute the bind-path from the well-known-guid-search result */
1429 wkn_dn = ads_get_dn(ads, res);
1430 if (!wkn_dn) {
1431 goto out;
1434 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1435 if (!wkn_dn_exp) {
1436 goto out;
1439 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1440 if (!bind_dn_exp) {
1441 goto out;
1444 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1446 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1449 new_ln = wkn_ln - bind_ln;
1451 ret = SMB_STRDUP(wkn_dn_exp[0]);
1452 if (!ret) {
1453 goto out;
1456 for (i=1; i < new_ln; i++) {
1457 char *s = NULL;
1459 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1460 SAFE_FREE(ret);
1461 goto out;
1464 SAFE_FREE(ret);
1465 ret = SMB_STRDUP(s);
1466 free(s);
1467 if (!ret) {
1468 goto out;
1472 out:
1473 SAFE_FREE(base);
1474 ads_msgfree(ads, res);
1475 ads_memfree(ads, wkn_dn);
1476 if (wkn_dn_exp) {
1477 ldap_value_free(wkn_dn_exp);
1479 if (bind_dn_exp) {
1480 ldap_value_free(bind_dn_exp);
1483 return ret;
1487 * Adds (appends) an item to an attribute array, rather then
1488 * replacing the whole list
1489 * @param ctx An initialized TALLOC_CTX
1490 * @param mods An initialized ADS_MODLIST
1491 * @param name name of the ldap attribute to append to
1492 * @param vals an array of values to add
1493 * @return status of addition
1496 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1497 const char *name, const char **vals)
1499 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1500 (const void *) vals);
1504 * Determines the computer account's current KVNO via an LDAP lookup
1505 * @param ads An initialized ADS_STRUCT
1506 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1507 * @return the kvno for the computer account, or -1 in case of a failure.
1510 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *machine_name)
1512 LDAPMessage *res = NULL;
1513 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1514 char *filter;
1515 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1516 char *dn_string = NULL;
1517 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1519 DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name));
1520 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1521 return kvno;
1523 ret = ads_search(ads, &res, filter, attrs);
1524 SAFE_FREE(filter);
1525 if (!ADS_ERR_OK(ret) && ads_count_replies(ads, res)) {
1526 DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name));
1527 ads_msgfree(ads, res);
1528 return kvno;
1531 dn_string = ads_get_dn(ads, res);
1532 if (!dn_string) {
1533 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1534 ads_msgfree(ads, res);
1535 return kvno;
1537 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1538 ads_memfree(ads, dn_string);
1540 /* ---------------------------------------------------------
1541 * 0 is returned as a default KVNO from this point on...
1542 * This is done because Windows 2000 does not support key
1543 * version numbers. Chances are that a failure in the next
1544 * step is simply due to Windows 2000 being used for a
1545 * domain controller. */
1546 kvno = 0;
1548 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1549 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1550 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1551 ads_msgfree(ads, res);
1552 return kvno;
1555 /* Success */
1556 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1557 ads_msgfree(ads, res);
1558 return kvno;
1562 * This clears out all registered spn's for a given hostname
1563 * @param ads An initilaized ADS_STRUCT
1564 * @param machine_name the NetBIOS name of the computer.
1565 * @return 0 upon success, non-zero otherwise.
1568 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1570 TALLOC_CTX *ctx;
1571 LDAPMessage *res = NULL;
1572 ADS_MODLIST mods;
1573 const char *servicePrincipalName[1] = {NULL};
1574 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1575 char *dn_string = NULL;
1577 ret = ads_find_machine_acct(ads, &res, machine_name);
1578 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1579 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1580 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1581 ads_msgfree(ads, res);
1582 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1585 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1586 ctx = talloc_init("ads_clear_service_principal_names");
1587 if (!ctx) {
1588 ads_msgfree(ads, res);
1589 return ADS_ERROR(LDAP_NO_MEMORY);
1592 if (!(mods = ads_init_mods(ctx))) {
1593 talloc_destroy(ctx);
1594 ads_msgfree(ads, res);
1595 return ADS_ERROR(LDAP_NO_MEMORY);
1597 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1598 if (!ADS_ERR_OK(ret)) {
1599 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1600 ads_msgfree(ads, res);
1601 talloc_destroy(ctx);
1602 return ret;
1604 dn_string = ads_get_dn(ads, res);
1605 if (!dn_string) {
1606 talloc_destroy(ctx);
1607 ads_msgfree(ads, res);
1608 return ADS_ERROR(LDAP_NO_MEMORY);
1610 ret = ads_gen_mod(ads, dn_string, mods);
1611 ads_memfree(ads,dn_string);
1612 if (!ADS_ERR_OK(ret)) {
1613 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1614 machine_name));
1615 ads_msgfree(ads, res);
1616 talloc_destroy(ctx);
1617 return ret;
1620 ads_msgfree(ads, res);
1621 talloc_destroy(ctx);
1622 return ret;
1626 * This adds a service principal name to an existing computer account
1627 * (found by hostname) in AD.
1628 * @param ads An initialized ADS_STRUCT
1629 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1630 * @param my_fqdn The fully qualified DNS name of the machine
1631 * @param spn A string of the service principal to add, i.e. 'host'
1632 * @return 0 upon sucess, or non-zero if a failure occurs
1635 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1636 const char *my_fqdn, const char *spn)
1638 ADS_STATUS ret;
1639 TALLOC_CTX *ctx;
1640 LDAPMessage *res = NULL;
1641 char *psp1, *psp2;
1642 ADS_MODLIST mods;
1643 char *dn_string = NULL;
1644 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1646 ret = ads_find_machine_acct(ads, &res, machine_name);
1647 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1648 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1649 machine_name));
1650 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1651 spn, machine_name, ads->config.realm));
1652 ads_msgfree(ads, res);
1653 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1656 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1657 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1658 ads_msgfree(ads, res);
1659 return ADS_ERROR(LDAP_NO_MEMORY);
1662 /* add short name spn */
1664 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1665 talloc_destroy(ctx);
1666 ads_msgfree(ads, res);
1667 return ADS_ERROR(LDAP_NO_MEMORY);
1669 strupper_m(psp1);
1670 strlower_m(&psp1[strlen(spn)]);
1671 servicePrincipalName[0] = psp1;
1673 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1674 psp1, machine_name));
1677 /* add fully qualified spn */
1679 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1680 ret = ADS_ERROR(LDAP_NO_MEMORY);
1681 goto out;
1683 strupper_m(psp2);
1684 strlower_m(&psp2[strlen(spn)]);
1685 servicePrincipalName[1] = psp2;
1687 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1688 psp2, machine_name));
1690 if ( (mods = ads_init_mods(ctx)) == NULL ) {
1691 ret = ADS_ERROR(LDAP_NO_MEMORY);
1692 goto out;
1695 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1696 if (!ADS_ERR_OK(ret)) {
1697 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1698 goto out;
1701 if ( (dn_string = ads_get_dn(ads, res)) == NULL ) {
1702 ret = ADS_ERROR(LDAP_NO_MEMORY);
1703 goto out;
1706 ret = ads_gen_mod(ads, dn_string, mods);
1707 ads_memfree(ads,dn_string);
1708 if (!ADS_ERR_OK(ret)) {
1709 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1710 goto out;
1713 out:
1714 TALLOC_FREE( ctx );
1715 ads_msgfree(ads, res);
1716 return ret;
1720 * adds a machine account to the ADS server
1721 * @param ads An intialized ADS_STRUCT
1722 * @param machine_name - the NetBIOS machine name of this account.
1723 * @param account_type A number indicating the type of account to create
1724 * @param org_unit The LDAP path in which to place this account
1725 * @return 0 upon success, or non-zero otherwise
1728 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1729 const char *org_unit)
1731 ADS_STATUS ret;
1732 char *samAccountName, *controlstr;
1733 TALLOC_CTX *ctx;
1734 ADS_MODLIST mods;
1735 char *machine_escaped = NULL;
1736 char *new_dn;
1737 const char *objectClass[] = {"top", "person", "organizationalPerson",
1738 "user", "computer", NULL};
1739 LDAPMessage *res = NULL;
1740 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
1741 UF_DONT_EXPIRE_PASSWD |\
1742 UF_ACCOUNTDISABLE );
1744 if (!(ctx = talloc_init("ads_add_machine_acct")))
1745 return ADS_ERROR(LDAP_NO_MEMORY);
1747 ret = ADS_ERROR(LDAP_NO_MEMORY);
1749 machine_escaped = escape_rdn_val_string_alloc(machine_name);
1750 if (!machine_escaped) {
1751 goto done;
1754 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
1755 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
1757 if ( !new_dn || !samAccountName ) {
1758 goto done;
1761 #ifndef ENCTYPE_ARCFOUR_HMAC
1762 acct_control |= UF_USE_DES_KEY_ONLY;
1763 #endif
1765 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
1766 goto done;
1769 if (!(mods = ads_init_mods(ctx))) {
1770 goto done;
1773 ads_mod_str(ctx, &mods, "cn", machine_name);
1774 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1775 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1776 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1778 ret = ads_gen_add(ads, new_dn, mods);
1780 done:
1781 SAFE_FREE(machine_escaped);
1782 ads_msgfree(ads, res);
1783 talloc_destroy(ctx);
1785 return ret;
1789 * move a machine account to another OU on the ADS server
1790 * @param ads - An intialized ADS_STRUCT
1791 * @param machine_name - the NetBIOS machine name of this account.
1792 * @param org_unit - The LDAP path in which to place this account
1793 * @param moved - whether we moved the machine account (optional)
1794 * @return 0 upon success, or non-zero otherwise
1797 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1798 const char *org_unit, bool *moved)
1800 ADS_STATUS rc;
1801 int ldap_status;
1802 LDAPMessage *res = NULL;
1803 char *filter = NULL;
1804 char *computer_dn = NULL;
1805 char *parent_dn;
1806 char *computer_rdn = NULL;
1807 bool need_move = False;
1809 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1810 rc = ADS_ERROR(LDAP_NO_MEMORY);
1811 goto done;
1814 /* Find pre-existing machine */
1815 rc = ads_search(ads, &res, filter, NULL);
1816 if (!ADS_ERR_OK(rc)) {
1817 goto done;
1820 computer_dn = ads_get_dn(ads, res);
1821 if (!computer_dn) {
1822 rc = ADS_ERROR(LDAP_NO_MEMORY);
1823 goto done;
1826 parent_dn = ads_parent_dn(computer_dn);
1827 if (strequal(parent_dn, org_unit)) {
1828 goto done;
1831 need_move = True;
1833 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
1834 rc = ADS_ERROR(LDAP_NO_MEMORY);
1835 goto done;
1838 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
1839 org_unit, 1, NULL, NULL);
1840 rc = ADS_ERROR(ldap_status);
1842 done:
1843 ads_msgfree(ads, res);
1844 SAFE_FREE(filter);
1845 SAFE_FREE(computer_dn);
1846 SAFE_FREE(computer_rdn);
1848 if (!ADS_ERR_OK(rc)) {
1849 need_move = False;
1852 if (moved) {
1853 *moved = need_move;
1856 return rc;
1860 dump a binary result from ldap
1862 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
1864 int i, j;
1865 for (i=0; values[i]; i++) {
1866 printf("%s: ", field);
1867 for (j=0; j<values[i]->bv_len; j++) {
1868 printf("%02X", (unsigned char)values[i]->bv_val[j]);
1870 printf("\n");
1874 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
1876 int i;
1877 for (i=0; values[i]; i++) {
1879 UUID_FLAT guid;
1880 struct GUID tmp;
1882 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1883 smb_uuid_unpack(guid, &tmp);
1884 printf("%s: %s\n", field, smb_uuid_string(talloc_tos(), tmp));
1889 dump a sid result from ldap
1891 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
1893 int i;
1894 for (i=0; values[i]; i++) {
1895 DOM_SID sid;
1896 fstring tmp;
1897 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1898 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
1903 dump ntSecurityDescriptor
1905 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
1907 TALLOC_CTX *frame = talloc_stackframe();
1908 struct security_descriptor *psd;
1909 NTSTATUS status;
1911 status = unmarshall_sec_desc(talloc_tos(), (uint8 *)values[0]->bv_val,
1912 values[0]->bv_len, &psd);
1913 if (!NT_STATUS_IS_OK(status)) {
1914 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
1915 nt_errstr(status)));
1916 TALLOC_FREE(frame);
1917 return;
1920 if (psd) {
1921 ads_disp_sd(ads, talloc_tos(), psd);
1924 TALLOC_FREE(frame);
1928 dump a string result from ldap
1930 static void dump_string(const char *field, char **values)
1932 int i;
1933 for (i=0; values[i]; i++) {
1934 printf("%s: %s\n", field, values[i]);
1939 dump a field from LDAP on stdout
1940 used for debugging
1943 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
1945 const struct {
1946 const char *name;
1947 bool string;
1948 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
1949 } handlers[] = {
1950 {"objectGUID", False, dump_guid},
1951 {"netbootGUID", False, dump_guid},
1952 {"nTSecurityDescriptor", False, dump_sd},
1953 {"dnsRecord", False, dump_binary},
1954 {"objectSid", False, dump_sid},
1955 {"tokenGroups", False, dump_sid},
1956 {"tokenGroupsNoGCAcceptable", False, dump_sid},
1957 {"tokengroupsGlobalandUniversal", False, dump_sid},
1958 {"mS-DS-CreatorSID", False, dump_sid},
1959 {"msExchMailboxGuid", False, dump_guid},
1960 {NULL, True, NULL}
1962 int i;
1964 if (!field) { /* must be end of an entry */
1965 printf("\n");
1966 return False;
1969 for (i=0; handlers[i].name; i++) {
1970 if (StrCaseCmp(handlers[i].name, field) == 0) {
1971 if (!values) /* first time, indicate string or not */
1972 return handlers[i].string;
1973 handlers[i].handler(ads, field, (struct berval **) values);
1974 break;
1977 if (!handlers[i].name) {
1978 if (!values) /* first time, indicate string conversion */
1979 return True;
1980 dump_string(field, (char **)values);
1982 return False;
1986 * Dump a result from LDAP on stdout
1987 * used for debugging
1988 * @param ads connection to ads server
1989 * @param res Results to dump
1992 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
1994 ads_process_results(ads, res, ads_dump_field, NULL);
1998 * Walk through results, calling a function for each entry found.
1999 * The function receives a field name, a berval * array of values,
2000 * and a data area passed through from the start. The function is
2001 * called once with null for field and values at the end of each
2002 * entry.
2003 * @param ads connection to ads server
2004 * @param res Results to process
2005 * @param fn Function for processing each result
2006 * @param data_area user-defined area to pass to function
2008 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2009 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2010 void *data_area)
2012 LDAPMessage *msg;
2013 TALLOC_CTX *ctx;
2015 if (!(ctx = talloc_init("ads_process_results")))
2016 return;
2018 for (msg = ads_first_entry(ads, res); msg;
2019 msg = ads_next_entry(ads, msg)) {
2020 char *utf8_field;
2021 BerElement *b;
2023 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2024 (LDAPMessage *)msg,&b);
2025 utf8_field;
2026 utf8_field=ldap_next_attribute(ads->ldap.ld,
2027 (LDAPMessage *)msg,b)) {
2028 struct berval **ber_vals;
2029 char **str_vals, **utf8_vals;
2030 char *field;
2031 bool string;
2033 pull_utf8_talloc(ctx, &field, utf8_field);
2034 string = fn(ads, field, NULL, data_area);
2036 if (string) {
2037 utf8_vals = ldap_get_values(ads->ldap.ld,
2038 (LDAPMessage *)msg, field);
2039 str_vals = ads_pull_strvals(ctx,
2040 (const char **) utf8_vals);
2041 fn(ads, field, (void **) str_vals, data_area);
2042 ldap_value_free(utf8_vals);
2043 } else {
2044 ber_vals = ldap_get_values_len(ads->ldap.ld,
2045 (LDAPMessage *)msg, field);
2046 fn(ads, field, (void **) ber_vals, data_area);
2048 ldap_value_free_len(ber_vals);
2050 ldap_memfree(utf8_field);
2052 ber_free(b, 0);
2053 talloc_free_children(ctx);
2054 fn(ads, NULL, NULL, data_area); /* completed an entry */
2057 talloc_destroy(ctx);
2061 * count how many replies are in a LDAPMessage
2062 * @param ads connection to ads server
2063 * @param res Results to count
2064 * @return number of replies
2066 int ads_count_replies(ADS_STRUCT *ads, void *res)
2068 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2072 * pull the first entry from a ADS result
2073 * @param ads connection to ads server
2074 * @param res Results of search
2075 * @return first entry from result
2077 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2079 return ldap_first_entry(ads->ldap.ld, res);
2083 * pull the next entry from a ADS result
2084 * @param ads connection to ads server
2085 * @param res Results of search
2086 * @return next entry from result
2088 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2090 return ldap_next_entry(ads->ldap.ld, res);
2094 * pull a single string from a ADS result
2095 * @param ads connection to ads server
2096 * @param mem_ctx TALLOC_CTX to use for allocating result string
2097 * @param msg Results of search
2098 * @param field Attribute to retrieve
2099 * @return Result string in talloc context
2101 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2102 const char *field)
2104 char **values;
2105 char *ret = NULL;
2106 char *ux_string;
2107 size_t rc;
2109 values = ldap_get_values(ads->ldap.ld, msg, field);
2110 if (!values)
2111 return NULL;
2113 if (values[0]) {
2114 rc = pull_utf8_talloc(mem_ctx, &ux_string,
2115 values[0]);
2116 if (rc != (size_t)-1)
2117 ret = ux_string;
2120 ldap_value_free(values);
2121 return ret;
2125 * pull an array of strings from a ADS result
2126 * @param ads connection to ads server
2127 * @param mem_ctx TALLOC_CTX to use for allocating result string
2128 * @param msg Results of search
2129 * @param field Attribute to retrieve
2130 * @return Result strings in talloc context
2132 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2133 LDAPMessage *msg, const char *field,
2134 size_t *num_values)
2136 char **values;
2137 char **ret = NULL;
2138 int i;
2140 values = ldap_get_values(ads->ldap.ld, msg, field);
2141 if (!values)
2142 return NULL;
2144 *num_values = ldap_count_values(values);
2146 ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
2147 if (!ret) {
2148 ldap_value_free(values);
2149 return NULL;
2152 for (i=0;i<*num_values;i++) {
2153 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
2154 ldap_value_free(values);
2155 return NULL;
2158 ret[i] = NULL;
2160 ldap_value_free(values);
2161 return ret;
2165 * pull an array of strings from a ADS result
2166 * (handle large multivalue attributes with range retrieval)
2167 * @param ads connection to ads server
2168 * @param mem_ctx TALLOC_CTX to use for allocating result string
2169 * @param msg Results of search
2170 * @param field Attribute to retrieve
2171 * @param current_strings strings returned by a previous call to this function
2172 * @param next_attribute The next query should ask for this attribute
2173 * @param num_values How many values did we get this time?
2174 * @param more_values Are there more values to get?
2175 * @return Result strings in talloc context
2177 char **ads_pull_strings_range(ADS_STRUCT *ads,
2178 TALLOC_CTX *mem_ctx,
2179 LDAPMessage *msg, const char *field,
2180 char **current_strings,
2181 const char **next_attribute,
2182 size_t *num_strings,
2183 bool *more_strings)
2185 char *attr;
2186 char *expected_range_attrib, *range_attr;
2187 BerElement *ptr = NULL;
2188 char **strings;
2189 char **new_strings;
2190 size_t num_new_strings;
2191 unsigned long int range_start;
2192 unsigned long int range_end;
2194 /* we might have been given the whole lot anyway */
2195 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2196 *more_strings = False;
2197 return strings;
2200 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2202 /* look for Range result */
2203 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2204 attr;
2205 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2206 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2207 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2208 range_attr = attr;
2209 break;
2211 ldap_memfree(attr);
2213 if (!attr) {
2214 ber_free(ptr, 0);
2215 /* nothing here - this field is just empty */
2216 *more_strings = False;
2217 return NULL;
2220 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2221 &range_start, &range_end) == 2) {
2222 *more_strings = True;
2223 } else {
2224 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2225 &range_start) == 1) {
2226 *more_strings = False;
2227 } else {
2228 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2229 range_attr));
2230 ldap_memfree(range_attr);
2231 *more_strings = False;
2232 return NULL;
2236 if ((*num_strings) != range_start) {
2237 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2238 " - aborting range retreival\n",
2239 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2240 ldap_memfree(range_attr);
2241 *more_strings = False;
2242 return NULL;
2245 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2247 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2248 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2249 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2250 range_attr, (unsigned long int)range_end - range_start + 1,
2251 (unsigned long int)num_new_strings));
2252 ldap_memfree(range_attr);
2253 *more_strings = False;
2254 return NULL;
2257 strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2258 *num_strings + num_new_strings);
2260 if (strings == NULL) {
2261 ldap_memfree(range_attr);
2262 *more_strings = False;
2263 return NULL;
2266 if (new_strings && num_new_strings) {
2267 memcpy(&strings[*num_strings], new_strings,
2268 sizeof(*new_strings) * num_new_strings);
2271 (*num_strings) += num_new_strings;
2273 if (*more_strings) {
2274 *next_attribute = talloc_asprintf(mem_ctx,
2275 "%s;range=%d-*",
2276 field,
2277 (int)*num_strings);
2279 if (!*next_attribute) {
2280 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2281 ldap_memfree(range_attr);
2282 *more_strings = False;
2283 return NULL;
2287 ldap_memfree(range_attr);
2289 return strings;
2293 * pull a single uint32 from a ADS result
2294 * @param ads connection to ads server
2295 * @param msg Results of search
2296 * @param field Attribute to retrieve
2297 * @param v Pointer to int to store result
2298 * @return boolean inidicating success
2300 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2301 uint32 *v)
2303 char **values;
2305 values = ldap_get_values(ads->ldap.ld, msg, field);
2306 if (!values)
2307 return False;
2308 if (!values[0]) {
2309 ldap_value_free(values);
2310 return False;
2313 *v = atoi(values[0]);
2314 ldap_value_free(values);
2315 return True;
2319 * pull a single objectGUID from an ADS result
2320 * @param ads connection to ADS server
2321 * @param msg results of search
2322 * @param guid 37-byte area to receive text guid
2323 * @return boolean indicating success
2325 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2327 char **values;
2328 UUID_FLAT flat_guid;
2330 values = ldap_get_values(ads->ldap.ld, msg, "objectGUID");
2331 if (!values)
2332 return False;
2334 if (values[0]) {
2335 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2336 smb_uuid_unpack(flat_guid, guid);
2337 ldap_value_free(values);
2338 return True;
2340 ldap_value_free(values);
2341 return False;
2347 * pull a single DOM_SID from a ADS result
2348 * @param ads connection to ads server
2349 * @param msg Results of search
2350 * @param field Attribute to retrieve
2351 * @param sid Pointer to sid to store result
2352 * @return boolean inidicating success
2354 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2355 DOM_SID *sid)
2357 struct berval **values;
2358 bool ret = False;
2360 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2362 if (!values)
2363 return False;
2365 if (values[0])
2366 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2368 ldap_value_free_len(values);
2369 return ret;
2373 * pull an array of DOM_SIDs from a ADS result
2374 * @param ads connection to ads server
2375 * @param mem_ctx TALLOC_CTX for allocating sid array
2376 * @param msg Results of search
2377 * @param field Attribute to retrieve
2378 * @param sids pointer to sid array to allocate
2379 * @return the count of SIDs pulled
2381 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2382 LDAPMessage *msg, const char *field, DOM_SID **sids)
2384 struct berval **values;
2385 bool ret;
2386 int count, i;
2388 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2390 if (!values)
2391 return 0;
2393 for (i=0; values[i]; i++)
2394 /* nop */ ;
2396 if (i) {
2397 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2398 if (!(*sids)) {
2399 ldap_value_free_len(values);
2400 return 0;
2402 } else {
2403 (*sids) = NULL;
2406 count = 0;
2407 for (i=0; values[i]; i++) {
2408 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2409 if (ret) {
2410 DEBUG(10, ("pulling SID: %s\n",
2411 sid_string_dbg(&(*sids)[count])));
2412 count++;
2416 ldap_value_free_len(values);
2417 return count;
2421 * pull a SEC_DESC from a ADS result
2422 * @param ads connection to ads server
2423 * @param mem_ctx TALLOC_CTX for allocating sid array
2424 * @param msg Results of search
2425 * @param field Attribute to retrieve
2426 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2427 * @return boolean inidicating success
2429 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2430 LDAPMessage *msg, const char *field, SEC_DESC **sd)
2432 struct berval **values;
2433 bool ret = true;
2435 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2437 if (!values) return false;
2439 if (values[0]) {
2440 NTSTATUS status;
2441 status = unmarshall_sec_desc(mem_ctx,
2442 (uint8 *)values[0]->bv_val,
2443 values[0]->bv_len, sd);
2444 if (!NT_STATUS_IS_OK(status)) {
2445 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2446 nt_errstr(status)));
2447 ret = false;
2451 ldap_value_free_len(values);
2452 return ret;
2456 * in order to support usernames longer than 21 characters we need to
2457 * use both the sAMAccountName and the userPrincipalName attributes
2458 * It seems that not all users have the userPrincipalName attribute set
2460 * @param ads connection to ads server
2461 * @param mem_ctx TALLOC_CTX for allocating sid array
2462 * @param msg Results of search
2463 * @return the username
2465 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2466 LDAPMessage *msg)
2468 #if 0 /* JERRY */
2469 char *ret, *p;
2471 /* lookup_name() only works on the sAMAccountName to
2472 returning the username portion of userPrincipalName
2473 breaks winbindd_getpwnam() */
2475 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2476 if (ret && (p = strchr_m(ret, '@'))) {
2477 *p = 0;
2478 return ret;
2480 #endif
2481 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2486 * find the update serial number - this is the core of the ldap cache
2487 * @param ads connection to ads server
2488 * @param ads connection to ADS server
2489 * @param usn Pointer to retrieved update serial number
2490 * @return status of search
2492 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2494 const char *attrs[] = {"highestCommittedUSN", NULL};
2495 ADS_STATUS status;
2496 LDAPMessage *res;
2498 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2499 if (!ADS_ERR_OK(status))
2500 return status;
2502 if (ads_count_replies(ads, res) != 1) {
2503 ads_msgfree(ads, res);
2504 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2507 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2508 ads_msgfree(ads, res);
2509 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2512 ads_msgfree(ads, res);
2513 return ADS_SUCCESS;
2516 /* parse a ADS timestring - typical string is
2517 '20020917091222.0Z0' which means 09:12.22 17th September
2518 2002, timezone 0 */
2519 static time_t ads_parse_time(const char *str)
2521 struct tm tm;
2523 ZERO_STRUCT(tm);
2525 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2526 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2527 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2528 return 0;
2530 tm.tm_year -= 1900;
2531 tm.tm_mon -= 1;
2533 return timegm(&tm);
2536 /********************************************************************
2537 ********************************************************************/
2539 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2541 const char *attrs[] = {"currentTime", NULL};
2542 ADS_STATUS status;
2543 LDAPMessage *res;
2544 char *timestr;
2545 TALLOC_CTX *ctx;
2546 ADS_STRUCT *ads_s = ads;
2548 if (!(ctx = talloc_init("ads_current_time"))) {
2549 return ADS_ERROR(LDAP_NO_MEMORY);
2552 /* establish a new ldap tcp session if necessary */
2554 if ( !ads->ldap.ld ) {
2555 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2556 ads->server.ldap_server )) == NULL )
2558 goto done;
2560 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2561 status = ads_connect( ads_s );
2562 if ( !ADS_ERR_OK(status))
2563 goto done;
2566 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2567 if (!ADS_ERR_OK(status)) {
2568 goto done;
2571 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2572 if (!timestr) {
2573 ads_msgfree(ads_s, res);
2574 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2575 goto done;
2578 /* but save the time and offset in the original ADS_STRUCT */
2580 ads->config.current_time = ads_parse_time(timestr);
2582 if (ads->config.current_time != 0) {
2583 ads->auth.time_offset = ads->config.current_time - time(NULL);
2584 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2587 ads_msgfree(ads, res);
2589 status = ADS_SUCCESS;
2591 done:
2592 /* free any temporary ads connections */
2593 if ( ads_s != ads ) {
2594 ads_destroy( &ads_s );
2596 talloc_destroy(ctx);
2598 return status;
2601 /********************************************************************
2602 ********************************************************************/
2604 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2606 const char *attrs[] = {"domainFunctionality", NULL};
2607 ADS_STATUS status;
2608 LDAPMessage *res;
2609 ADS_STRUCT *ads_s = ads;
2611 *val = DS_DOMAIN_FUNCTION_2000;
2613 /* establish a new ldap tcp session if necessary */
2615 if ( !ads->ldap.ld ) {
2616 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2617 ads->server.ldap_server )) == NULL )
2619 goto done;
2621 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2622 status = ads_connect( ads_s );
2623 if ( !ADS_ERR_OK(status))
2624 goto done;
2627 /* If the attribute does not exist assume it is a Windows 2000
2628 functional domain */
2630 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2631 if (!ADS_ERR_OK(status)) {
2632 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2633 status = ADS_SUCCESS;
2635 goto done;
2638 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2639 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2641 DEBUG(3,("ads_domain_func_level: %d\n", *val));
2644 ads_msgfree(ads, res);
2646 done:
2647 /* free any temporary ads connections */
2648 if ( ads_s != ads ) {
2649 ads_destroy( &ads_s );
2652 return status;
2656 * find the domain sid for our domain
2657 * @param ads connection to ads server
2658 * @param sid Pointer to domain sid
2659 * @return status of search
2661 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2663 const char *attrs[] = {"objectSid", NULL};
2664 LDAPMessage *res;
2665 ADS_STATUS rc;
2667 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2668 attrs, &res);
2669 if (!ADS_ERR_OK(rc)) return rc;
2670 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2671 ads_msgfree(ads, res);
2672 return ADS_ERROR_SYSTEM(ENOENT);
2674 ads_msgfree(ads, res);
2676 return ADS_SUCCESS;
2680 * find our site name
2681 * @param ads connection to ads server
2682 * @param mem_ctx Pointer to talloc context
2683 * @param site_name Pointer to the sitename
2684 * @return status of search
2686 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2688 ADS_STATUS status;
2689 LDAPMessage *res;
2690 const char *dn, *service_name;
2691 const char *attrs[] = { "dsServiceName", NULL };
2693 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2694 if (!ADS_ERR_OK(status)) {
2695 return status;
2698 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2699 if (service_name == NULL) {
2700 ads_msgfree(ads, res);
2701 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2704 ads_msgfree(ads, res);
2706 /* go up three levels */
2707 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2708 if (dn == NULL) {
2709 return ADS_ERROR(LDAP_NO_MEMORY);
2712 *site_name = talloc_strdup(mem_ctx, dn);
2713 if (*site_name == NULL) {
2714 return ADS_ERROR(LDAP_NO_MEMORY);
2717 return status;
2719 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2724 * find the site dn where a machine resides
2725 * @param ads connection to ads server
2726 * @param mem_ctx Pointer to talloc context
2727 * @param computer_name name of the machine
2728 * @param site_name Pointer to the sitename
2729 * @return status of search
2731 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
2733 ADS_STATUS status;
2734 LDAPMessage *res;
2735 const char *parent, *filter;
2736 char *config_context = NULL;
2737 char *dn;
2739 /* shortcut a query */
2740 if (strequal(computer_name, ads->config.ldap_server_name)) {
2741 return ads_site_dn(ads, mem_ctx, site_dn);
2744 status = ads_config_path(ads, mem_ctx, &config_context);
2745 if (!ADS_ERR_OK(status)) {
2746 return status;
2749 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
2750 if (filter == NULL) {
2751 return ADS_ERROR(LDAP_NO_MEMORY);
2754 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
2755 filter, NULL, &res);
2756 if (!ADS_ERR_OK(status)) {
2757 return status;
2760 if (ads_count_replies(ads, res) != 1) {
2761 ads_msgfree(ads, res);
2762 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2765 dn = ads_get_dn(ads, res);
2766 if (dn == NULL) {
2767 ads_msgfree(ads, res);
2768 return ADS_ERROR(LDAP_NO_MEMORY);
2771 /* go up three levels */
2772 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
2773 if (parent == NULL) {
2774 ads_msgfree(ads, res);
2775 ads_memfree(ads, dn);
2776 return ADS_ERROR(LDAP_NO_MEMORY);
2779 *site_dn = talloc_strdup(mem_ctx, parent);
2780 if (*site_dn == NULL) {
2781 ads_msgfree(ads, res);
2782 ads_memfree(ads, dn);
2783 return ADS_ERROR(LDAP_NO_MEMORY);
2786 ads_memfree(ads, dn);
2787 ads_msgfree(ads, res);
2789 return status;
2793 * get the upn suffixes for a domain
2794 * @param ads connection to ads server
2795 * @param mem_ctx Pointer to talloc context
2796 * @param suffixes Pointer to an array of suffixes
2797 * @param num_suffixes Pointer to the number of suffixes
2798 * @return status of search
2800 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
2802 ADS_STATUS status;
2803 LDAPMessage *res;
2804 const char *base;
2805 char *config_context = NULL;
2806 const char *attrs[] = { "uPNSuffixes", NULL };
2808 status = ads_config_path(ads, mem_ctx, &config_context);
2809 if (!ADS_ERR_OK(status)) {
2810 return status;
2813 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
2814 if (base == NULL) {
2815 return ADS_ERROR(LDAP_NO_MEMORY);
2818 status = ads_search_dn(ads, &res, base, attrs);
2819 if (!ADS_ERR_OK(status)) {
2820 return status;
2823 if (ads_count_replies(ads, res) != 1) {
2824 ads_msgfree(ads, res);
2825 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2828 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
2829 if ((*suffixes) == NULL) {
2830 ads_msgfree(ads, res);
2831 return ADS_ERROR(LDAP_NO_MEMORY);
2834 ads_msgfree(ads, res);
2836 return status;
2840 * get the joinable ous for a domain
2841 * @param ads connection to ads server
2842 * @param mem_ctx Pointer to talloc context
2843 * @param ous Pointer to an array of ous
2844 * @param num_ous Pointer to the number of ous
2845 * @return status of search
2847 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
2848 TALLOC_CTX *mem_ctx,
2849 char ***ous,
2850 size_t *num_ous)
2852 ADS_STATUS status;
2853 LDAPMessage *res = NULL;
2854 LDAPMessage *msg = NULL;
2855 const char *attrs[] = { "dn", NULL };
2856 int count = 0;
2858 status = ads_search(ads, &res,
2859 "(|(objectClass=domain)(objectclass=organizationalUnit))",
2860 attrs);
2861 if (!ADS_ERR_OK(status)) {
2862 return status;
2865 count = ads_count_replies(ads, res);
2866 if (count < 1) {
2867 ads_msgfree(ads, res);
2868 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2871 for (msg = ads_first_entry(ads, res); msg;
2872 msg = ads_next_entry(ads, msg)) {
2874 char *dn = NULL;
2876 dn = ads_get_dn(ads, msg);
2877 if (!dn) {
2878 ads_msgfree(ads, res);
2879 return ADS_ERROR(LDAP_NO_MEMORY);
2882 if (!add_string_to_array(mem_ctx, dn,
2883 (const char ***)ous,
2884 (int *)num_ous)) {
2885 ads_memfree(ads, dn);
2886 ads_msgfree(ads, res);
2887 return ADS_ERROR(LDAP_NO_MEMORY);
2890 ads_memfree(ads, dn);
2893 ads_msgfree(ads, res);
2895 return status;
2900 * pull a DOM_SID from an extended dn string
2901 * @param mem_ctx TALLOC_CTX
2902 * @param extended_dn string
2903 * @param flags string type of extended_dn
2904 * @param sid pointer to a DOM_SID
2905 * @return boolean inidicating success
2907 bool ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
2908 const char *extended_dn,
2909 enum ads_extended_dn_flags flags,
2910 DOM_SID *sid)
2912 char *p, *q, *dn;
2914 if (!extended_dn) {
2915 return False;
2918 /* otherwise extended_dn gets stripped off */
2919 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
2920 return False;
2923 * ADS_EXTENDED_DN_HEX_STRING:
2924 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
2926 * ADS_EXTENDED_DN_STRING (only with w2k3):
2927 <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
2930 p = strchr(dn, ';');
2931 if (!p) {
2932 return False;
2935 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
2936 return False;
2939 p += strlen(";<SID=");
2941 q = strchr(p, '>');
2942 if (!q) {
2943 return False;
2946 *q = '\0';
2948 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
2950 switch (flags) {
2952 case ADS_EXTENDED_DN_STRING:
2953 if (!string_to_sid(sid, p)) {
2954 return False;
2956 break;
2957 case ADS_EXTENDED_DN_HEX_STRING: {
2958 fstring buf;
2959 size_t buf_len;
2961 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
2962 if (buf_len == 0) {
2963 return False;
2966 if (!sid_parse(buf, buf_len, sid)) {
2967 DEBUG(10,("failed to parse sid\n"));
2968 return False;
2970 break;
2972 default:
2973 DEBUG(10,("unknown extended dn format\n"));
2974 return False;
2977 return True;
2981 * pull an array of DOM_SIDs from a ADS result
2982 * @param ads connection to ads server
2983 * @param mem_ctx TALLOC_CTX for allocating sid array
2984 * @param msg Results of search
2985 * @param field Attribute to retrieve
2986 * @param flags string type of extended_dn
2987 * @param sids pointer to sid array to allocate
2988 * @return the count of SIDs pulled
2990 int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads,
2991 TALLOC_CTX *mem_ctx,
2992 LDAPMessage *msg,
2993 const char *field,
2994 enum ads_extended_dn_flags flags,
2995 DOM_SID **sids)
2997 int i;
2998 size_t dn_count;
2999 char **dn_strings;
3001 if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field,
3002 &dn_count)) == NULL) {
3003 return 0;
3006 (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, dn_count + 1);
3007 if (!(*sids)) {
3008 TALLOC_FREE(dn_strings);
3009 return 0;
3012 for (i=0; i<dn_count; i++) {
3014 if (!ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i],
3015 flags, &(*sids)[i])) {
3016 TALLOC_FREE(*sids);
3017 TALLOC_FREE(dn_strings);
3018 return 0;
3022 TALLOC_FREE(dn_strings);
3024 return dn_count;
3027 /********************************************************************
3028 ********************************************************************/
3030 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3032 LDAPMessage *res = NULL;
3033 ADS_STATUS status;
3034 int count = 0;
3035 char *name = NULL;
3037 status = ads_find_machine_acct(ads, &res, global_myname());
3038 if (!ADS_ERR_OK(status)) {
3039 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3040 global_myname()));
3041 goto out;
3044 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3045 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3046 goto out;
3049 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3050 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3053 out:
3054 ads_msgfree(ads, res);
3056 return name;
3059 /********************************************************************
3060 ********************************************************************/
3062 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3064 LDAPMessage *res = NULL;
3065 ADS_STATUS status;
3066 int count = 0;
3067 char *name = NULL;
3069 status = ads_find_machine_acct(ads, &res, machine_name);
3070 if (!ADS_ERR_OK(status)) {
3071 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3072 global_myname()));
3073 goto out;
3076 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3077 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3078 goto out;
3081 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3082 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3085 out:
3086 ads_msgfree(ads, res);
3088 return name;
3091 /********************************************************************
3092 ********************************************************************/
3094 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3096 LDAPMessage *res = NULL;
3097 ADS_STATUS status;
3098 int count = 0;
3099 char *name = NULL;
3101 status = ads_find_machine_acct(ads, &res, global_myname());
3102 if (!ADS_ERR_OK(status)) {
3103 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3104 global_myname()));
3105 goto out;
3108 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3109 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3110 goto out;
3113 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3114 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3117 out:
3118 ads_msgfree(ads, res);
3120 return name;
3123 #if 0
3125 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3128 * Join a machine to a realm
3129 * Creates the machine account and sets the machine password
3130 * @param ads connection to ads server
3131 * @param machine name of host to add
3132 * @param org_unit Organizational unit to place machine in
3133 * @return status of join
3135 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3136 uint32 account_type, const char *org_unit)
3138 ADS_STATUS status;
3139 LDAPMessage *res = NULL;
3140 char *machine;
3142 /* machine name must be lowercase */
3143 machine = SMB_STRDUP(machine_name);
3144 strlower_m(machine);
3147 status = ads_find_machine_acct(ads, (void **)&res, machine);
3148 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3149 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3150 status = ads_leave_realm(ads, machine);
3151 if (!ADS_ERR_OK(status)) {
3152 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3153 machine, ads->config.realm));
3154 return status;
3158 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3159 if (!ADS_ERR_OK(status)) {
3160 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3161 SAFE_FREE(machine);
3162 return status;
3165 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3166 if (!ADS_ERR_OK(status)) {
3167 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3168 SAFE_FREE(machine);
3169 return status;
3172 SAFE_FREE(machine);
3173 ads_msgfree(ads, res);
3175 return status;
3177 #endif
3180 * Delete a machine from the realm
3181 * @param ads connection to ads server
3182 * @param hostname Machine to remove
3183 * @return status of delete
3185 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3187 ADS_STATUS status;
3188 void *msg;
3189 LDAPMessage *res;
3190 char *hostnameDN, *host;
3191 int rc;
3192 LDAPControl ldap_control;
3193 LDAPControl * pldap_control[2] = {NULL, NULL};
3195 pldap_control[0] = &ldap_control;
3196 memset(&ldap_control, 0, sizeof(LDAPControl));
3197 ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
3199 /* hostname must be lowercase */
3200 host = SMB_STRDUP(hostname);
3201 strlower_m(host);
3203 status = ads_find_machine_acct(ads, &res, host);
3204 if (!ADS_ERR_OK(status)) {
3205 DEBUG(0, ("Host account for %s does not exist.\n", host));
3206 SAFE_FREE(host);
3207 return status;
3210 msg = ads_first_entry(ads, res);
3211 if (!msg) {
3212 SAFE_FREE(host);
3213 return ADS_ERROR_SYSTEM(ENOENT);
3216 hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
3218 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3219 if (rc) {
3220 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3221 }else {
3222 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3225 if (rc != LDAP_SUCCESS) {
3226 const char *attrs[] = { "cn", NULL };
3227 LDAPMessage *msg_sub;
3229 /* we only search with scope ONE, we do not expect any further
3230 * objects to be created deeper */
3232 status = ads_do_search_retry(ads, hostnameDN,
3233 LDAP_SCOPE_ONELEVEL,
3234 "(objectclass=*)", attrs, &res);
3236 if (!ADS_ERR_OK(status)) {
3237 SAFE_FREE(host);
3238 ads_memfree(ads, hostnameDN);
3239 return status;
3242 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3243 msg_sub = ads_next_entry(ads, msg_sub)) {
3245 char *dn = NULL;
3247 if ((dn = ads_get_dn(ads, msg_sub)) == NULL) {
3248 SAFE_FREE(host);
3249 ads_memfree(ads, hostnameDN);
3250 return ADS_ERROR(LDAP_NO_MEMORY);
3253 status = ads_del_dn(ads, dn);
3254 if (!ADS_ERR_OK(status)) {
3255 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3256 SAFE_FREE(host);
3257 ads_memfree(ads, dn);
3258 ads_memfree(ads, hostnameDN);
3259 return status;
3262 ads_memfree(ads, dn);
3265 /* there should be no subordinate objects anymore */
3266 status = ads_do_search_retry(ads, hostnameDN,
3267 LDAP_SCOPE_ONELEVEL,
3268 "(objectclass=*)", attrs, &res);
3270 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3271 SAFE_FREE(host);
3272 ads_memfree(ads, hostnameDN);
3273 return status;
3276 /* delete hostnameDN now */
3277 status = ads_del_dn(ads, hostnameDN);
3278 if (!ADS_ERR_OK(status)) {
3279 SAFE_FREE(host);
3280 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3281 ads_memfree(ads, hostnameDN);
3282 return status;
3286 ads_memfree(ads, hostnameDN);
3288 status = ads_find_machine_acct(ads, &res, host);
3289 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3290 DEBUG(3, ("Failed to remove host account.\n"));
3291 SAFE_FREE(host);
3292 return status;
3295 SAFE_FREE(host);
3296 return status;
3300 * pull all token-sids from an LDAP dn
3301 * @param ads connection to ads server
3302 * @param mem_ctx TALLOC_CTX for allocating sid array
3303 * @param dn of LDAP object
3304 * @param user_sid pointer to DOM_SID (objectSid)
3305 * @param primary_group_sid pointer to DOM_SID (self composed)
3306 * @param sids pointer to sid array to allocate
3307 * @param num_sids counter of SIDs pulled
3308 * @return status of token query
3310 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3311 TALLOC_CTX *mem_ctx,
3312 const char *dn,
3313 DOM_SID *user_sid,
3314 DOM_SID *primary_group_sid,
3315 DOM_SID **sids,
3316 size_t *num_sids)
3318 ADS_STATUS status;
3319 LDAPMessage *res = NULL;
3320 int count = 0;
3321 size_t tmp_num_sids;
3322 DOM_SID *tmp_sids;
3323 DOM_SID tmp_user_sid;
3324 DOM_SID tmp_primary_group_sid;
3325 uint32 pgid;
3326 const char *attrs[] = {
3327 "objectSid",
3328 "tokenGroups",
3329 "primaryGroupID",
3330 NULL
3333 status = ads_search_retry_dn(ads, &res, dn, attrs);
3334 if (!ADS_ERR_OK(status)) {
3335 return status;
3338 count = ads_count_replies(ads, res);
3339 if (count != 1) {
3340 ads_msgfree(ads, res);
3341 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3344 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3345 ads_msgfree(ads, res);
3346 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3349 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3350 ads_msgfree(ads, res);
3351 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3355 /* hack to compose the primary group sid without knowing the
3356 * domsid */
3358 DOM_SID domsid;
3359 uint32 dummy_rid;
3361 sid_copy(&domsid, &tmp_user_sid);
3363 if (!sid_split_rid(&domsid, &dummy_rid)) {
3364 ads_msgfree(ads, res);
3365 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3368 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3369 ads_msgfree(ads, res);
3370 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3374 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3376 if (tmp_num_sids == 0 || !tmp_sids) {
3377 ads_msgfree(ads, res);
3378 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3381 if (num_sids) {
3382 *num_sids = tmp_num_sids;
3385 if (sids) {
3386 *sids = tmp_sids;
3389 if (user_sid) {
3390 *user_sid = tmp_user_sid;
3393 if (primary_group_sid) {
3394 *primary_group_sid = tmp_primary_group_sid;
3397 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3399 ads_msgfree(ads, res);
3400 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3404 * Find a sAMAccoutName in LDAP
3405 * @param ads connection to ads server
3406 * @param mem_ctx TALLOC_CTX for allocating sid array
3407 * @param samaccountname to search
3408 * @param uac_ret uint32 pointer userAccountControl attribute value
3409 * @param dn_ret pointer to dn
3410 * @return status of token query
3412 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3413 TALLOC_CTX *mem_ctx,
3414 const char *samaccountname,
3415 uint32 *uac_ret,
3416 const char **dn_ret)
3418 ADS_STATUS status;
3419 const char *attrs[] = { "userAccountControl", NULL };
3420 const char *filter;
3421 LDAPMessage *res = NULL;
3422 char *dn = NULL;
3423 uint32 uac = 0;
3425 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3426 samaccountname);
3427 if (filter == NULL) {
3428 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3429 goto out;
3432 status = ads_do_search_all(ads, ads->config.bind_path,
3433 LDAP_SCOPE_SUBTREE,
3434 filter, attrs, &res);
3436 if (!ADS_ERR_OK(status)) {
3437 goto out;
3440 if (ads_count_replies(ads, res) != 1) {
3441 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3442 goto out;
3445 dn = ads_get_dn(ads, res);
3446 if (dn == NULL) {
3447 status = ADS_ERROR(LDAP_NO_MEMORY);
3448 goto out;
3451 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3452 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3453 goto out;
3456 if (uac_ret) {
3457 *uac_ret = uac;
3460 if (dn_ret) {
3461 *dn_ret = talloc_strdup(mem_ctx, dn);
3462 if (!*dn_ret) {
3463 status = ADS_ERROR(LDAP_NO_MEMORY);
3464 goto out;
3467 out:
3468 ads_memfree(ads, dn);
3469 ads_msgfree(ads, res);
3471 return status;
3475 * find our configuration path
3476 * @param ads connection to ads server
3477 * @param mem_ctx Pointer to talloc context
3478 * @param config_path Pointer to the config path
3479 * @return status of search
3481 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3482 TALLOC_CTX *mem_ctx,
3483 char **config_path)
3485 ADS_STATUS status;
3486 LDAPMessage *res = NULL;
3487 const char *config_context = NULL;
3488 const char *attrs[] = { "configurationNamingContext", NULL };
3490 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3491 "(objectclass=*)", attrs, &res);
3492 if (!ADS_ERR_OK(status)) {
3493 return status;
3496 config_context = ads_pull_string(ads, mem_ctx, res,
3497 "configurationNamingContext");
3498 ads_msgfree(ads, res);
3499 if (!config_context) {
3500 return ADS_ERROR(LDAP_NO_MEMORY);
3503 if (config_path) {
3504 *config_path = talloc_strdup(mem_ctx, config_context);
3505 if (!*config_path) {
3506 return ADS_ERROR(LDAP_NO_MEMORY);
3510 return ADS_ERROR(LDAP_SUCCESS);
3514 * find the displayName of an extended right
3515 * @param ads connection to ads server
3516 * @param config_path The config path
3517 * @param mem_ctx Pointer to talloc context
3518 * @param GUID struct of the rightsGUID
3519 * @return status of search
3521 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3522 const char *config_path,
3523 TALLOC_CTX *mem_ctx,
3524 const struct GUID *rights_guid)
3526 ADS_STATUS rc;
3527 LDAPMessage *res = NULL;
3528 char *expr = NULL;
3529 const char *attrs[] = { "displayName", NULL };
3530 const char *result = NULL;
3531 const char *path;
3533 if (!ads || !mem_ctx || !rights_guid) {
3534 goto done;
3537 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3538 smb_uuid_string(mem_ctx, *rights_guid));
3539 if (!expr) {
3540 goto done;
3543 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3544 if (!path) {
3545 goto done;
3548 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3549 expr, attrs, &res);
3550 if (!ADS_ERR_OK(rc)) {
3551 goto done;
3554 if (ads_count_replies(ads, res) != 1) {
3555 goto done;
3558 result = ads_pull_string(ads, mem_ctx, res, "displayName");
3560 done:
3561 ads_msgfree(ads, res);
3562 return result;
3567 * verify or build and verify an account ou
3568 * @param mem_ctx Pointer to talloc context
3569 * @param ads connection to ads server
3570 * @param account_ou
3571 * @return status of search
3574 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3575 ADS_STRUCT *ads,
3576 const char *account_ou)
3578 struct ldb_dn *name_dn = NULL;
3579 const char *name = NULL;
3580 char *ou_string = NULL;
3582 name_dn = ldb_dn_explode(mem_ctx, account_ou);
3583 if (name_dn) {
3584 return ADS_SUCCESS;
3587 ou_string = ads_ou_string(ads, account_ou);
3588 if (!ou_string) {
3589 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3592 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3593 ads->config.bind_path);
3594 SAFE_FREE(ou_string);
3595 if (!name) {
3596 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3599 name_dn = ldb_dn_explode(mem_ctx, name);
3600 if (!name_dn) {
3601 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3604 account_ou = talloc_strdup(mem_ctx, name);
3605 if (!account_ou) {
3606 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3609 return ADS_SUCCESS;
3612 #endif