libads: add ads_get_machine_kvno() to make ads_get_kvno() a bit more generic.
[Samba/gbeck.git] / source3 / libads / ldap.c
blob7b9e51068b7b59bf3c9e3b74af0eb1d1e2d29e0f
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_5(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);
252 sitename_store( cldap_reply.dns_domain, cldap_reply.client_site);
254 ret = true;
255 out:
256 SAFE_FREE(srv);
257 TALLOC_FREE(mem_ctx);
259 return ret;
262 /**********************************************************************
263 Try to find an AD dc using our internal name resolution routines
264 Try the realm first and then then workgroup name if netbios is not
265 disabled
266 **********************************************************************/
268 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
270 const char *c_realm;
271 int count, i=0;
272 struct ip_service *ip_list;
273 const char *realm;
274 bool got_realm = False;
275 bool use_own_domain = False;
276 char *sitename;
277 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
279 /* if the realm and workgroup are both empty, assume they are ours */
281 /* realm */
282 c_realm = ads->server.realm;
284 if ( !c_realm || !*c_realm ) {
285 /* special case where no realm and no workgroup means our own */
286 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
287 use_own_domain = True;
288 c_realm = lp_realm();
292 if (c_realm && *c_realm)
293 got_realm = True;
295 /* we need to try once with the realm name and fallback to the
296 netbios domain name if we fail (if netbios has not been disabled */
298 if ( !got_realm && !lp_disable_netbios() ) {
299 c_realm = ads->server.workgroup;
300 if (!c_realm || !*c_realm) {
301 if ( use_own_domain )
302 c_realm = lp_workgroup();
305 if ( !c_realm || !*c_realm ) {
306 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
307 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
311 realm = c_realm;
313 sitename = sitename_fetch(realm);
315 again:
317 DEBUG(6,("ads_find_dc: looking for %s '%s'\n",
318 (got_realm ? "realm" : "domain"), realm));
320 status = get_sorted_dc_list(realm, sitename, &ip_list, &count, got_realm);
321 if (!NT_STATUS_IS_OK(status)) {
322 /* fall back to netbios if we can */
323 if ( got_realm && !lp_disable_netbios() ) {
324 got_realm = False;
325 goto again;
328 SAFE_FREE(sitename);
329 return status;
332 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
333 for ( i=0; i<count; i++ ) {
334 char server[INET6_ADDRSTRLEN];
336 print_sockaddr(server, sizeof(server), &ip_list[i].ss);
338 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
339 continue;
341 if (!got_realm) {
342 /* realm in this case is a workgroup name. We need
343 to ignore any IP addresses in the negative connection
344 cache that match ip addresses returned in the ad realm
345 case. It sucks that I have to reproduce the logic above... */
346 c_realm = ads->server.realm;
347 if ( !c_realm || !*c_realm ) {
348 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
349 c_realm = lp_realm();
352 if (c_realm && *c_realm &&
353 !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm, server))) {
354 /* Ensure we add the workgroup name for this
355 IP address as negative too. */
356 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
357 continue;
361 if ( ads_try_connect(ads, server) ) {
362 SAFE_FREE(ip_list);
363 SAFE_FREE(sitename);
364 return NT_STATUS_OK;
367 /* keep track of failures */
368 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
371 SAFE_FREE(ip_list);
373 /* In case we failed to contact one of our closest DC on our site we
374 * need to try to find another DC, retry with a site-less SRV DNS query
375 * - Guenther */
377 if (sitename) {
378 DEBUG(1,("ads_find_dc: failed to find a valid DC on our site (%s), "
379 "trying to find another DC\n", sitename));
380 SAFE_FREE(sitename);
381 namecache_delete(realm, 0x1C);
382 goto again;
385 return NT_STATUS_NO_LOGON_SERVERS;
390 * Connect to the LDAP server
391 * @param ads Pointer to an existing ADS_STRUCT
392 * @return status of connection
394 ADS_STATUS ads_connect(ADS_STRUCT *ads)
396 int version = LDAP_VERSION3;
397 ADS_STATUS status;
398 NTSTATUS ntstatus;
399 char addr[INET6_ADDRSTRLEN];
401 ZERO_STRUCT(ads->ldap);
402 ads->ldap.last_attempt = time(NULL);
403 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
405 /* try with a user specified server */
407 if (DEBUGLEVEL >= 11) {
408 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
409 DEBUG(11,("ads_connect: entering\n"));
410 DEBUGADD(11,("%s\n", s));
411 TALLOC_FREE(s);
414 if (ads->server.ldap_server &&
415 ads_try_connect(ads, ads->server.ldap_server)) {
416 goto got_connection;
419 ntstatus = ads_find_dc(ads);
420 if (NT_STATUS_IS_OK(ntstatus)) {
421 goto got_connection;
424 status = ADS_ERROR_NT(ntstatus);
425 goto out;
427 got_connection:
429 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
430 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
432 if (!ads->auth.user_name) {
433 /* Must use the userPrincipalName value here or sAMAccountName
434 and not servicePrincipalName; found by Guenther Deschner */
436 asprintf(&ads->auth.user_name, "%s$", global_myname() );
439 if (!ads->auth.realm) {
440 ads->auth.realm = SMB_STRDUP(ads->config.realm);
443 if (!ads->auth.kdc_server) {
444 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
445 ads->auth.kdc_server = SMB_STRDUP(addr);
448 #if KRB5_DNS_HACK
449 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
450 to MIT kerberos to work (tridge) */
452 char *env;
453 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
454 setenv(env, ads->auth.kdc_server, 1);
455 free(env);
457 #endif
459 /* If the caller() requested no LDAP bind, then we are done */
461 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
462 status = ADS_SUCCESS;
463 goto out;
466 ads->ldap.mem_ctx = talloc_init("ads LDAP connection memory");
467 if (!ads->ldap.mem_ctx) {
468 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
469 goto out;
472 /* Otherwise setup the TCP LDAP session */
474 ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name,
475 LDAP_PORT, lp_ldap_timeout());
476 if (ads->ldap.ld == NULL) {
477 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
478 goto out;
480 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
482 /* cache the successful connection for workgroup and realm */
483 if (ads_closest_dc(ads)) {
484 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
485 saf_store( ads->server.workgroup, addr);
486 saf_store( ads->server.realm, addr);
489 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
491 status = ADS_ERROR(smb_ldap_start_tls(ads->ldap.ld, version));
492 if (!ADS_ERR_OK(status)) {
493 goto out;
496 /* fill in the current time and offsets */
498 status = ads_current_time( ads );
499 if ( !ADS_ERR_OK(status) ) {
500 goto out;
503 /* Now do the bind */
505 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
506 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
507 goto out;
510 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
511 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
512 goto out;
515 status = ads_sasl_bind(ads);
517 out:
518 if (DEBUGLEVEL >= 11) {
519 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
520 DEBUG(11,("ads_connect: leaving with: %s\n",
521 ads_errstr(status)));
522 DEBUGADD(11,("%s\n", s));
523 TALLOC_FREE(s);
526 return status;
530 * Disconnect the LDAP server
531 * @param ads Pointer to an existing ADS_STRUCT
533 void ads_disconnect(ADS_STRUCT *ads)
535 if (ads->ldap.ld) {
536 ldap_unbind(ads->ldap.ld);
537 ads->ldap.ld = NULL;
539 if (ads->ldap.wrap_ops && ads->ldap.wrap_ops->disconnect) {
540 ads->ldap.wrap_ops->disconnect(ads);
542 if (ads->ldap.mem_ctx) {
543 talloc_free(ads->ldap.mem_ctx);
545 ZERO_STRUCT(ads->ldap);
549 Duplicate a struct berval into talloc'ed memory
551 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
553 struct berval *value;
555 if (!in_val) return NULL;
557 value = TALLOC_ZERO_P(ctx, struct berval);
558 if (value == NULL)
559 return NULL;
560 if (in_val->bv_len == 0) return value;
562 value->bv_len = in_val->bv_len;
563 value->bv_val = (char *)TALLOC_MEMDUP(ctx, in_val->bv_val,
564 in_val->bv_len);
565 return value;
569 Make a values list out of an array of (struct berval *)
571 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
572 const struct berval **in_vals)
574 struct berval **values;
575 int i;
577 if (!in_vals) return NULL;
578 for (i=0; in_vals[i]; i++)
579 ; /* count values */
580 values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
581 if (!values) return NULL;
583 for (i=0; in_vals[i]; i++) {
584 values[i] = dup_berval(ctx, in_vals[i]);
586 return values;
590 UTF8-encode a values list out of an array of (char *)
592 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
594 char **values;
595 int i;
596 size_t size;
598 if (!in_vals) return NULL;
599 for (i=0; in_vals[i]; i++)
600 ; /* count values */
601 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
602 if (!values) return NULL;
604 for (i=0; in_vals[i]; i++) {
605 if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
606 TALLOC_FREE(values);
607 return NULL;
610 return values;
614 Pull a (char *) array out of a UTF8-encoded values list
616 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
618 char **values;
619 int i;
620 size_t converted_size;
622 if (!in_vals) return NULL;
623 for (i=0; in_vals[i]; i++)
624 ; /* count values */
625 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
626 if (!values) return NULL;
628 for (i=0; in_vals[i]; i++) {
629 if (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
630 &converted_size)) {
631 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
632 "%s", strerror(errno)));
635 return values;
639 * Do a search with paged results. cookie must be null on the first
640 * call, and then returned on each subsequent call. It will be null
641 * again when the entire search is complete
642 * @param ads connection to ads server
643 * @param bind_path Base dn for the search
644 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
645 * @param expr Search expression - specified in local charset
646 * @param attrs Attributes to retrieve - specified in utf8 or ascii
647 * @param res ** which will contain results - free res* with ads_msgfree()
648 * @param count Number of entries retrieved on this page
649 * @param cookie The paged results cookie to be returned on subsequent calls
650 * @return status of search
652 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
653 const char *bind_path,
654 int scope, const char *expr,
655 const char **attrs, void *args,
656 LDAPMessage **res,
657 int *count, struct berval **cookie)
659 int rc, i, version;
660 char *utf8_expr, *utf8_path, **search_attrs;
661 size_t converted_size;
662 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
663 BerElement *cookie_be = NULL;
664 struct berval *cookie_bv= NULL;
665 BerElement *ext_be = NULL;
666 struct berval *ext_bv= NULL;
668 TALLOC_CTX *ctx;
669 ads_control *external_control = (ads_control *) args;
671 *res = NULL;
673 if (!(ctx = talloc_init("ads_do_paged_search_args")))
674 return ADS_ERROR(LDAP_NO_MEMORY);
676 /* 0 means the conversion worked but the result was empty
677 so we only fail if it's -1. In any case, it always
678 at least nulls out the dest */
679 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
680 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
682 rc = LDAP_NO_MEMORY;
683 goto done;
686 if (!attrs || !(*attrs))
687 search_attrs = NULL;
688 else {
689 /* This would be the utf8-encoded version...*/
690 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
691 if (!(str_list_copy(talloc_tos(), &search_attrs, attrs))) {
692 rc = LDAP_NO_MEMORY;
693 goto done;
697 /* Paged results only available on ldap v3 or later */
698 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
699 if (version < LDAP_VERSION3) {
700 rc = LDAP_NOT_SUPPORTED;
701 goto done;
704 cookie_be = ber_alloc_t(LBER_USE_DER);
705 if (*cookie) {
706 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
707 ber_bvfree(*cookie); /* don't need it from last time */
708 *cookie = NULL;
709 } else {
710 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
712 ber_flatten(cookie_be, &cookie_bv);
713 PagedResults.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID);
714 PagedResults.ldctl_iscritical = (char) 1;
715 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
716 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
718 NoReferrals.ldctl_oid = CONST_DISCARD(char *, ADS_NO_REFERRALS_OID);
719 NoReferrals.ldctl_iscritical = (char) 0;
720 NoReferrals.ldctl_value.bv_len = 0;
721 NoReferrals.ldctl_value.bv_val = CONST_DISCARD(char *, "");
723 if (external_control &&
724 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
725 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
727 ExternalCtrl.ldctl_oid = CONST_DISCARD(char *, external_control->control);
728 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
730 /* win2k does not accept a ldctl_value beeing passed in */
732 if (external_control->val != 0) {
734 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
735 rc = LDAP_NO_MEMORY;
736 goto done;
739 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
740 rc = LDAP_NO_MEMORY;
741 goto done;
743 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
744 rc = LDAP_NO_MEMORY;
745 goto done;
748 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
749 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
751 } else {
752 ExternalCtrl.ldctl_value.bv_len = 0;
753 ExternalCtrl.ldctl_value.bv_val = NULL;
756 controls[0] = &NoReferrals;
757 controls[1] = &PagedResults;
758 controls[2] = &ExternalCtrl;
759 controls[3] = NULL;
761 } else {
762 controls[0] = &NoReferrals;
763 controls[1] = &PagedResults;
764 controls[2] = NULL;
767 /* we need to disable referrals as the openldap libs don't
768 handle them and paged results at the same time. Using them
769 together results in the result record containing the server
770 page control being removed from the result list (tridge/jmcd)
772 leaving this in despite the control that says don't generate
773 referrals, in case the server doesn't support it (jmcd)
775 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
777 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
778 search_attrs, 0, controls,
779 NULL, LDAP_NO_LIMIT,
780 (LDAPMessage **)res);
782 ber_free(cookie_be, 1);
783 ber_bvfree(cookie_bv);
785 if (rc) {
786 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
787 ldap_err2string(rc)));
788 goto done;
791 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
792 NULL, &rcontrols, 0);
794 if (!rcontrols) {
795 goto done;
798 for (i=0; rcontrols[i]; i++) {
799 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
800 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
801 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
802 &cookie_bv);
803 /* the berval is the cookie, but must be freed when
804 it is all done */
805 if (cookie_bv->bv_len) /* still more to do */
806 *cookie=ber_bvdup(cookie_bv);
807 else
808 *cookie=NULL;
809 ber_bvfree(cookie_bv);
810 ber_free(cookie_be, 1);
811 break;
814 ldap_controls_free(rcontrols);
816 done:
817 talloc_destroy(ctx);
819 if (ext_be) {
820 ber_free(ext_be, 1);
823 if (ext_bv) {
824 ber_bvfree(ext_bv);
827 /* if/when we decide to utf8-encode attrs, take out this next line */
828 TALLOC_FREE(search_attrs);
830 return ADS_ERROR(rc);
833 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
834 int scope, const char *expr,
835 const char **attrs, LDAPMessage **res,
836 int *count, struct berval **cookie)
838 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
843 * Get all results for a search. This uses ads_do_paged_search() to return
844 * all entries in a large search.
845 * @param ads connection to ads server
846 * @param bind_path Base dn for the search
847 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
848 * @param expr Search expression
849 * @param attrs Attributes to retrieve
850 * @param res ** which will contain results - free res* with ads_msgfree()
851 * @return status of search
853 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
854 int scope, const char *expr,
855 const char **attrs, void *args,
856 LDAPMessage **res)
858 struct berval *cookie = NULL;
859 int count = 0;
860 ADS_STATUS status;
862 *res = NULL;
863 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
864 &count, &cookie);
866 if (!ADS_ERR_OK(status))
867 return status;
869 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
870 while (cookie) {
871 LDAPMessage *res2 = NULL;
872 ADS_STATUS status2;
873 LDAPMessage *msg, *next;
875 status2 = ads_do_paged_search_args(ads, bind_path, scope, expr,
876 attrs, args, &res2, &count, &cookie);
878 if (!ADS_ERR_OK(status2)) break;
880 /* this relies on the way that ldap_add_result_entry() works internally. I hope
881 that this works on all ldap libs, but I have only tested with openldap */
882 for (msg = ads_first_message(ads, res2); msg; msg = next) {
883 next = ads_next_message(ads, msg);
884 ldap_add_result_entry((LDAPMessage **)res, msg);
886 /* note that we do not free res2, as the memory is now
887 part of the main returned list */
889 #else
890 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
891 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
892 #endif
894 return status;
897 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
898 int scope, const char *expr,
899 const char **attrs, LDAPMessage **res)
901 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
904 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
905 int scope, const char *expr,
906 const char **attrs, uint32 sd_flags,
907 LDAPMessage **res)
909 ads_control args;
911 args.control = ADS_SD_FLAGS_OID;
912 args.val = sd_flags;
913 args.critical = True;
915 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
920 * Run a function on all results for a search. Uses ads_do_paged_search() and
921 * runs the function as each page is returned, using ads_process_results()
922 * @param ads connection to ads server
923 * @param bind_path Base dn for the search
924 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
925 * @param expr Search expression - specified in local charset
926 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
927 * @param fn Function which takes attr name, values list, and data_area
928 * @param data_area Pointer which is passed to function on each call
929 * @return status of search
931 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
932 int scope, const char *expr, const char **attrs,
933 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
934 void *data_area)
936 struct berval *cookie = NULL;
937 int count = 0;
938 ADS_STATUS status;
939 LDAPMessage *res;
941 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
942 &count, &cookie);
944 if (!ADS_ERR_OK(status)) return status;
946 ads_process_results(ads, res, fn, data_area);
947 ads_msgfree(ads, res);
949 while (cookie) {
950 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
951 &res, &count, &cookie);
953 if (!ADS_ERR_OK(status)) break;
955 ads_process_results(ads, res, fn, data_area);
956 ads_msgfree(ads, res);
959 return status;
963 * Do a search with a timeout.
964 * @param ads connection to ads server
965 * @param bind_path Base dn for the search
966 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
967 * @param expr Search expression
968 * @param attrs Attributes to retrieve
969 * @param res ** which will contain results - free res* with ads_msgfree()
970 * @return status of search
972 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
973 const char *expr,
974 const char **attrs, LDAPMessage **res)
976 int rc;
977 char *utf8_expr, *utf8_path, **search_attrs = NULL;
978 size_t converted_size;
979 TALLOC_CTX *ctx;
981 *res = NULL;
982 if (!(ctx = talloc_init("ads_do_search"))) {
983 DEBUG(1,("ads_do_search: talloc_init() failed!"));
984 return ADS_ERROR(LDAP_NO_MEMORY);
987 /* 0 means the conversion worked but the result was empty
988 so we only fail if it's negative. In any case, it always
989 at least nulls out the dest */
990 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
991 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
993 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
994 rc = LDAP_NO_MEMORY;
995 goto done;
998 if (!attrs || !(*attrs))
999 search_attrs = NULL;
1000 else {
1001 /* This would be the utf8-encoded version...*/
1002 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1003 if (!(str_list_copy(talloc_tos(), &search_attrs, attrs)))
1005 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1006 rc = LDAP_NO_MEMORY;
1007 goto done;
1011 /* see the note in ads_do_paged_search - we *must* disable referrals */
1012 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1014 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1015 search_attrs, 0, NULL, NULL,
1016 LDAP_NO_LIMIT,
1017 (LDAPMessage **)res);
1019 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1020 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1021 rc = 0;
1024 done:
1025 talloc_destroy(ctx);
1026 /* if/when we decide to utf8-encode attrs, take out this next line */
1027 TALLOC_FREE(search_attrs);
1028 return ADS_ERROR(rc);
1031 * Do a general ADS search
1032 * @param ads connection to ads server
1033 * @param res ** which will contain results - free res* with ads_msgfree()
1034 * @param expr Search expression
1035 * @param attrs Attributes to retrieve
1036 * @return status of search
1038 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1039 const char *expr, const char **attrs)
1041 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1042 expr, attrs, res);
1046 * Do a search on a specific DistinguishedName
1047 * @param ads connection to ads server
1048 * @param res ** which will contain results - free res* with ads_msgfree()
1049 * @param dn DistinguishName to search
1050 * @param attrs Attributes to retrieve
1051 * @return status of search
1053 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1054 const char *dn, const char **attrs)
1056 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1057 attrs, res);
1061 * Free up memory from a ads_search
1062 * @param ads connection to ads server
1063 * @param msg Search results to free
1065 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1067 if (!msg) return;
1068 ldap_msgfree(msg);
1072 * Free up memory from various ads requests
1073 * @param ads connection to ads server
1074 * @param mem Area to free
1076 void ads_memfree(ADS_STRUCT *ads, void *mem)
1078 SAFE_FREE(mem);
1082 * Get a dn from search results
1083 * @param ads connection to ads server
1084 * @param msg Search result
1085 * @return dn string
1087 char *ads_get_dn(ADS_STRUCT *ads, LDAPMessage *msg)
1089 char *utf8_dn, *unix_dn;
1090 size_t converted_size;
1092 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1094 if (!utf8_dn) {
1095 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1096 return NULL;
1099 if (!pull_utf8_allocate(&unix_dn, utf8_dn, &converted_size)) {
1100 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1101 utf8_dn ));
1102 return NULL;
1104 ldap_memfree(utf8_dn);
1105 return unix_dn;
1109 * Get the parent from a dn
1110 * @param dn the dn to return the parent from
1111 * @return parent dn string
1113 char *ads_parent_dn(const char *dn)
1115 char *p;
1117 if (dn == NULL) {
1118 return NULL;
1121 p = strchr(dn, ',');
1123 if (p == NULL) {
1124 return NULL;
1127 return p+1;
1131 * Find a machine account given a hostname
1132 * @param ads connection to ads server
1133 * @param res ** which will contain results - free res* with ads_msgfree()
1134 * @param host Hostname to search for
1135 * @return status of search
1137 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1138 const char *machine)
1140 ADS_STATUS status;
1141 char *expr;
1142 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1144 *res = NULL;
1146 /* the easiest way to find a machine account anywhere in the tree
1147 is to look for hostname$ */
1148 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1149 DEBUG(1, ("asprintf failed!\n"));
1150 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1153 status = ads_search(ads, res, expr, attrs);
1154 SAFE_FREE(expr);
1155 return status;
1159 * Initialize a list of mods to be used in a modify request
1160 * @param ctx An initialized TALLOC_CTX
1161 * @return allocated ADS_MODLIST
1163 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1165 #define ADS_MODLIST_ALLOC_SIZE 10
1166 LDAPMod **mods;
1168 if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1169 /* -1 is safety to make sure we don't go over the end.
1170 need to reset it to NULL before doing ldap modify */
1171 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1173 return (ADS_MODLIST)mods;
1178 add an attribute to the list, with values list already constructed
1180 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1181 int mod_op, const char *name,
1182 const void *_invals)
1184 const void **invals = (const void **)_invals;
1185 int curmod;
1186 LDAPMod **modlist = (LDAPMod **) *mods;
1187 struct berval **ber_values = NULL;
1188 char **char_values = NULL;
1190 if (!invals) {
1191 mod_op = LDAP_MOD_DELETE;
1192 } else {
1193 if (mod_op & LDAP_MOD_BVALUES)
1194 ber_values = ads_dup_values(ctx,
1195 (const struct berval **)invals);
1196 else
1197 char_values = ads_push_strvals(ctx,
1198 (const char **) invals);
1201 /* find the first empty slot */
1202 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1203 curmod++);
1204 if (modlist[curmod] == (LDAPMod *) -1) {
1205 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
1206 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1207 return ADS_ERROR(LDAP_NO_MEMORY);
1208 memset(&modlist[curmod], 0,
1209 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1210 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1211 *mods = (ADS_MODLIST)modlist;
1214 if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
1215 return ADS_ERROR(LDAP_NO_MEMORY);
1216 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1217 if (mod_op & LDAP_MOD_BVALUES) {
1218 modlist[curmod]->mod_bvalues = ber_values;
1219 } else if (mod_op & LDAP_MOD_DELETE) {
1220 modlist[curmod]->mod_values = NULL;
1221 } else {
1222 modlist[curmod]->mod_values = char_values;
1225 modlist[curmod]->mod_op = mod_op;
1226 return ADS_ERROR(LDAP_SUCCESS);
1230 * Add a single string value to a mod list
1231 * @param ctx An initialized TALLOC_CTX
1232 * @param mods An initialized ADS_MODLIST
1233 * @param name The attribute name to add
1234 * @param val The value to add - NULL means DELETE
1235 * @return ADS STATUS indicating success of add
1237 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1238 const char *name, const char *val)
1240 const char *values[2];
1242 values[0] = val;
1243 values[1] = NULL;
1245 if (!val)
1246 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1247 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1251 * Add an array of string values to a mod list
1252 * @param ctx An initialized TALLOC_CTX
1253 * @param mods An initialized ADS_MODLIST
1254 * @param name The attribute name to add
1255 * @param vals The array of string values to add - NULL means DELETE
1256 * @return ADS STATUS indicating success of add
1258 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1259 const char *name, const char **vals)
1261 if (!vals)
1262 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1263 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1264 name, (const void **) vals);
1267 #if 0
1269 * Add a single ber-encoded value to a mod list
1270 * @param ctx An initialized TALLOC_CTX
1271 * @param mods An initialized ADS_MODLIST
1272 * @param name The attribute name to add
1273 * @param val The value to add - NULL means DELETE
1274 * @return ADS STATUS indicating success of add
1276 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1277 const char *name, const struct berval *val)
1279 const struct berval *values[2];
1281 values[0] = val;
1282 values[1] = NULL;
1283 if (!val)
1284 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1285 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1286 name, (const void **) values);
1288 #endif
1291 * Perform an ldap modify
1292 * @param ads connection to ads server
1293 * @param mod_dn DistinguishedName to modify
1294 * @param mods list of modifications to perform
1295 * @return status of modify
1297 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1299 int ret,i;
1300 char *utf8_dn = NULL;
1301 size_t converted_size;
1303 this control is needed to modify that contains a currently
1304 non-existent attribute (but allowable for the object) to run
1306 LDAPControl PermitModify = {
1307 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
1308 {0, NULL},
1309 (char) 1};
1310 LDAPControl *controls[2];
1312 controls[0] = &PermitModify;
1313 controls[1] = NULL;
1315 if (!push_utf8_allocate(&utf8_dn, mod_dn, &converted_size)) {
1316 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1319 /* find the end of the list, marked by NULL or -1 */
1320 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1321 /* make sure the end of the list is NULL */
1322 mods[i] = NULL;
1323 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1324 (LDAPMod **) mods, controls, NULL);
1325 SAFE_FREE(utf8_dn);
1326 return ADS_ERROR(ret);
1330 * Perform an ldap add
1331 * @param ads connection to ads server
1332 * @param new_dn DistinguishedName to add
1333 * @param mods list of attributes and values for DN
1334 * @return status of add
1336 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1338 int ret, i;
1339 char *utf8_dn = NULL;
1340 size_t converted_size;
1342 if (!push_utf8_allocate(&utf8_dn, new_dn, &converted_size)) {
1343 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1344 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1347 /* find the end of the list, marked by NULL or -1 */
1348 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1349 /* make sure the end of the list is NULL */
1350 mods[i] = NULL;
1352 ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1353 SAFE_FREE(utf8_dn);
1354 return ADS_ERROR(ret);
1358 * Delete a DistinguishedName
1359 * @param ads connection to ads server
1360 * @param new_dn DistinguishedName to delete
1361 * @return status of delete
1363 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1365 int ret;
1366 char *utf8_dn = NULL;
1367 size_t converted_size;
1368 if (!push_utf8_allocate(&utf8_dn, del_dn, &converted_size)) {
1369 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1370 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1373 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1374 SAFE_FREE(utf8_dn);
1375 return ADS_ERROR(ret);
1379 * Build an org unit string
1380 * if org unit is Computers or blank then assume a container, otherwise
1381 * assume a / separated list of organisational units.
1382 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1383 * @param ads connection to ads server
1384 * @param org_unit Organizational unit
1385 * @return org unit string - caller must free
1387 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1389 char *ret = NULL;
1391 if (!org_unit || !*org_unit) {
1393 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1395 /* samba4 might not yet respond to a wellknownobject-query */
1396 return ret ? ret : SMB_STRDUP("cn=Computers");
1399 if (strequal(org_unit, "Computers")) {
1400 return SMB_STRDUP("cn=Computers");
1403 /* jmcd: removed "\\" from the separation chars, because it is
1404 needed as an escape for chars like '#' which are valid in an
1405 OU name */
1406 return ads_build_path(org_unit, "/", "ou=", 1);
1410 * Get a org unit string for a well-known GUID
1411 * @param ads connection to ads server
1412 * @param wknguid Well known GUID
1413 * @return org unit string - caller must free
1415 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1417 ADS_STATUS status;
1418 LDAPMessage *res = NULL;
1419 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1420 **bind_dn_exp = NULL;
1421 const char *attrs[] = {"distinguishedName", NULL};
1422 int new_ln, wkn_ln, bind_ln, i;
1424 if (wknguid == NULL) {
1425 return NULL;
1428 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1429 DEBUG(1, ("asprintf failed!\n"));
1430 return NULL;
1433 status = ads_search_dn(ads, &res, base, attrs);
1434 if (!ADS_ERR_OK(status)) {
1435 DEBUG(1,("Failed while searching for: %s\n", base));
1436 goto out;
1439 if (ads_count_replies(ads, res) != 1) {
1440 goto out;
1443 /* substitute the bind-path from the well-known-guid-search result */
1444 wkn_dn = ads_get_dn(ads, res);
1445 if (!wkn_dn) {
1446 goto out;
1449 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1450 if (!wkn_dn_exp) {
1451 goto out;
1454 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1455 if (!bind_dn_exp) {
1456 goto out;
1459 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1461 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1464 new_ln = wkn_ln - bind_ln;
1466 ret = SMB_STRDUP(wkn_dn_exp[0]);
1467 if (!ret) {
1468 goto out;
1471 for (i=1; i < new_ln; i++) {
1472 char *s = NULL;
1474 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1475 SAFE_FREE(ret);
1476 goto out;
1479 SAFE_FREE(ret);
1480 ret = SMB_STRDUP(s);
1481 free(s);
1482 if (!ret) {
1483 goto out;
1487 out:
1488 SAFE_FREE(base);
1489 ads_msgfree(ads, res);
1490 ads_memfree(ads, wkn_dn);
1491 if (wkn_dn_exp) {
1492 ldap_value_free(wkn_dn_exp);
1494 if (bind_dn_exp) {
1495 ldap_value_free(bind_dn_exp);
1498 return ret;
1502 * Adds (appends) an item to an attribute array, rather then
1503 * replacing the whole list
1504 * @param ctx An initialized TALLOC_CTX
1505 * @param mods An initialized ADS_MODLIST
1506 * @param name name of the ldap attribute to append to
1507 * @param vals an array of values to add
1508 * @return status of addition
1511 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1512 const char *name, const char **vals)
1514 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1515 (const void *) vals);
1519 * Determines the an account's current KVNO via an LDAP lookup
1520 * @param ads An initialized ADS_STRUCT
1521 * @param account_name the NT samaccountname.
1522 * @return the kvno for the account, or -1 in case of a failure.
1525 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
1527 LDAPMessage *res = NULL;
1528 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1529 char *filter;
1530 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1531 char *dn_string = NULL;
1532 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1534 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
1535 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
1536 return kvno;
1538 ret = ads_search(ads, &res, filter, attrs);
1539 SAFE_FREE(filter);
1540 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
1541 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
1542 ads_msgfree(ads, res);
1543 return kvno;
1546 dn_string = ads_get_dn(ads, res);
1547 if (!dn_string) {
1548 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1549 ads_msgfree(ads, res);
1550 return kvno;
1552 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1553 ads_memfree(ads, dn_string);
1555 /* ---------------------------------------------------------
1556 * 0 is returned as a default KVNO from this point on...
1557 * This is done because Windows 2000 does not support key
1558 * version numbers. Chances are that a failure in the next
1559 * step is simply due to Windows 2000 being used for a
1560 * domain controller. */
1561 kvno = 0;
1563 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1564 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1565 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1566 ads_msgfree(ads, res);
1567 return kvno;
1570 /* Success */
1571 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1572 ads_msgfree(ads, res);
1573 return kvno;
1577 * Determines the computer account's current KVNO via an LDAP lookup
1578 * @param ads An initialized ADS_STRUCT
1579 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1580 * @return the kvno for the computer account, or -1 in case of a failure.
1583 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
1585 char *computer_account = NULL;
1586 uint32_t kvno = -1;
1588 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
1589 return kvno;
1592 kvno = ads_get_kvno(ads, computer_account);
1593 free(computer_account);
1595 return kvno;
1599 * This clears out all registered spn's for a given hostname
1600 * @param ads An initilaized ADS_STRUCT
1601 * @param machine_name the NetBIOS name of the computer.
1602 * @return 0 upon success, non-zero otherwise.
1605 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1607 TALLOC_CTX *ctx;
1608 LDAPMessage *res = NULL;
1609 ADS_MODLIST mods;
1610 const char *servicePrincipalName[1] = {NULL};
1611 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1612 char *dn_string = NULL;
1614 ret = ads_find_machine_acct(ads, &res, machine_name);
1615 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1616 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1617 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1618 ads_msgfree(ads, res);
1619 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1622 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1623 ctx = talloc_init("ads_clear_service_principal_names");
1624 if (!ctx) {
1625 ads_msgfree(ads, res);
1626 return ADS_ERROR(LDAP_NO_MEMORY);
1629 if (!(mods = ads_init_mods(ctx))) {
1630 talloc_destroy(ctx);
1631 ads_msgfree(ads, res);
1632 return ADS_ERROR(LDAP_NO_MEMORY);
1634 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1635 if (!ADS_ERR_OK(ret)) {
1636 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1637 ads_msgfree(ads, res);
1638 talloc_destroy(ctx);
1639 return ret;
1641 dn_string = ads_get_dn(ads, res);
1642 if (!dn_string) {
1643 talloc_destroy(ctx);
1644 ads_msgfree(ads, res);
1645 return ADS_ERROR(LDAP_NO_MEMORY);
1647 ret = ads_gen_mod(ads, dn_string, mods);
1648 ads_memfree(ads,dn_string);
1649 if (!ADS_ERR_OK(ret)) {
1650 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1651 machine_name));
1652 ads_msgfree(ads, res);
1653 talloc_destroy(ctx);
1654 return ret;
1657 ads_msgfree(ads, res);
1658 talloc_destroy(ctx);
1659 return ret;
1663 * This adds a service principal name to an existing computer account
1664 * (found by hostname) in AD.
1665 * @param ads An initialized ADS_STRUCT
1666 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1667 * @param my_fqdn The fully qualified DNS name of the machine
1668 * @param spn A string of the service principal to add, i.e. 'host'
1669 * @return 0 upon sucess, or non-zero if a failure occurs
1672 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1673 const char *my_fqdn, const char *spn)
1675 ADS_STATUS ret;
1676 TALLOC_CTX *ctx;
1677 LDAPMessage *res = NULL;
1678 char *psp1, *psp2;
1679 ADS_MODLIST mods;
1680 char *dn_string = NULL;
1681 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1683 ret = ads_find_machine_acct(ads, &res, machine_name);
1684 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1685 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1686 machine_name));
1687 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1688 spn, machine_name, ads->config.realm));
1689 ads_msgfree(ads, res);
1690 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1693 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1694 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1695 ads_msgfree(ads, res);
1696 return ADS_ERROR(LDAP_NO_MEMORY);
1699 /* add short name spn */
1701 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1702 talloc_destroy(ctx);
1703 ads_msgfree(ads, res);
1704 return ADS_ERROR(LDAP_NO_MEMORY);
1706 strupper_m(psp1);
1707 strlower_m(&psp1[strlen(spn)]);
1708 servicePrincipalName[0] = psp1;
1710 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1711 psp1, machine_name));
1714 /* add fully qualified spn */
1716 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1717 ret = ADS_ERROR(LDAP_NO_MEMORY);
1718 goto out;
1720 strupper_m(psp2);
1721 strlower_m(&psp2[strlen(spn)]);
1722 servicePrincipalName[1] = psp2;
1724 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1725 psp2, machine_name));
1727 if ( (mods = ads_init_mods(ctx)) == NULL ) {
1728 ret = ADS_ERROR(LDAP_NO_MEMORY);
1729 goto out;
1732 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1733 if (!ADS_ERR_OK(ret)) {
1734 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1735 goto out;
1738 if ( (dn_string = ads_get_dn(ads, res)) == NULL ) {
1739 ret = ADS_ERROR(LDAP_NO_MEMORY);
1740 goto out;
1743 ret = ads_gen_mod(ads, dn_string, mods);
1744 ads_memfree(ads,dn_string);
1745 if (!ADS_ERR_OK(ret)) {
1746 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1747 goto out;
1750 out:
1751 TALLOC_FREE( ctx );
1752 ads_msgfree(ads, res);
1753 return ret;
1757 * adds a machine account to the ADS server
1758 * @param ads An intialized ADS_STRUCT
1759 * @param machine_name - the NetBIOS machine name of this account.
1760 * @param account_type A number indicating the type of account to create
1761 * @param org_unit The LDAP path in which to place this account
1762 * @return 0 upon success, or non-zero otherwise
1765 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1766 const char *org_unit)
1768 ADS_STATUS ret;
1769 char *samAccountName, *controlstr;
1770 TALLOC_CTX *ctx;
1771 ADS_MODLIST mods;
1772 char *machine_escaped = NULL;
1773 char *new_dn;
1774 const char *objectClass[] = {"top", "person", "organizationalPerson",
1775 "user", "computer", NULL};
1776 LDAPMessage *res = NULL;
1777 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
1778 UF_DONT_EXPIRE_PASSWD |\
1779 UF_ACCOUNTDISABLE );
1781 if (!(ctx = talloc_init("ads_add_machine_acct")))
1782 return ADS_ERROR(LDAP_NO_MEMORY);
1784 ret = ADS_ERROR(LDAP_NO_MEMORY);
1786 machine_escaped = escape_rdn_val_string_alloc(machine_name);
1787 if (!machine_escaped) {
1788 goto done;
1791 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
1792 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
1794 if ( !new_dn || !samAccountName ) {
1795 goto done;
1798 #ifndef ENCTYPE_ARCFOUR_HMAC
1799 acct_control |= UF_USE_DES_KEY_ONLY;
1800 #endif
1802 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
1803 goto done;
1806 if (!(mods = ads_init_mods(ctx))) {
1807 goto done;
1810 ads_mod_str(ctx, &mods, "cn", machine_name);
1811 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1812 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1813 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1815 ret = ads_gen_add(ads, new_dn, mods);
1817 done:
1818 SAFE_FREE(machine_escaped);
1819 ads_msgfree(ads, res);
1820 talloc_destroy(ctx);
1822 return ret;
1826 * move a machine account to another OU on the ADS server
1827 * @param ads - An intialized ADS_STRUCT
1828 * @param machine_name - the NetBIOS machine name of this account.
1829 * @param org_unit - The LDAP path in which to place this account
1830 * @param moved - whether we moved the machine account (optional)
1831 * @return 0 upon success, or non-zero otherwise
1834 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1835 const char *org_unit, bool *moved)
1837 ADS_STATUS rc;
1838 int ldap_status;
1839 LDAPMessage *res = NULL;
1840 char *filter = NULL;
1841 char *computer_dn = NULL;
1842 char *parent_dn;
1843 char *computer_rdn = NULL;
1844 bool need_move = False;
1846 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1847 rc = ADS_ERROR(LDAP_NO_MEMORY);
1848 goto done;
1851 /* Find pre-existing machine */
1852 rc = ads_search(ads, &res, filter, NULL);
1853 if (!ADS_ERR_OK(rc)) {
1854 goto done;
1857 computer_dn = ads_get_dn(ads, res);
1858 if (!computer_dn) {
1859 rc = ADS_ERROR(LDAP_NO_MEMORY);
1860 goto done;
1863 parent_dn = ads_parent_dn(computer_dn);
1864 if (strequal(parent_dn, org_unit)) {
1865 goto done;
1868 need_move = True;
1870 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
1871 rc = ADS_ERROR(LDAP_NO_MEMORY);
1872 goto done;
1875 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
1876 org_unit, 1, NULL, NULL);
1877 rc = ADS_ERROR(ldap_status);
1879 done:
1880 ads_msgfree(ads, res);
1881 SAFE_FREE(filter);
1882 SAFE_FREE(computer_dn);
1883 SAFE_FREE(computer_rdn);
1885 if (!ADS_ERR_OK(rc)) {
1886 need_move = False;
1889 if (moved) {
1890 *moved = need_move;
1893 return rc;
1897 dump a binary result from ldap
1899 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
1901 int i, j;
1902 for (i=0; values[i]; i++) {
1903 printf("%s: ", field);
1904 for (j=0; j<values[i]->bv_len; j++) {
1905 printf("%02X", (unsigned char)values[i]->bv_val[j]);
1907 printf("\n");
1911 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
1913 int i;
1914 for (i=0; values[i]; i++) {
1916 UUID_FLAT guid;
1917 struct GUID tmp;
1919 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1920 smb_uuid_unpack(guid, &tmp);
1921 printf("%s: %s\n", field, smb_uuid_string(talloc_tos(), tmp));
1926 dump a sid result from ldap
1928 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
1930 int i;
1931 for (i=0; values[i]; i++) {
1932 DOM_SID sid;
1933 fstring tmp;
1934 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1935 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
1940 dump ntSecurityDescriptor
1942 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
1944 TALLOC_CTX *frame = talloc_stackframe();
1945 struct security_descriptor *psd;
1946 NTSTATUS status;
1948 status = unmarshall_sec_desc(talloc_tos(), (uint8 *)values[0]->bv_val,
1949 values[0]->bv_len, &psd);
1950 if (!NT_STATUS_IS_OK(status)) {
1951 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
1952 nt_errstr(status)));
1953 TALLOC_FREE(frame);
1954 return;
1957 if (psd) {
1958 ads_disp_sd(ads, talloc_tos(), psd);
1961 TALLOC_FREE(frame);
1965 dump a string result from ldap
1967 static void dump_string(const char *field, char **values)
1969 int i;
1970 for (i=0; values[i]; i++) {
1971 printf("%s: %s\n", field, values[i]);
1976 dump a field from LDAP on stdout
1977 used for debugging
1980 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
1982 const struct {
1983 const char *name;
1984 bool string;
1985 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
1986 } handlers[] = {
1987 {"objectGUID", False, dump_guid},
1988 {"netbootGUID", False, dump_guid},
1989 {"nTSecurityDescriptor", False, dump_sd},
1990 {"dnsRecord", False, dump_binary},
1991 {"objectSid", False, dump_sid},
1992 {"tokenGroups", False, dump_sid},
1993 {"tokenGroupsNoGCAcceptable", False, dump_sid},
1994 {"tokengroupsGlobalandUniversal", False, dump_sid},
1995 {"mS-DS-CreatorSID", False, dump_sid},
1996 {"msExchMailboxGuid", False, dump_guid},
1997 {NULL, True, NULL}
1999 int i;
2001 if (!field) { /* must be end of an entry */
2002 printf("\n");
2003 return False;
2006 for (i=0; handlers[i].name; i++) {
2007 if (StrCaseCmp(handlers[i].name, field) == 0) {
2008 if (!values) /* first time, indicate string or not */
2009 return handlers[i].string;
2010 handlers[i].handler(ads, field, (struct berval **) values);
2011 break;
2014 if (!handlers[i].name) {
2015 if (!values) /* first time, indicate string conversion */
2016 return True;
2017 dump_string(field, (char **)values);
2019 return False;
2023 * Dump a result from LDAP on stdout
2024 * used for debugging
2025 * @param ads connection to ads server
2026 * @param res Results to dump
2029 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2031 ads_process_results(ads, res, ads_dump_field, NULL);
2035 * Walk through results, calling a function for each entry found.
2036 * The function receives a field name, a berval * array of values,
2037 * and a data area passed through from the start. The function is
2038 * called once with null for field and values at the end of each
2039 * entry.
2040 * @param ads connection to ads server
2041 * @param res Results to process
2042 * @param fn Function for processing each result
2043 * @param data_area user-defined area to pass to function
2045 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2046 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2047 void *data_area)
2049 LDAPMessage *msg;
2050 TALLOC_CTX *ctx;
2051 size_t converted_size;
2053 if (!(ctx = talloc_init("ads_process_results")))
2054 return;
2056 for (msg = ads_first_entry(ads, res); msg;
2057 msg = ads_next_entry(ads, msg)) {
2058 char *utf8_field;
2059 BerElement *b;
2061 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2062 (LDAPMessage *)msg,&b);
2063 utf8_field;
2064 utf8_field=ldap_next_attribute(ads->ldap.ld,
2065 (LDAPMessage *)msg,b)) {
2066 struct berval **ber_vals;
2067 char **str_vals, **utf8_vals;
2068 char *field;
2069 bool string;
2071 if (!pull_utf8_talloc(ctx, &field, utf8_field,
2072 &converted_size))
2074 DEBUG(0,("ads_process_results: "
2075 "pull_utf8_talloc failed: %s",
2076 strerror(errno)));
2079 string = fn(ads, field, NULL, data_area);
2081 if (string) {
2082 utf8_vals = ldap_get_values(ads->ldap.ld,
2083 (LDAPMessage *)msg, field);
2084 str_vals = ads_pull_strvals(ctx,
2085 (const char **) utf8_vals);
2086 fn(ads, field, (void **) str_vals, data_area);
2087 ldap_value_free(utf8_vals);
2088 } else {
2089 ber_vals = ldap_get_values_len(ads->ldap.ld,
2090 (LDAPMessage *)msg, field);
2091 fn(ads, field, (void **) ber_vals, data_area);
2093 ldap_value_free_len(ber_vals);
2095 ldap_memfree(utf8_field);
2097 ber_free(b, 0);
2098 talloc_free_children(ctx);
2099 fn(ads, NULL, NULL, data_area); /* completed an entry */
2102 talloc_destroy(ctx);
2106 * count how many replies are in a LDAPMessage
2107 * @param ads connection to ads server
2108 * @param res Results to count
2109 * @return number of replies
2111 int ads_count_replies(ADS_STRUCT *ads, void *res)
2113 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2117 * pull the first entry from a ADS result
2118 * @param ads connection to ads server
2119 * @param res Results of search
2120 * @return first entry from result
2122 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2124 return ldap_first_entry(ads->ldap.ld, res);
2128 * pull the next entry from a ADS result
2129 * @param ads connection to ads server
2130 * @param res Results of search
2131 * @return next entry from result
2133 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2135 return ldap_next_entry(ads->ldap.ld, res);
2139 * pull the first message from a ADS result
2140 * @param ads connection to ads server
2141 * @param res Results of search
2142 * @return first message from result
2144 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2146 return ldap_first_message(ads->ldap.ld, res);
2150 * pull the next message from a ADS result
2151 * @param ads connection to ads server
2152 * @param res Results of search
2153 * @return next message from result
2155 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2157 return ldap_next_message(ads->ldap.ld, res);
2161 * pull a single string from a ADS result
2162 * @param ads connection to ads server
2163 * @param mem_ctx TALLOC_CTX to use for allocating result string
2164 * @param msg Results of search
2165 * @param field Attribute to retrieve
2166 * @return Result string in talloc context
2168 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2169 const char *field)
2171 char **values;
2172 char *ret = NULL;
2173 char *ux_string;
2174 size_t converted_size;
2176 values = ldap_get_values(ads->ldap.ld, msg, field);
2177 if (!values)
2178 return NULL;
2180 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2181 &converted_size))
2183 ret = ux_string;
2185 ldap_value_free(values);
2186 return ret;
2190 * pull an array of strings from a ADS result
2191 * @param ads connection to ads server
2192 * @param mem_ctx TALLOC_CTX to use for allocating result string
2193 * @param msg Results of search
2194 * @param field Attribute to retrieve
2195 * @return Result strings in talloc context
2197 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2198 LDAPMessage *msg, const char *field,
2199 size_t *num_values)
2201 char **values;
2202 char **ret = NULL;
2203 int i;
2204 size_t converted_size;
2206 values = ldap_get_values(ads->ldap.ld, msg, field);
2207 if (!values)
2208 return NULL;
2210 *num_values = ldap_count_values(values);
2212 ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
2213 if (!ret) {
2214 ldap_value_free(values);
2215 return NULL;
2218 for (i=0;i<*num_values;i++) {
2219 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2220 &converted_size))
2222 ldap_value_free(values);
2223 return NULL;
2226 ret[i] = NULL;
2228 ldap_value_free(values);
2229 return ret;
2233 * pull an array of strings from a ADS result
2234 * (handle large multivalue attributes with range retrieval)
2235 * @param ads connection to ads server
2236 * @param mem_ctx TALLOC_CTX to use for allocating result string
2237 * @param msg Results of search
2238 * @param field Attribute to retrieve
2239 * @param current_strings strings returned by a previous call to this function
2240 * @param next_attribute The next query should ask for this attribute
2241 * @param num_values How many values did we get this time?
2242 * @param more_values Are there more values to get?
2243 * @return Result strings in talloc context
2245 char **ads_pull_strings_range(ADS_STRUCT *ads,
2246 TALLOC_CTX *mem_ctx,
2247 LDAPMessage *msg, const char *field,
2248 char **current_strings,
2249 const char **next_attribute,
2250 size_t *num_strings,
2251 bool *more_strings)
2253 char *attr;
2254 char *expected_range_attrib, *range_attr;
2255 BerElement *ptr = NULL;
2256 char **strings;
2257 char **new_strings;
2258 size_t num_new_strings;
2259 unsigned long int range_start;
2260 unsigned long int range_end;
2262 /* we might have been given the whole lot anyway */
2263 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2264 *more_strings = False;
2265 return strings;
2268 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2270 /* look for Range result */
2271 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2272 attr;
2273 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2274 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2275 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2276 range_attr = attr;
2277 break;
2279 ldap_memfree(attr);
2281 if (!attr) {
2282 ber_free(ptr, 0);
2283 /* nothing here - this field is just empty */
2284 *more_strings = False;
2285 return NULL;
2288 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2289 &range_start, &range_end) == 2) {
2290 *more_strings = True;
2291 } else {
2292 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2293 &range_start) == 1) {
2294 *more_strings = False;
2295 } else {
2296 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2297 range_attr));
2298 ldap_memfree(range_attr);
2299 *more_strings = False;
2300 return NULL;
2304 if ((*num_strings) != range_start) {
2305 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2306 " - aborting range retreival\n",
2307 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2308 ldap_memfree(range_attr);
2309 *more_strings = False;
2310 return NULL;
2313 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2315 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2316 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2317 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2318 range_attr, (unsigned long int)range_end - range_start + 1,
2319 (unsigned long int)num_new_strings));
2320 ldap_memfree(range_attr);
2321 *more_strings = False;
2322 return NULL;
2325 strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2326 *num_strings + num_new_strings);
2328 if (strings == NULL) {
2329 ldap_memfree(range_attr);
2330 *more_strings = False;
2331 return NULL;
2334 if (new_strings && num_new_strings) {
2335 memcpy(&strings[*num_strings], new_strings,
2336 sizeof(*new_strings) * num_new_strings);
2339 (*num_strings) += num_new_strings;
2341 if (*more_strings) {
2342 *next_attribute = talloc_asprintf(mem_ctx,
2343 "%s;range=%d-*",
2344 field,
2345 (int)*num_strings);
2347 if (!*next_attribute) {
2348 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2349 ldap_memfree(range_attr);
2350 *more_strings = False;
2351 return NULL;
2355 ldap_memfree(range_attr);
2357 return strings;
2361 * pull a single uint32 from a ADS result
2362 * @param ads connection to ads server
2363 * @param msg Results of search
2364 * @param field Attribute to retrieve
2365 * @param v Pointer to int to store result
2366 * @return boolean inidicating success
2368 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2369 uint32 *v)
2371 char **values;
2373 values = ldap_get_values(ads->ldap.ld, msg, field);
2374 if (!values)
2375 return False;
2376 if (!values[0]) {
2377 ldap_value_free(values);
2378 return False;
2381 *v = atoi(values[0]);
2382 ldap_value_free(values);
2383 return True;
2387 * pull a single objectGUID from an ADS result
2388 * @param ads connection to ADS server
2389 * @param msg results of search
2390 * @param guid 37-byte area to receive text guid
2391 * @return boolean indicating success
2393 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2395 char **values;
2396 UUID_FLAT flat_guid;
2398 values = ldap_get_values(ads->ldap.ld, msg, "objectGUID");
2399 if (!values)
2400 return False;
2402 if (values[0]) {
2403 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2404 smb_uuid_unpack(flat_guid, guid);
2405 ldap_value_free(values);
2406 return True;
2408 ldap_value_free(values);
2409 return False;
2415 * pull a single DOM_SID from a ADS result
2416 * @param ads connection to ads server
2417 * @param msg Results of search
2418 * @param field Attribute to retrieve
2419 * @param sid Pointer to sid to store result
2420 * @return boolean inidicating success
2422 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2423 DOM_SID *sid)
2425 struct berval **values;
2426 bool ret = False;
2428 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2430 if (!values)
2431 return False;
2433 if (values[0])
2434 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2436 ldap_value_free_len(values);
2437 return ret;
2441 * pull an array of DOM_SIDs from a ADS result
2442 * @param ads connection to ads server
2443 * @param mem_ctx TALLOC_CTX for allocating sid array
2444 * @param msg Results of search
2445 * @param field Attribute to retrieve
2446 * @param sids pointer to sid array to allocate
2447 * @return the count of SIDs pulled
2449 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2450 LDAPMessage *msg, const char *field, DOM_SID **sids)
2452 struct berval **values;
2453 bool ret;
2454 int count, i;
2456 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2458 if (!values)
2459 return 0;
2461 for (i=0; values[i]; i++)
2462 /* nop */ ;
2464 if (i) {
2465 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2466 if (!(*sids)) {
2467 ldap_value_free_len(values);
2468 return 0;
2470 } else {
2471 (*sids) = NULL;
2474 count = 0;
2475 for (i=0; values[i]; i++) {
2476 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2477 if (ret) {
2478 DEBUG(10, ("pulling SID: %s\n",
2479 sid_string_dbg(&(*sids)[count])));
2480 count++;
2484 ldap_value_free_len(values);
2485 return count;
2489 * pull a SEC_DESC from a ADS result
2490 * @param ads connection to ads server
2491 * @param mem_ctx TALLOC_CTX for allocating sid array
2492 * @param msg Results of search
2493 * @param field Attribute to retrieve
2494 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2495 * @return boolean inidicating success
2497 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2498 LDAPMessage *msg, const char *field, SEC_DESC **sd)
2500 struct berval **values;
2501 bool ret = true;
2503 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2505 if (!values) return false;
2507 if (values[0]) {
2508 NTSTATUS status;
2509 status = unmarshall_sec_desc(mem_ctx,
2510 (uint8 *)values[0]->bv_val,
2511 values[0]->bv_len, sd);
2512 if (!NT_STATUS_IS_OK(status)) {
2513 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2514 nt_errstr(status)));
2515 ret = false;
2519 ldap_value_free_len(values);
2520 return ret;
2524 * in order to support usernames longer than 21 characters we need to
2525 * use both the sAMAccountName and the userPrincipalName attributes
2526 * It seems that not all users have the userPrincipalName attribute set
2528 * @param ads connection to ads server
2529 * @param mem_ctx TALLOC_CTX for allocating sid array
2530 * @param msg Results of search
2531 * @return the username
2533 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2534 LDAPMessage *msg)
2536 #if 0 /* JERRY */
2537 char *ret, *p;
2539 /* lookup_name() only works on the sAMAccountName to
2540 returning the username portion of userPrincipalName
2541 breaks winbindd_getpwnam() */
2543 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2544 if (ret && (p = strchr_m(ret, '@'))) {
2545 *p = 0;
2546 return ret;
2548 #endif
2549 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2554 * find the update serial number - this is the core of the ldap cache
2555 * @param ads connection to ads server
2556 * @param ads connection to ADS server
2557 * @param usn Pointer to retrieved update serial number
2558 * @return status of search
2560 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2562 const char *attrs[] = {"highestCommittedUSN", NULL};
2563 ADS_STATUS status;
2564 LDAPMessage *res;
2566 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2567 if (!ADS_ERR_OK(status))
2568 return status;
2570 if (ads_count_replies(ads, res) != 1) {
2571 ads_msgfree(ads, res);
2572 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2575 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2576 ads_msgfree(ads, res);
2577 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2580 ads_msgfree(ads, res);
2581 return ADS_SUCCESS;
2584 /* parse a ADS timestring - typical string is
2585 '20020917091222.0Z0' which means 09:12.22 17th September
2586 2002, timezone 0 */
2587 static time_t ads_parse_time(const char *str)
2589 struct tm tm;
2591 ZERO_STRUCT(tm);
2593 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2594 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2595 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2596 return 0;
2598 tm.tm_year -= 1900;
2599 tm.tm_mon -= 1;
2601 return timegm(&tm);
2604 /********************************************************************
2605 ********************************************************************/
2607 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2609 const char *attrs[] = {"currentTime", NULL};
2610 ADS_STATUS status;
2611 LDAPMessage *res;
2612 char *timestr;
2613 TALLOC_CTX *ctx;
2614 ADS_STRUCT *ads_s = ads;
2616 if (!(ctx = talloc_init("ads_current_time"))) {
2617 return ADS_ERROR(LDAP_NO_MEMORY);
2620 /* establish a new ldap tcp session if necessary */
2622 if ( !ads->ldap.ld ) {
2623 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2624 ads->server.ldap_server )) == NULL )
2626 goto done;
2628 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2629 status = ads_connect( ads_s );
2630 if ( !ADS_ERR_OK(status))
2631 goto done;
2634 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2635 if (!ADS_ERR_OK(status)) {
2636 goto done;
2639 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2640 if (!timestr) {
2641 ads_msgfree(ads_s, res);
2642 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2643 goto done;
2646 /* but save the time and offset in the original ADS_STRUCT */
2648 ads->config.current_time = ads_parse_time(timestr);
2650 if (ads->config.current_time != 0) {
2651 ads->auth.time_offset = ads->config.current_time - time(NULL);
2652 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2655 ads_msgfree(ads, res);
2657 status = ADS_SUCCESS;
2659 done:
2660 /* free any temporary ads connections */
2661 if ( ads_s != ads ) {
2662 ads_destroy( &ads_s );
2664 talloc_destroy(ctx);
2666 return status;
2669 /********************************************************************
2670 ********************************************************************/
2672 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2674 const char *attrs[] = {"domainFunctionality", NULL};
2675 ADS_STATUS status;
2676 LDAPMessage *res;
2677 ADS_STRUCT *ads_s = ads;
2679 *val = DS_DOMAIN_FUNCTION_2000;
2681 /* establish a new ldap tcp session if necessary */
2683 if ( !ads->ldap.ld ) {
2684 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2685 ads->server.ldap_server )) == NULL )
2687 goto done;
2689 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2690 status = ads_connect( ads_s );
2691 if ( !ADS_ERR_OK(status))
2692 goto done;
2695 /* If the attribute does not exist assume it is a Windows 2000
2696 functional domain */
2698 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2699 if (!ADS_ERR_OK(status)) {
2700 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2701 status = ADS_SUCCESS;
2703 goto done;
2706 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2707 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2709 DEBUG(3,("ads_domain_func_level: %d\n", *val));
2712 ads_msgfree(ads, res);
2714 done:
2715 /* free any temporary ads connections */
2716 if ( ads_s != ads ) {
2717 ads_destroy( &ads_s );
2720 return status;
2724 * find the domain sid for our domain
2725 * @param ads connection to ads server
2726 * @param sid Pointer to domain sid
2727 * @return status of search
2729 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2731 const char *attrs[] = {"objectSid", NULL};
2732 LDAPMessage *res;
2733 ADS_STATUS rc;
2735 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2736 attrs, &res);
2737 if (!ADS_ERR_OK(rc)) return rc;
2738 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2739 ads_msgfree(ads, res);
2740 return ADS_ERROR_SYSTEM(ENOENT);
2742 ads_msgfree(ads, res);
2744 return ADS_SUCCESS;
2748 * find our site name
2749 * @param ads connection to ads server
2750 * @param mem_ctx Pointer to talloc context
2751 * @param site_name Pointer to the sitename
2752 * @return status of search
2754 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2756 ADS_STATUS status;
2757 LDAPMessage *res;
2758 const char *dn, *service_name;
2759 const char *attrs[] = { "dsServiceName", NULL };
2761 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2762 if (!ADS_ERR_OK(status)) {
2763 return status;
2766 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2767 if (service_name == NULL) {
2768 ads_msgfree(ads, res);
2769 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2772 ads_msgfree(ads, res);
2774 /* go up three levels */
2775 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2776 if (dn == NULL) {
2777 return ADS_ERROR(LDAP_NO_MEMORY);
2780 *site_name = talloc_strdup(mem_ctx, dn);
2781 if (*site_name == NULL) {
2782 return ADS_ERROR(LDAP_NO_MEMORY);
2785 return status;
2787 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2792 * find the site dn where a machine resides
2793 * @param ads connection to ads server
2794 * @param mem_ctx Pointer to talloc context
2795 * @param computer_name name of the machine
2796 * @param site_name Pointer to the sitename
2797 * @return status of search
2799 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
2801 ADS_STATUS status;
2802 LDAPMessage *res;
2803 const char *parent, *filter;
2804 char *config_context = NULL;
2805 char *dn;
2807 /* shortcut a query */
2808 if (strequal(computer_name, ads->config.ldap_server_name)) {
2809 return ads_site_dn(ads, mem_ctx, site_dn);
2812 status = ads_config_path(ads, mem_ctx, &config_context);
2813 if (!ADS_ERR_OK(status)) {
2814 return status;
2817 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
2818 if (filter == NULL) {
2819 return ADS_ERROR(LDAP_NO_MEMORY);
2822 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
2823 filter, NULL, &res);
2824 if (!ADS_ERR_OK(status)) {
2825 return status;
2828 if (ads_count_replies(ads, res) != 1) {
2829 ads_msgfree(ads, res);
2830 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2833 dn = ads_get_dn(ads, res);
2834 if (dn == NULL) {
2835 ads_msgfree(ads, res);
2836 return ADS_ERROR(LDAP_NO_MEMORY);
2839 /* go up three levels */
2840 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
2841 if (parent == NULL) {
2842 ads_msgfree(ads, res);
2843 ads_memfree(ads, dn);
2844 return ADS_ERROR(LDAP_NO_MEMORY);
2847 *site_dn = talloc_strdup(mem_ctx, parent);
2848 if (*site_dn == NULL) {
2849 ads_msgfree(ads, res);
2850 ads_memfree(ads, dn);
2851 return ADS_ERROR(LDAP_NO_MEMORY);
2854 ads_memfree(ads, dn);
2855 ads_msgfree(ads, res);
2857 return status;
2861 * get the upn suffixes for a domain
2862 * @param ads connection to ads server
2863 * @param mem_ctx Pointer to talloc context
2864 * @param suffixes Pointer to an array of suffixes
2865 * @param num_suffixes Pointer to the number of suffixes
2866 * @return status of search
2868 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
2870 ADS_STATUS status;
2871 LDAPMessage *res;
2872 const char *base;
2873 char *config_context = NULL;
2874 const char *attrs[] = { "uPNSuffixes", NULL };
2876 status = ads_config_path(ads, mem_ctx, &config_context);
2877 if (!ADS_ERR_OK(status)) {
2878 return status;
2881 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
2882 if (base == NULL) {
2883 return ADS_ERROR(LDAP_NO_MEMORY);
2886 status = ads_search_dn(ads, &res, base, attrs);
2887 if (!ADS_ERR_OK(status)) {
2888 return status;
2891 if (ads_count_replies(ads, res) != 1) {
2892 ads_msgfree(ads, res);
2893 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2896 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
2897 if ((*suffixes) == NULL) {
2898 ads_msgfree(ads, res);
2899 return ADS_ERROR(LDAP_NO_MEMORY);
2902 ads_msgfree(ads, res);
2904 return status;
2908 * get the joinable ous for a domain
2909 * @param ads connection to ads server
2910 * @param mem_ctx Pointer to talloc context
2911 * @param ous Pointer to an array of ous
2912 * @param num_ous Pointer to the number of ous
2913 * @return status of search
2915 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
2916 TALLOC_CTX *mem_ctx,
2917 char ***ous,
2918 size_t *num_ous)
2920 ADS_STATUS status;
2921 LDAPMessage *res = NULL;
2922 LDAPMessage *msg = NULL;
2923 const char *attrs[] = { "dn", NULL };
2924 int count = 0;
2926 status = ads_search(ads, &res,
2927 "(|(objectClass=domain)(objectclass=organizationalUnit))",
2928 attrs);
2929 if (!ADS_ERR_OK(status)) {
2930 return status;
2933 count = ads_count_replies(ads, res);
2934 if (count < 1) {
2935 ads_msgfree(ads, res);
2936 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2939 for (msg = ads_first_entry(ads, res); msg;
2940 msg = ads_next_entry(ads, msg)) {
2942 char *dn = NULL;
2944 dn = ads_get_dn(ads, msg);
2945 if (!dn) {
2946 ads_msgfree(ads, res);
2947 return ADS_ERROR(LDAP_NO_MEMORY);
2950 if (!add_string_to_array(mem_ctx, dn,
2951 (const char ***)ous,
2952 (int *)num_ous)) {
2953 ads_memfree(ads, dn);
2954 ads_msgfree(ads, res);
2955 return ADS_ERROR(LDAP_NO_MEMORY);
2958 ads_memfree(ads, dn);
2961 ads_msgfree(ads, res);
2963 return status;
2968 * pull a DOM_SID from an extended dn string
2969 * @param mem_ctx TALLOC_CTX
2970 * @param extended_dn string
2971 * @param flags string type of extended_dn
2972 * @param sid pointer to a DOM_SID
2973 * @return boolean inidicating success
2975 bool ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
2976 const char *extended_dn,
2977 enum ads_extended_dn_flags flags,
2978 DOM_SID *sid)
2980 char *p, *q, *dn;
2982 if (!extended_dn) {
2983 return False;
2986 /* otherwise extended_dn gets stripped off */
2987 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
2988 return False;
2991 * ADS_EXTENDED_DN_HEX_STRING:
2992 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
2994 * ADS_EXTENDED_DN_STRING (only with w2k3):
2995 <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
2998 p = strchr(dn, ';');
2999 if (!p) {
3000 return False;
3003 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3004 return False;
3007 p += strlen(";<SID=");
3009 q = strchr(p, '>');
3010 if (!q) {
3011 return False;
3014 *q = '\0';
3016 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3018 switch (flags) {
3020 case ADS_EXTENDED_DN_STRING:
3021 if (!string_to_sid(sid, p)) {
3022 return False;
3024 break;
3025 case ADS_EXTENDED_DN_HEX_STRING: {
3026 fstring buf;
3027 size_t buf_len;
3029 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3030 if (buf_len == 0) {
3031 return False;
3034 if (!sid_parse(buf, buf_len, sid)) {
3035 DEBUG(10,("failed to parse sid\n"));
3036 return False;
3038 break;
3040 default:
3041 DEBUG(10,("unknown extended dn format\n"));
3042 return False;
3045 return True;
3049 * pull an array of DOM_SIDs from a ADS result
3050 * @param ads connection to ads server
3051 * @param mem_ctx TALLOC_CTX for allocating sid array
3052 * @param msg Results of search
3053 * @param field Attribute to retrieve
3054 * @param flags string type of extended_dn
3055 * @param sids pointer to sid array to allocate
3056 * @return the count of SIDs pulled
3058 int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads,
3059 TALLOC_CTX *mem_ctx,
3060 LDAPMessage *msg,
3061 const char *field,
3062 enum ads_extended_dn_flags flags,
3063 DOM_SID **sids)
3065 int i;
3066 size_t dn_count;
3067 char **dn_strings;
3069 if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field,
3070 &dn_count)) == NULL) {
3071 return 0;
3074 (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, dn_count + 1);
3075 if (!(*sids)) {
3076 TALLOC_FREE(dn_strings);
3077 return 0;
3080 for (i=0; i<dn_count; i++) {
3082 if (!ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i],
3083 flags, &(*sids)[i])) {
3084 TALLOC_FREE(*sids);
3085 TALLOC_FREE(dn_strings);
3086 return 0;
3090 TALLOC_FREE(dn_strings);
3092 return dn_count;
3095 /********************************************************************
3096 ********************************************************************/
3098 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3100 LDAPMessage *res = NULL;
3101 ADS_STATUS status;
3102 int count = 0;
3103 char *name = NULL;
3105 status = ads_find_machine_acct(ads, &res, global_myname());
3106 if (!ADS_ERR_OK(status)) {
3107 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3108 global_myname()));
3109 goto out;
3112 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3113 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3114 goto out;
3117 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3118 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3121 out:
3122 ads_msgfree(ads, res);
3124 return name;
3127 /********************************************************************
3128 ********************************************************************/
3130 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3132 LDAPMessage *res = NULL;
3133 ADS_STATUS status;
3134 int count = 0;
3135 char *name = NULL;
3137 status = ads_find_machine_acct(ads, &res, machine_name);
3138 if (!ADS_ERR_OK(status)) {
3139 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3140 global_myname()));
3141 goto out;
3144 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3145 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3146 goto out;
3149 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3150 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3153 out:
3154 ads_msgfree(ads, res);
3156 return name;
3159 /********************************************************************
3160 ********************************************************************/
3162 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3164 LDAPMessage *res = NULL;
3165 ADS_STATUS status;
3166 int count = 0;
3167 char *name = NULL;
3169 status = ads_find_machine_acct(ads, &res, global_myname());
3170 if (!ADS_ERR_OK(status)) {
3171 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3172 global_myname()));
3173 goto out;
3176 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3177 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3178 goto out;
3181 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3182 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3185 out:
3186 ads_msgfree(ads, res);
3188 return name;
3191 #if 0
3193 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3196 * Join a machine to a realm
3197 * Creates the machine account and sets the machine password
3198 * @param ads connection to ads server
3199 * @param machine name of host to add
3200 * @param org_unit Organizational unit to place machine in
3201 * @return status of join
3203 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3204 uint32 account_type, const char *org_unit)
3206 ADS_STATUS status;
3207 LDAPMessage *res = NULL;
3208 char *machine;
3210 /* machine name must be lowercase */
3211 machine = SMB_STRDUP(machine_name);
3212 strlower_m(machine);
3215 status = ads_find_machine_acct(ads, (void **)&res, machine);
3216 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3217 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3218 status = ads_leave_realm(ads, machine);
3219 if (!ADS_ERR_OK(status)) {
3220 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3221 machine, ads->config.realm));
3222 return status;
3226 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3227 if (!ADS_ERR_OK(status)) {
3228 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3229 SAFE_FREE(machine);
3230 return status;
3233 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3234 if (!ADS_ERR_OK(status)) {
3235 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3236 SAFE_FREE(machine);
3237 return status;
3240 SAFE_FREE(machine);
3241 ads_msgfree(ads, res);
3243 return status;
3245 #endif
3248 * Delete a machine from the realm
3249 * @param ads connection to ads server
3250 * @param hostname Machine to remove
3251 * @return status of delete
3253 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3255 ADS_STATUS status;
3256 void *msg;
3257 LDAPMessage *res;
3258 char *hostnameDN, *host;
3259 int rc;
3260 LDAPControl ldap_control;
3261 LDAPControl * pldap_control[2] = {NULL, NULL};
3263 pldap_control[0] = &ldap_control;
3264 memset(&ldap_control, 0, sizeof(LDAPControl));
3265 ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
3267 /* hostname must be lowercase */
3268 host = SMB_STRDUP(hostname);
3269 strlower_m(host);
3271 status = ads_find_machine_acct(ads, &res, host);
3272 if (!ADS_ERR_OK(status)) {
3273 DEBUG(0, ("Host account for %s does not exist.\n", host));
3274 SAFE_FREE(host);
3275 return status;
3278 msg = ads_first_entry(ads, res);
3279 if (!msg) {
3280 SAFE_FREE(host);
3281 return ADS_ERROR_SYSTEM(ENOENT);
3284 hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
3286 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3287 if (rc) {
3288 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3289 }else {
3290 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3293 if (rc != LDAP_SUCCESS) {
3294 const char *attrs[] = { "cn", NULL };
3295 LDAPMessage *msg_sub;
3297 /* we only search with scope ONE, we do not expect any further
3298 * objects to be created deeper */
3300 status = ads_do_search_retry(ads, hostnameDN,
3301 LDAP_SCOPE_ONELEVEL,
3302 "(objectclass=*)", attrs, &res);
3304 if (!ADS_ERR_OK(status)) {
3305 SAFE_FREE(host);
3306 ads_memfree(ads, hostnameDN);
3307 return status;
3310 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3311 msg_sub = ads_next_entry(ads, msg_sub)) {
3313 char *dn = NULL;
3315 if ((dn = ads_get_dn(ads, msg_sub)) == NULL) {
3316 SAFE_FREE(host);
3317 ads_memfree(ads, hostnameDN);
3318 return ADS_ERROR(LDAP_NO_MEMORY);
3321 status = ads_del_dn(ads, dn);
3322 if (!ADS_ERR_OK(status)) {
3323 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3324 SAFE_FREE(host);
3325 ads_memfree(ads, dn);
3326 ads_memfree(ads, hostnameDN);
3327 return status;
3330 ads_memfree(ads, dn);
3333 /* there should be no subordinate objects anymore */
3334 status = ads_do_search_retry(ads, hostnameDN,
3335 LDAP_SCOPE_ONELEVEL,
3336 "(objectclass=*)", attrs, &res);
3338 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3339 SAFE_FREE(host);
3340 ads_memfree(ads, hostnameDN);
3341 return status;
3344 /* delete hostnameDN now */
3345 status = ads_del_dn(ads, hostnameDN);
3346 if (!ADS_ERR_OK(status)) {
3347 SAFE_FREE(host);
3348 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3349 ads_memfree(ads, hostnameDN);
3350 return status;
3354 ads_memfree(ads, hostnameDN);
3356 status = ads_find_machine_acct(ads, &res, host);
3357 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3358 DEBUG(3, ("Failed to remove host account.\n"));
3359 SAFE_FREE(host);
3360 return status;
3363 SAFE_FREE(host);
3364 return status;
3368 * pull all token-sids from an LDAP dn
3369 * @param ads connection to ads server
3370 * @param mem_ctx TALLOC_CTX for allocating sid array
3371 * @param dn of LDAP object
3372 * @param user_sid pointer to DOM_SID (objectSid)
3373 * @param primary_group_sid pointer to DOM_SID (self composed)
3374 * @param sids pointer to sid array to allocate
3375 * @param num_sids counter of SIDs pulled
3376 * @return status of token query
3378 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3379 TALLOC_CTX *mem_ctx,
3380 const char *dn,
3381 DOM_SID *user_sid,
3382 DOM_SID *primary_group_sid,
3383 DOM_SID **sids,
3384 size_t *num_sids)
3386 ADS_STATUS status;
3387 LDAPMessage *res = NULL;
3388 int count = 0;
3389 size_t tmp_num_sids;
3390 DOM_SID *tmp_sids;
3391 DOM_SID tmp_user_sid;
3392 DOM_SID tmp_primary_group_sid;
3393 uint32 pgid;
3394 const char *attrs[] = {
3395 "objectSid",
3396 "tokenGroups",
3397 "primaryGroupID",
3398 NULL
3401 status = ads_search_retry_dn(ads, &res, dn, attrs);
3402 if (!ADS_ERR_OK(status)) {
3403 return status;
3406 count = ads_count_replies(ads, res);
3407 if (count != 1) {
3408 ads_msgfree(ads, res);
3409 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3412 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3413 ads_msgfree(ads, res);
3414 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3417 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3418 ads_msgfree(ads, res);
3419 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3423 /* hack to compose the primary group sid without knowing the
3424 * domsid */
3426 DOM_SID domsid;
3427 uint32 dummy_rid;
3429 sid_copy(&domsid, &tmp_user_sid);
3431 if (!sid_split_rid(&domsid, &dummy_rid)) {
3432 ads_msgfree(ads, res);
3433 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3436 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3437 ads_msgfree(ads, res);
3438 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3442 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3444 if (tmp_num_sids == 0 || !tmp_sids) {
3445 ads_msgfree(ads, res);
3446 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3449 if (num_sids) {
3450 *num_sids = tmp_num_sids;
3453 if (sids) {
3454 *sids = tmp_sids;
3457 if (user_sid) {
3458 *user_sid = tmp_user_sid;
3461 if (primary_group_sid) {
3462 *primary_group_sid = tmp_primary_group_sid;
3465 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3467 ads_msgfree(ads, res);
3468 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3472 * Find a sAMAccoutName in LDAP
3473 * @param ads connection to ads server
3474 * @param mem_ctx TALLOC_CTX for allocating sid array
3475 * @param samaccountname to search
3476 * @param uac_ret uint32 pointer userAccountControl attribute value
3477 * @param dn_ret pointer to dn
3478 * @return status of token query
3480 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3481 TALLOC_CTX *mem_ctx,
3482 const char *samaccountname,
3483 uint32 *uac_ret,
3484 const char **dn_ret)
3486 ADS_STATUS status;
3487 const char *attrs[] = { "userAccountControl", NULL };
3488 const char *filter;
3489 LDAPMessage *res = NULL;
3490 char *dn = NULL;
3491 uint32 uac = 0;
3493 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3494 samaccountname);
3495 if (filter == NULL) {
3496 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3497 goto out;
3500 status = ads_do_search_all(ads, ads->config.bind_path,
3501 LDAP_SCOPE_SUBTREE,
3502 filter, attrs, &res);
3504 if (!ADS_ERR_OK(status)) {
3505 goto out;
3508 if (ads_count_replies(ads, res) != 1) {
3509 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3510 goto out;
3513 dn = ads_get_dn(ads, res);
3514 if (dn == NULL) {
3515 status = ADS_ERROR(LDAP_NO_MEMORY);
3516 goto out;
3519 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3520 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3521 goto out;
3524 if (uac_ret) {
3525 *uac_ret = uac;
3528 if (dn_ret) {
3529 *dn_ret = talloc_strdup(mem_ctx, dn);
3530 if (!*dn_ret) {
3531 status = ADS_ERROR(LDAP_NO_MEMORY);
3532 goto out;
3535 out:
3536 ads_memfree(ads, dn);
3537 ads_msgfree(ads, res);
3539 return status;
3543 * find our configuration path
3544 * @param ads connection to ads server
3545 * @param mem_ctx Pointer to talloc context
3546 * @param config_path Pointer to the config path
3547 * @return status of search
3549 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3550 TALLOC_CTX *mem_ctx,
3551 char **config_path)
3553 ADS_STATUS status;
3554 LDAPMessage *res = NULL;
3555 const char *config_context = NULL;
3556 const char *attrs[] = { "configurationNamingContext", NULL };
3558 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3559 "(objectclass=*)", attrs, &res);
3560 if (!ADS_ERR_OK(status)) {
3561 return status;
3564 config_context = ads_pull_string(ads, mem_ctx, res,
3565 "configurationNamingContext");
3566 ads_msgfree(ads, res);
3567 if (!config_context) {
3568 return ADS_ERROR(LDAP_NO_MEMORY);
3571 if (config_path) {
3572 *config_path = talloc_strdup(mem_ctx, config_context);
3573 if (!*config_path) {
3574 return ADS_ERROR(LDAP_NO_MEMORY);
3578 return ADS_ERROR(LDAP_SUCCESS);
3582 * find the displayName of an extended right
3583 * @param ads connection to ads server
3584 * @param config_path The config path
3585 * @param mem_ctx Pointer to talloc context
3586 * @param GUID struct of the rightsGUID
3587 * @return status of search
3589 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3590 const char *config_path,
3591 TALLOC_CTX *mem_ctx,
3592 const struct GUID *rights_guid)
3594 ADS_STATUS rc;
3595 LDAPMessage *res = NULL;
3596 char *expr = NULL;
3597 const char *attrs[] = { "displayName", NULL };
3598 const char *result = NULL;
3599 const char *path;
3601 if (!ads || !mem_ctx || !rights_guid) {
3602 goto done;
3605 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3606 smb_uuid_string(mem_ctx, *rights_guid));
3607 if (!expr) {
3608 goto done;
3611 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3612 if (!path) {
3613 goto done;
3616 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3617 expr, attrs, &res);
3618 if (!ADS_ERR_OK(rc)) {
3619 goto done;
3622 if (ads_count_replies(ads, res) != 1) {
3623 goto done;
3626 result = ads_pull_string(ads, mem_ctx, res, "displayName");
3628 done:
3629 ads_msgfree(ads, res);
3630 return result;
3635 * verify or build and verify an account ou
3636 * @param mem_ctx Pointer to talloc context
3637 * @param ads connection to ads server
3638 * @param account_ou
3639 * @return status of search
3642 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3643 ADS_STRUCT *ads,
3644 const char **account_ou)
3646 struct ldb_dn *name_dn = NULL;
3647 const char *name = NULL;
3648 char *ou_string = NULL;
3650 name_dn = ldb_dn_explode(mem_ctx, *account_ou);
3651 if (name_dn) {
3652 return ADS_SUCCESS;
3655 ou_string = ads_ou_string(ads, *account_ou);
3656 if (!ou_string) {
3657 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3660 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3661 ads->config.bind_path);
3662 SAFE_FREE(ou_string);
3663 if (!name) {
3664 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3667 name_dn = ldb_dn_explode(mem_ctx, name);
3668 if (!name_dn) {
3669 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3672 *account_ou = talloc_strdup(mem_ctx, name);
3673 if (!*account_ou) {
3674 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3677 return ADS_SUCCESS;
3680 #endif