Always pass a TALLOC_CTX to str_list_make and str_list_copy
[Samba/gbeck.git] / source3 / libads / ldap.c
blobef6feb8ee06948ee0f2174df5d8c84f086e40598
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"
26 #ifdef HAVE_LDAP
28 /**
29 * @file ldap.c
30 * @brief basic ldap client-side routines for ads server communications
32 * The routines contained here should do the necessary ldap calls for
33 * ads setups.
35 * Important note: attribute names passed into ads_ routines must
36 * already be in UTF-8 format. We do not convert them because in almost
37 * all cases, they are just ascii (which is represented with the same
38 * codepoints in UTF-8). This may have to change at some point
39 **/
42 #define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"
44 static SIG_ATOMIC_T gotalarm;
46 /***************************************************************
47 Signal function to tell us we timed out.
48 ****************************************************************/
50 static void gotalarm_sig(void)
52 gotalarm = 1;
55 LDAP *ldap_open_with_timeout(const char *server, int port, unsigned int to)
57 LDAP *ldp = NULL;
60 DEBUG(10, ("Opening connection to LDAP server '%s:%d', timeout "
61 "%u seconds\n", server, port, to));
63 /* Setup timeout */
64 gotalarm = 0;
65 CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
66 alarm(to);
67 /* End setup timeout. */
69 ldp = ldap_open(server, port);
71 if (ldp == NULL) {
72 DEBUG(2,("Could not open connection to LDAP server %s:%d: %s\n",
73 server, port, strerror(errno)));
74 } else {
75 DEBUG(10, ("Connected to LDAP server '%s:%d'\n", server, port));
78 /* Teardown timeout. */
79 CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
80 alarm(0);
82 return ldp;
85 static int ldap_search_with_timeout(LDAP *ld,
86 LDAP_CONST char *base,
87 int scope,
88 LDAP_CONST char *filter,
89 char **attrs,
90 int attrsonly,
91 LDAPControl **sctrls,
92 LDAPControl **cctrls,
93 int sizelimit,
94 LDAPMessage **res )
96 struct timeval timeout;
97 int result;
99 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
100 timeout.tv_sec = lp_ldap_timeout();
101 timeout.tv_usec = 0;
103 /* Setup alarm timeout.... Do we need both of these ? JRA. */
104 gotalarm = 0;
105 CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
106 alarm(lp_ldap_timeout());
107 /* End setup timeout. */
109 result = ldap_search_ext_s(ld, base, scope, filter, attrs,
110 attrsonly, sctrls, cctrls, &timeout,
111 sizelimit, res);
113 /* Teardown timeout. */
114 CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
115 alarm(0);
117 if (gotalarm != 0)
118 return LDAP_TIMELIMIT_EXCEEDED;
120 return result;
123 /**********************************************
124 Do client and server sitename match ?
125 **********************************************/
127 bool ads_sitename_match(ADS_STRUCT *ads)
129 if (ads->config.server_site_name == NULL &&
130 ads->config.client_site_name == NULL ) {
131 DEBUG(10,("ads_sitename_match: both null\n"));
132 return True;
134 if (ads->config.server_site_name &&
135 ads->config.client_site_name &&
136 strequal(ads->config.server_site_name,
137 ads->config.client_site_name)) {
138 DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
139 return True;
141 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
142 ads->config.server_site_name ? ads->config.server_site_name : "NULL",
143 ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
144 return False;
147 /**********************************************
148 Is this the closest DC ?
149 **********************************************/
151 bool ads_closest_dc(ADS_STRUCT *ads)
153 if (ads->config.flags & ADS_CLOSEST) {
154 DEBUG(10,("ads_closest_dc: ADS_CLOSEST flag set\n"));
155 return True;
158 /* not sure if this can ever happen */
159 if (ads_sitename_match(ads)) {
160 DEBUG(10,("ads_closest_dc: ADS_CLOSEST flag not set but sites match\n"));
161 return True;
164 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
165 ads->config.ldap_server_name));
167 return False;
172 try a connection to a given ldap server, returning True and setting the servers IP
173 in the ads struct if successful
175 bool ads_try_connect(ADS_STRUCT *ads, const char *server )
177 char *srv;
178 struct cldap_netlogon_reply cldap_reply;
180 if (!server || !*server) {
181 return False;
184 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
185 server, ads->server.realm));
187 /* this copes with inet_ntoa brokenness */
189 srv = SMB_STRDUP(server);
191 ZERO_STRUCT( cldap_reply );
193 if ( !ads_cldap_netlogon( srv, ads->server.realm, &cldap_reply ) ) {
194 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", srv));
195 SAFE_FREE( srv );
196 return False;
199 /* Check the CLDAP reply flags */
201 if ( !(cldap_reply.flags & ADS_LDAP) ) {
202 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
203 srv));
204 SAFE_FREE( srv );
205 return False;
208 /* Fill in the ads->config values */
210 SAFE_FREE(ads->config.realm);
211 SAFE_FREE(ads->config.bind_path);
212 SAFE_FREE(ads->config.ldap_server_name);
213 SAFE_FREE(ads->config.server_site_name);
214 SAFE_FREE(ads->config.client_site_name);
215 SAFE_FREE(ads->server.workgroup);
217 ads->config.flags = cldap_reply.flags;
218 ads->config.ldap_server_name = SMB_STRDUP(cldap_reply.hostname);
219 strupper_m(cldap_reply.domain);
220 ads->config.realm = SMB_STRDUP(cldap_reply.domain);
221 ads->config.bind_path = ads_build_dn(ads->config.realm);
222 if (*cldap_reply.server_site_name) {
223 ads->config.server_site_name =
224 SMB_STRDUP(cldap_reply.server_site_name);
226 if (*cldap_reply.client_site_name) {
227 ads->config.client_site_name =
228 SMB_STRDUP(cldap_reply.client_site_name);
230 ads->server.workgroup = SMB_STRDUP(cldap_reply.netbios_domain);
232 ads->ldap.port = LDAP_PORT;
233 if (!interpret_string_addr(&ads->ldap.ss, srv, 0)) {
234 DEBUG(1,("ads_try_connect: unable to convert %s "
235 "to an address\n",
236 srv));
237 SAFE_FREE( srv );
238 return False;
241 SAFE_FREE(srv);
243 /* Store our site name. */
244 sitename_store( cldap_reply.domain, cldap_reply.client_site_name );
246 return True;
249 /**********************************************************************
250 Try to find an AD dc using our internal name resolution routines
251 Try the realm first and then then workgroup name if netbios is not
252 disabled
253 **********************************************************************/
255 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
257 const char *c_realm;
258 int count, i=0;
259 struct ip_service *ip_list;
260 const char *realm;
261 bool got_realm = False;
262 bool use_own_domain = False;
263 char *sitename;
264 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
266 /* if the realm and workgroup are both empty, assume they are ours */
268 /* realm */
269 c_realm = ads->server.realm;
271 if ( !c_realm || !*c_realm ) {
272 /* special case where no realm and no workgroup means our own */
273 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
274 use_own_domain = True;
275 c_realm = lp_realm();
279 if (c_realm && *c_realm)
280 got_realm = True;
282 /* we need to try once with the realm name and fallback to the
283 netbios domain name if we fail (if netbios has not been disabled */
285 if ( !got_realm && !lp_disable_netbios() ) {
286 c_realm = ads->server.workgroup;
287 if (!c_realm || !*c_realm) {
288 if ( use_own_domain )
289 c_realm = lp_workgroup();
292 if ( !c_realm || !*c_realm ) {
293 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
294 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
298 realm = c_realm;
300 sitename = sitename_fetch(realm);
302 again:
304 DEBUG(6,("ads_find_dc: looking for %s '%s'\n",
305 (got_realm ? "realm" : "domain"), realm));
307 status = get_sorted_dc_list(realm, sitename, &ip_list, &count, got_realm);
308 if (!NT_STATUS_IS_OK(status)) {
309 /* fall back to netbios if we can */
310 if ( got_realm && !lp_disable_netbios() ) {
311 got_realm = False;
312 goto again;
315 SAFE_FREE(sitename);
316 return status;
319 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
320 for ( i=0; i<count; i++ ) {
321 char server[INET6_ADDRSTRLEN];
323 print_sockaddr(server, sizeof(server), &ip_list[i].ss);
325 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
326 continue;
328 if (!got_realm) {
329 /* realm in this case is a workgroup name. We need
330 to ignore any IP addresses in the negative connection
331 cache that match ip addresses returned in the ad realm
332 case. It sucks that I have to reproduce the logic above... */
333 c_realm = ads->server.realm;
334 if ( !c_realm || !*c_realm ) {
335 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
336 c_realm = lp_realm();
339 if (c_realm && *c_realm &&
340 !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm, server))) {
341 /* Ensure we add the workgroup name for this
342 IP address as negative too. */
343 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
344 continue;
348 if ( ads_try_connect(ads, server) ) {
349 SAFE_FREE(ip_list);
350 SAFE_FREE(sitename);
351 return NT_STATUS_OK;
354 /* keep track of failures */
355 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
358 SAFE_FREE(ip_list);
360 /* In case we failed to contact one of our closest DC on our site we
361 * need to try to find another DC, retry with a site-less SRV DNS query
362 * - Guenther */
364 if (sitename) {
365 DEBUG(1,("ads_find_dc: failed to find a valid DC on our site (%s), "
366 "trying to find another DC\n", sitename));
367 SAFE_FREE(sitename);
368 namecache_delete(realm, 0x1C);
369 goto again;
372 return NT_STATUS_NO_LOGON_SERVERS;
377 * Connect to the LDAP server
378 * @param ads Pointer to an existing ADS_STRUCT
379 * @return status of connection
381 ADS_STATUS ads_connect(ADS_STRUCT *ads)
383 int version = LDAP_VERSION3;
384 ADS_STATUS status;
385 NTSTATUS ntstatus;
386 char addr[INET6_ADDRSTRLEN];
388 ZERO_STRUCT(ads->ldap);
389 ads->ldap.last_attempt = time(NULL);
390 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
392 /* try with a user specified server */
394 if (DEBUGLEVEL >= 11) {
395 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
396 DEBUG(11,("ads_connect: entering\n"));
397 DEBUGADD(11,("%s\n", s));
398 TALLOC_FREE(s);
401 if (ads->server.ldap_server &&
402 ads_try_connect(ads, ads->server.ldap_server)) {
403 goto got_connection;
406 ntstatus = ads_find_dc(ads);
407 if (NT_STATUS_IS_OK(ntstatus)) {
408 goto got_connection;
411 status = ADS_ERROR_NT(ntstatus);
412 goto out;
414 got_connection:
416 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
417 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
419 if (!ads->auth.user_name) {
420 /* Must use the userPrincipalName value here or sAMAccountName
421 and not servicePrincipalName; found by Guenther Deschner */
423 asprintf(&ads->auth.user_name, "%s$", global_myname() );
426 if (!ads->auth.realm) {
427 ads->auth.realm = SMB_STRDUP(ads->config.realm);
430 if (!ads->auth.kdc_server) {
431 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
432 ads->auth.kdc_server = SMB_STRDUP(addr);
435 #if KRB5_DNS_HACK
436 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
437 to MIT kerberos to work (tridge) */
439 char *env;
440 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
441 setenv(env, ads->auth.kdc_server, 1);
442 free(env);
444 #endif
446 /* If the caller() requested no LDAP bind, then we are done */
448 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
449 status = ADS_SUCCESS;
450 goto out;
453 ads->ldap.mem_ctx = talloc_init("ads LDAP connection memory");
454 if (!ads->ldap.mem_ctx) {
455 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
456 goto out;
459 /* Otherwise setup the TCP LDAP session */
461 ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name,
462 LDAP_PORT, lp_ldap_timeout());
463 if (ads->ldap.ld == NULL) {
464 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
465 goto out;
467 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
469 /* cache the successful connection for workgroup and realm */
470 if (ads_closest_dc(ads)) {
471 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
472 saf_store( ads->server.workgroup, addr);
473 saf_store( ads->server.realm, addr);
476 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
478 status = ADS_ERROR(smb_ldap_start_tls(ads->ldap.ld, version));
479 if (!ADS_ERR_OK(status)) {
480 goto out;
483 /* fill in the current time and offsets */
485 status = ads_current_time( ads );
486 if ( !ADS_ERR_OK(status) ) {
487 goto out;
490 /* Now do the bind */
492 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
493 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
494 goto out;
497 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
498 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
499 goto out;
502 status = ads_sasl_bind(ads);
504 out:
505 if (DEBUGLEVEL >= 11) {
506 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
507 DEBUG(11,("ads_connect: leaving with: %s\n",
508 ads_errstr(status)));
509 DEBUGADD(11,("%s\n", s));
510 TALLOC_FREE(s);
513 return status;
517 * Disconnect the LDAP server
518 * @param ads Pointer to an existing ADS_STRUCT
520 void ads_disconnect(ADS_STRUCT *ads)
522 if (ads->ldap.ld) {
523 ldap_unbind(ads->ldap.ld);
524 ads->ldap.ld = NULL;
526 if (ads->ldap.wrap_ops && ads->ldap.wrap_ops->disconnect) {
527 ads->ldap.wrap_ops->disconnect(ads);
529 if (ads->ldap.mem_ctx) {
530 talloc_free(ads->ldap.mem_ctx);
532 ZERO_STRUCT(ads->ldap);
536 Duplicate a struct berval into talloc'ed memory
538 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
540 struct berval *value;
542 if (!in_val) return NULL;
544 value = TALLOC_ZERO_P(ctx, struct berval);
545 if (value == NULL)
546 return NULL;
547 if (in_val->bv_len == 0) return value;
549 value->bv_len = in_val->bv_len;
550 value->bv_val = (char *)TALLOC_MEMDUP(ctx, in_val->bv_val,
551 in_val->bv_len);
552 return value;
556 Make a values list out of an array of (struct berval *)
558 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
559 const struct berval **in_vals)
561 struct berval **values;
562 int i;
564 if (!in_vals) return NULL;
565 for (i=0; in_vals[i]; i++)
566 ; /* count values */
567 values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
568 if (!values) return NULL;
570 for (i=0; in_vals[i]; i++) {
571 values[i] = dup_berval(ctx, in_vals[i]);
573 return values;
577 UTF8-encode a values list out of an array of (char *)
579 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
581 char **values;
582 int i;
584 if (!in_vals) return NULL;
585 for (i=0; in_vals[i]; i++)
586 ; /* count values */
587 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
588 if (!values) return NULL;
590 for (i=0; in_vals[i]; i++) {
591 push_utf8_talloc(ctx, &values[i], in_vals[i]);
593 return values;
597 Pull a (char *) array out of a UTF8-encoded values list
599 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
601 char **values;
602 int i;
604 if (!in_vals) return NULL;
605 for (i=0; in_vals[i]; i++)
606 ; /* count values */
607 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
608 if (!values) return NULL;
610 for (i=0; in_vals[i]; i++) {
611 pull_utf8_talloc(ctx, &values[i], in_vals[i]);
613 return values;
617 * Do a search with paged results. cookie must be null on the first
618 * call, and then returned on each subsequent call. It will be null
619 * again when the entire search is complete
620 * @param ads connection to ads server
621 * @param bind_path Base dn for the search
622 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
623 * @param expr Search expression - specified in local charset
624 * @param attrs Attributes to retrieve - specified in utf8 or ascii
625 * @param res ** which will contain results - free res* with ads_msgfree()
626 * @param count Number of entries retrieved on this page
627 * @param cookie The paged results cookie to be returned on subsequent calls
628 * @return status of search
630 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
631 const char *bind_path,
632 int scope, const char *expr,
633 const char **attrs, void *args,
634 LDAPMessage **res,
635 int *count, struct berval **cookie)
637 int rc, i, version;
638 char *utf8_expr, *utf8_path, **search_attrs;
639 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
640 BerElement *cookie_be = NULL;
641 struct berval *cookie_bv= NULL;
642 BerElement *ext_be = NULL;
643 struct berval *ext_bv= NULL;
645 TALLOC_CTX *ctx;
646 ads_control *external_control = (ads_control *) args;
648 *res = NULL;
650 if (!(ctx = talloc_init("ads_do_paged_search_args")))
651 return ADS_ERROR(LDAP_NO_MEMORY);
653 /* 0 means the conversion worked but the result was empty
654 so we only fail if it's -1. In any case, it always
655 at least nulls out the dest */
656 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
657 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
658 rc = LDAP_NO_MEMORY;
659 goto done;
662 if (!attrs || !(*attrs))
663 search_attrs = NULL;
664 else {
665 /* This would be the utf8-encoded version...*/
666 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
667 if (!(str_list_copy(talloc_tos(), &search_attrs, attrs))) {
668 rc = LDAP_NO_MEMORY;
669 goto done;
673 /* Paged results only available on ldap v3 or later */
674 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
675 if (version < LDAP_VERSION3) {
676 rc = LDAP_NOT_SUPPORTED;
677 goto done;
680 cookie_be = ber_alloc_t(LBER_USE_DER);
681 if (*cookie) {
682 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
683 ber_bvfree(*cookie); /* don't need it from last time */
684 *cookie = NULL;
685 } else {
686 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
688 ber_flatten(cookie_be, &cookie_bv);
689 PagedResults.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID);
690 PagedResults.ldctl_iscritical = (char) 1;
691 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
692 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
694 NoReferrals.ldctl_oid = CONST_DISCARD(char *, ADS_NO_REFERRALS_OID);
695 NoReferrals.ldctl_iscritical = (char) 0;
696 NoReferrals.ldctl_value.bv_len = 0;
697 NoReferrals.ldctl_value.bv_val = CONST_DISCARD(char *, "");
699 if (external_control &&
700 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
701 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
703 ExternalCtrl.ldctl_oid = CONST_DISCARD(char *, external_control->control);
704 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
706 /* win2k does not accept a ldctl_value beeing passed in */
708 if (external_control->val != 0) {
710 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
711 rc = LDAP_NO_MEMORY;
712 goto done;
715 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
716 rc = LDAP_NO_MEMORY;
717 goto done;
719 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
720 rc = LDAP_NO_MEMORY;
721 goto done;
724 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
725 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
727 } else {
728 ExternalCtrl.ldctl_value.bv_len = 0;
729 ExternalCtrl.ldctl_value.bv_val = NULL;
732 controls[0] = &NoReferrals;
733 controls[1] = &PagedResults;
734 controls[2] = &ExternalCtrl;
735 controls[3] = NULL;
737 } else {
738 controls[0] = &NoReferrals;
739 controls[1] = &PagedResults;
740 controls[2] = NULL;
743 /* we need to disable referrals as the openldap libs don't
744 handle them and paged results at the same time. Using them
745 together results in the result record containing the server
746 page control being removed from the result list (tridge/jmcd)
748 leaving this in despite the control that says don't generate
749 referrals, in case the server doesn't support it (jmcd)
751 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
753 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
754 search_attrs, 0, controls,
755 NULL, LDAP_NO_LIMIT,
756 (LDAPMessage **)res);
758 ber_free(cookie_be, 1);
759 ber_bvfree(cookie_bv);
761 if (rc) {
762 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
763 ldap_err2string(rc)));
764 goto done;
767 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
768 NULL, &rcontrols, 0);
770 if (!rcontrols) {
771 goto done;
774 for (i=0; rcontrols[i]; i++) {
775 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
776 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
777 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
778 &cookie_bv);
779 /* the berval is the cookie, but must be freed when
780 it is all done */
781 if (cookie_bv->bv_len) /* still more to do */
782 *cookie=ber_bvdup(cookie_bv);
783 else
784 *cookie=NULL;
785 ber_bvfree(cookie_bv);
786 ber_free(cookie_be, 1);
787 break;
790 ldap_controls_free(rcontrols);
792 done:
793 talloc_destroy(ctx);
795 if (ext_be) {
796 ber_free(ext_be, 1);
799 if (ext_bv) {
800 ber_bvfree(ext_bv);
803 /* if/when we decide to utf8-encode attrs, take out this next line */
804 str_list_free(&search_attrs);
806 return ADS_ERROR(rc);
809 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
810 int scope, const char *expr,
811 const char **attrs, LDAPMessage **res,
812 int *count, struct berval **cookie)
814 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
819 * Get all results for a search. This uses ads_do_paged_search() to return
820 * all entries in a large search.
821 * @param ads connection to ads server
822 * @param bind_path Base dn for the search
823 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
824 * @param expr Search expression
825 * @param attrs Attributes to retrieve
826 * @param res ** which will contain results - free res* with ads_msgfree()
827 * @return status of search
829 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
830 int scope, const char *expr,
831 const char **attrs, void *args,
832 LDAPMessage **res)
834 struct berval *cookie = NULL;
835 int count = 0;
836 ADS_STATUS status;
838 *res = NULL;
839 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
840 &count, &cookie);
842 if (!ADS_ERR_OK(status))
843 return status;
845 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
846 while (cookie) {
847 LDAPMessage *res2 = NULL;
848 ADS_STATUS status2;
849 LDAPMessage *msg, *next;
851 status2 = ads_do_paged_search_args(ads, bind_path, scope, expr,
852 attrs, args, &res2, &count, &cookie);
854 if (!ADS_ERR_OK(status2)) break;
856 /* this relies on the way that ldap_add_result_entry() works internally. I hope
857 that this works on all ldap libs, but I have only tested with openldap */
858 for (msg = ads_first_entry(ads, res2); msg; msg = next) {
859 next = ads_next_entry(ads, msg);
860 ldap_add_result_entry((LDAPMessage **)res, msg);
862 /* note that we do not free res2, as the memory is now
863 part of the main returned list */
865 #else
866 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
867 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
868 #endif
870 return status;
873 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
874 int scope, const char *expr,
875 const char **attrs, LDAPMessage **res)
877 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
880 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
881 int scope, const char *expr,
882 const char **attrs, uint32 sd_flags,
883 LDAPMessage **res)
885 ads_control args;
887 args.control = ADS_SD_FLAGS_OID;
888 args.val = sd_flags;
889 args.critical = True;
891 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
896 * Run a function on all results for a search. Uses ads_do_paged_search() and
897 * runs the function as each page is returned, using ads_process_results()
898 * @param ads connection to ads server
899 * @param bind_path Base dn for the search
900 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
901 * @param expr Search expression - specified in local charset
902 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
903 * @param fn Function which takes attr name, values list, and data_area
904 * @param data_area Pointer which is passed to function on each call
905 * @return status of search
907 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
908 int scope, const char *expr, const char **attrs,
909 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
910 void *data_area)
912 struct berval *cookie = NULL;
913 int count = 0;
914 ADS_STATUS status;
915 LDAPMessage *res;
917 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
918 &count, &cookie);
920 if (!ADS_ERR_OK(status)) return status;
922 ads_process_results(ads, res, fn, data_area);
923 ads_msgfree(ads, res);
925 while (cookie) {
926 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
927 &res, &count, &cookie);
929 if (!ADS_ERR_OK(status)) break;
931 ads_process_results(ads, res, fn, data_area);
932 ads_msgfree(ads, res);
935 return status;
939 * Do a search with a timeout.
940 * @param ads connection to ads server
941 * @param bind_path Base dn for the search
942 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
943 * @param expr Search expression
944 * @param attrs Attributes to retrieve
945 * @param res ** which will contain results - free res* with ads_msgfree()
946 * @return status of search
948 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
949 const char *expr,
950 const char **attrs, LDAPMessage **res)
952 int rc;
953 char *utf8_expr, *utf8_path, **search_attrs = NULL;
954 TALLOC_CTX *ctx;
956 *res = NULL;
957 if (!(ctx = talloc_init("ads_do_search"))) {
958 DEBUG(1,("ads_do_search: talloc_init() failed!"));
959 return ADS_ERROR(LDAP_NO_MEMORY);
962 /* 0 means the conversion worked but the result was empty
963 so we only fail if it's negative. In any case, it always
964 at least nulls out the dest */
965 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
966 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
967 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
968 rc = LDAP_NO_MEMORY;
969 goto done;
972 if (!attrs || !(*attrs))
973 search_attrs = NULL;
974 else {
975 /* This would be the utf8-encoded version...*/
976 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
977 if (!(str_list_copy(talloc_tos(), &search_attrs, attrs)))
979 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
980 rc = LDAP_NO_MEMORY;
981 goto done;
985 /* see the note in ads_do_paged_search - we *must* disable referrals */
986 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
988 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
989 search_attrs, 0, NULL, NULL,
990 LDAP_NO_LIMIT,
991 (LDAPMessage **)res);
993 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
994 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
995 rc = 0;
998 done:
999 talloc_destroy(ctx);
1000 /* if/when we decide to utf8-encode attrs, take out this next line */
1001 str_list_free(&search_attrs);
1002 return ADS_ERROR(rc);
1005 * Do a general ADS search
1006 * @param ads connection to ads server
1007 * @param res ** which will contain results - free res* with ads_msgfree()
1008 * @param expr Search expression
1009 * @param attrs Attributes to retrieve
1010 * @return status of search
1012 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1013 const char *expr, const char **attrs)
1015 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1016 expr, attrs, res);
1020 * Do a search on a specific DistinguishedName
1021 * @param ads connection to ads server
1022 * @param res ** which will contain results - free res* with ads_msgfree()
1023 * @param dn DistinguishName to search
1024 * @param attrs Attributes to retrieve
1025 * @return status of search
1027 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1028 const char *dn, const char **attrs)
1030 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1031 attrs, res);
1035 * Free up memory from a ads_search
1036 * @param ads connection to ads server
1037 * @param msg Search results to free
1039 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1041 if (!msg) return;
1042 ldap_msgfree(msg);
1046 * Free up memory from various ads requests
1047 * @param ads connection to ads server
1048 * @param mem Area to free
1050 void ads_memfree(ADS_STRUCT *ads, void *mem)
1052 SAFE_FREE(mem);
1056 * Get a dn from search results
1057 * @param ads connection to ads server
1058 * @param msg Search result
1059 * @return dn string
1061 char *ads_get_dn(ADS_STRUCT *ads, LDAPMessage *msg)
1063 char *utf8_dn, *unix_dn;
1065 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1067 if (!utf8_dn) {
1068 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1069 return NULL;
1072 if (pull_utf8_allocate(&unix_dn, utf8_dn) == (size_t)-1) {
1073 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1074 utf8_dn ));
1075 return NULL;
1077 ldap_memfree(utf8_dn);
1078 return unix_dn;
1082 * Get the parent from a dn
1083 * @param dn the dn to return the parent from
1084 * @return parent dn string
1086 char *ads_parent_dn(const char *dn)
1088 char *p;
1090 if (dn == NULL) {
1091 return NULL;
1094 p = strchr(dn, ',');
1096 if (p == NULL) {
1097 return NULL;
1100 return p+1;
1104 * Find a machine account given a hostname
1105 * @param ads connection to ads server
1106 * @param res ** which will contain results - free res* with ads_msgfree()
1107 * @param host Hostname to search for
1108 * @return status of search
1110 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1111 const char *machine)
1113 ADS_STATUS status;
1114 char *expr;
1115 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1117 *res = NULL;
1119 /* the easiest way to find a machine account anywhere in the tree
1120 is to look for hostname$ */
1121 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1122 DEBUG(1, ("asprintf failed!\n"));
1123 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1126 status = ads_search(ads, res, expr, attrs);
1127 SAFE_FREE(expr);
1128 return status;
1132 * Initialize a list of mods to be used in a modify request
1133 * @param ctx An initialized TALLOC_CTX
1134 * @return allocated ADS_MODLIST
1136 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1138 #define ADS_MODLIST_ALLOC_SIZE 10
1139 LDAPMod **mods;
1141 if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1142 /* -1 is safety to make sure we don't go over the end.
1143 need to reset it to NULL before doing ldap modify */
1144 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1146 return (ADS_MODLIST)mods;
1151 add an attribute to the list, with values list already constructed
1153 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1154 int mod_op, const char *name,
1155 const void *_invals)
1157 const void **invals = (const void **)_invals;
1158 int curmod;
1159 LDAPMod **modlist = (LDAPMod **) *mods;
1160 struct berval **ber_values = NULL;
1161 char **char_values = NULL;
1163 if (!invals) {
1164 mod_op = LDAP_MOD_DELETE;
1165 } else {
1166 if (mod_op & LDAP_MOD_BVALUES)
1167 ber_values = ads_dup_values(ctx,
1168 (const struct berval **)invals);
1169 else
1170 char_values = ads_push_strvals(ctx,
1171 (const char **) invals);
1174 /* find the first empty slot */
1175 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1176 curmod++);
1177 if (modlist[curmod] == (LDAPMod *) -1) {
1178 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
1179 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1180 return ADS_ERROR(LDAP_NO_MEMORY);
1181 memset(&modlist[curmod], 0,
1182 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1183 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1184 *mods = (ADS_MODLIST)modlist;
1187 if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
1188 return ADS_ERROR(LDAP_NO_MEMORY);
1189 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1190 if (mod_op & LDAP_MOD_BVALUES) {
1191 modlist[curmod]->mod_bvalues = ber_values;
1192 } else if (mod_op & LDAP_MOD_DELETE) {
1193 modlist[curmod]->mod_values = NULL;
1194 } else {
1195 modlist[curmod]->mod_values = char_values;
1198 modlist[curmod]->mod_op = mod_op;
1199 return ADS_ERROR(LDAP_SUCCESS);
1203 * Add a single string value to a mod list
1204 * @param ctx An initialized TALLOC_CTX
1205 * @param mods An initialized ADS_MODLIST
1206 * @param name The attribute name to add
1207 * @param val The value to add - NULL means DELETE
1208 * @return ADS STATUS indicating success of add
1210 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1211 const char *name, const char *val)
1213 const char *values[2];
1215 values[0] = val;
1216 values[1] = NULL;
1218 if (!val)
1219 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1220 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1224 * Add an array of string values to a mod list
1225 * @param ctx An initialized TALLOC_CTX
1226 * @param mods An initialized ADS_MODLIST
1227 * @param name The attribute name to add
1228 * @param vals The array of string values to add - NULL means DELETE
1229 * @return ADS STATUS indicating success of add
1231 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1232 const char *name, const char **vals)
1234 if (!vals)
1235 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1236 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1237 name, (const void **) vals);
1240 #if 0
1242 * Add a single ber-encoded value to a mod list
1243 * @param ctx An initialized TALLOC_CTX
1244 * @param mods An initialized ADS_MODLIST
1245 * @param name The attribute name to add
1246 * @param val The value to add - NULL means DELETE
1247 * @return ADS STATUS indicating success of add
1249 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1250 const char *name, const struct berval *val)
1252 const struct berval *values[2];
1254 values[0] = val;
1255 values[1] = NULL;
1256 if (!val)
1257 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1258 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1259 name, (const void **) values);
1261 #endif
1264 * Perform an ldap modify
1265 * @param ads connection to ads server
1266 * @param mod_dn DistinguishedName to modify
1267 * @param mods list of modifications to perform
1268 * @return status of modify
1270 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1272 int ret,i;
1273 char *utf8_dn = NULL;
1275 this control is needed to modify that contains a currently
1276 non-existent attribute (but allowable for the object) to run
1278 LDAPControl PermitModify = {
1279 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
1280 {0, NULL},
1281 (char) 1};
1282 LDAPControl *controls[2];
1284 controls[0] = &PermitModify;
1285 controls[1] = NULL;
1287 if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) {
1288 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1291 /* find the end of the list, marked by NULL or -1 */
1292 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1293 /* make sure the end of the list is NULL */
1294 mods[i] = NULL;
1295 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1296 (LDAPMod **) mods, controls, NULL);
1297 SAFE_FREE(utf8_dn);
1298 return ADS_ERROR(ret);
1302 * Perform an ldap add
1303 * @param ads connection to ads server
1304 * @param new_dn DistinguishedName to add
1305 * @param mods list of attributes and values for DN
1306 * @return status of add
1308 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1310 int ret, i;
1311 char *utf8_dn = NULL;
1313 if (push_utf8_allocate(&utf8_dn, new_dn) == -1) {
1314 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1315 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1318 /* find the end of the list, marked by NULL or -1 */
1319 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1320 /* make sure the end of the list is NULL */
1321 mods[i] = NULL;
1323 ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1324 SAFE_FREE(utf8_dn);
1325 return ADS_ERROR(ret);
1329 * Delete a DistinguishedName
1330 * @param ads connection to ads server
1331 * @param new_dn DistinguishedName to delete
1332 * @return status of delete
1334 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1336 int ret;
1337 char *utf8_dn = NULL;
1338 if (push_utf8_allocate(&utf8_dn, del_dn) == -1) {
1339 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1340 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1343 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1344 SAFE_FREE(utf8_dn);
1345 return ADS_ERROR(ret);
1349 * Build an org unit string
1350 * if org unit is Computers or blank then assume a container, otherwise
1351 * assume a / separated list of organisational units.
1352 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1353 * @param ads connection to ads server
1354 * @param org_unit Organizational unit
1355 * @return org unit string - caller must free
1357 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1359 char *ret = NULL;
1361 if (!org_unit || !*org_unit) {
1363 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1365 /* samba4 might not yet respond to a wellknownobject-query */
1366 return ret ? ret : SMB_STRDUP("cn=Computers");
1369 if (strequal(org_unit, "Computers")) {
1370 return SMB_STRDUP("cn=Computers");
1373 /* jmcd: removed "\\" from the separation chars, because it is
1374 needed as an escape for chars like '#' which are valid in an
1375 OU name */
1376 return ads_build_path(org_unit, "/", "ou=", 1);
1380 * Get a org unit string for a well-known GUID
1381 * @param ads connection to ads server
1382 * @param wknguid Well known GUID
1383 * @return org unit string - caller must free
1385 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1387 ADS_STATUS status;
1388 LDAPMessage *res = NULL;
1389 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1390 **bind_dn_exp = NULL;
1391 const char *attrs[] = {"distinguishedName", NULL};
1392 int new_ln, wkn_ln, bind_ln, i;
1394 if (wknguid == NULL) {
1395 return NULL;
1398 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1399 DEBUG(1, ("asprintf failed!\n"));
1400 return NULL;
1403 status = ads_search_dn(ads, &res, base, attrs);
1404 if (!ADS_ERR_OK(status)) {
1405 DEBUG(1,("Failed while searching for: %s\n", base));
1406 goto out;
1409 if (ads_count_replies(ads, res) != 1) {
1410 goto out;
1413 /* substitute the bind-path from the well-known-guid-search result */
1414 wkn_dn = ads_get_dn(ads, res);
1415 if (!wkn_dn) {
1416 goto out;
1419 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1420 if (!wkn_dn_exp) {
1421 goto out;
1424 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1425 if (!bind_dn_exp) {
1426 goto out;
1429 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1431 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1434 new_ln = wkn_ln - bind_ln;
1436 ret = SMB_STRDUP(wkn_dn_exp[0]);
1437 if (!ret) {
1438 goto out;
1441 for (i=1; i < new_ln; i++) {
1442 char *s = NULL;
1444 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1445 SAFE_FREE(ret);
1446 goto out;
1449 SAFE_FREE(ret);
1450 ret = SMB_STRDUP(s);
1451 free(s);
1452 if (!ret) {
1453 goto out;
1457 out:
1458 SAFE_FREE(base);
1459 ads_msgfree(ads, res);
1460 ads_memfree(ads, wkn_dn);
1461 if (wkn_dn_exp) {
1462 ldap_value_free(wkn_dn_exp);
1464 if (bind_dn_exp) {
1465 ldap_value_free(bind_dn_exp);
1468 return ret;
1472 * Adds (appends) an item to an attribute array, rather then
1473 * replacing the whole list
1474 * @param ctx An initialized TALLOC_CTX
1475 * @param mods An initialized ADS_MODLIST
1476 * @param name name of the ldap attribute to append to
1477 * @param vals an array of values to add
1478 * @return status of addition
1481 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1482 const char *name, const char **vals)
1484 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1485 (const void *) vals);
1489 * Determines the computer account's current KVNO via an LDAP lookup
1490 * @param ads An initialized ADS_STRUCT
1491 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1492 * @return the kvno for the computer account, or -1 in case of a failure.
1495 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *machine_name)
1497 LDAPMessage *res = NULL;
1498 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1499 char *filter;
1500 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1501 char *dn_string = NULL;
1502 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1504 DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name));
1505 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1506 return kvno;
1508 ret = ads_search(ads, &res, filter, attrs);
1509 SAFE_FREE(filter);
1510 if (!ADS_ERR_OK(ret) && ads_count_replies(ads, res)) {
1511 DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name));
1512 ads_msgfree(ads, res);
1513 return kvno;
1516 dn_string = ads_get_dn(ads, res);
1517 if (!dn_string) {
1518 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1519 ads_msgfree(ads, res);
1520 return kvno;
1522 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1523 ads_memfree(ads, dn_string);
1525 /* ---------------------------------------------------------
1526 * 0 is returned as a default KVNO from this point on...
1527 * This is done because Windows 2000 does not support key
1528 * version numbers. Chances are that a failure in the next
1529 * step is simply due to Windows 2000 being used for a
1530 * domain controller. */
1531 kvno = 0;
1533 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1534 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1535 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1536 ads_msgfree(ads, res);
1537 return kvno;
1540 /* Success */
1541 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1542 ads_msgfree(ads, res);
1543 return kvno;
1547 * This clears out all registered spn's for a given hostname
1548 * @param ads An initilaized ADS_STRUCT
1549 * @param machine_name the NetBIOS name of the computer.
1550 * @return 0 upon success, non-zero otherwise.
1553 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1555 TALLOC_CTX *ctx;
1556 LDAPMessage *res = NULL;
1557 ADS_MODLIST mods;
1558 const char *servicePrincipalName[1] = {NULL};
1559 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1560 char *dn_string = NULL;
1562 ret = ads_find_machine_acct(ads, &res, machine_name);
1563 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1564 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1565 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1566 ads_msgfree(ads, res);
1567 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1570 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1571 ctx = talloc_init("ads_clear_service_principal_names");
1572 if (!ctx) {
1573 ads_msgfree(ads, res);
1574 return ADS_ERROR(LDAP_NO_MEMORY);
1577 if (!(mods = ads_init_mods(ctx))) {
1578 talloc_destroy(ctx);
1579 ads_msgfree(ads, res);
1580 return ADS_ERROR(LDAP_NO_MEMORY);
1582 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1583 if (!ADS_ERR_OK(ret)) {
1584 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1585 ads_msgfree(ads, res);
1586 talloc_destroy(ctx);
1587 return ret;
1589 dn_string = ads_get_dn(ads, res);
1590 if (!dn_string) {
1591 talloc_destroy(ctx);
1592 ads_msgfree(ads, res);
1593 return ADS_ERROR(LDAP_NO_MEMORY);
1595 ret = ads_gen_mod(ads, dn_string, mods);
1596 ads_memfree(ads,dn_string);
1597 if (!ADS_ERR_OK(ret)) {
1598 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1599 machine_name));
1600 ads_msgfree(ads, res);
1601 talloc_destroy(ctx);
1602 return ret;
1605 ads_msgfree(ads, res);
1606 talloc_destroy(ctx);
1607 return ret;
1611 * This adds a service principal name to an existing computer account
1612 * (found by hostname) in AD.
1613 * @param ads An initialized ADS_STRUCT
1614 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1615 * @param my_fqdn The fully qualified DNS name of the machine
1616 * @param spn A string of the service principal to add, i.e. 'host'
1617 * @return 0 upon sucess, or non-zero if a failure occurs
1620 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1621 const char *my_fqdn, const char *spn)
1623 ADS_STATUS ret;
1624 TALLOC_CTX *ctx;
1625 LDAPMessage *res = NULL;
1626 char *psp1, *psp2;
1627 ADS_MODLIST mods;
1628 char *dn_string = NULL;
1629 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1631 ret = ads_find_machine_acct(ads, &res, machine_name);
1632 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1633 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1634 machine_name));
1635 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1636 spn, machine_name, ads->config.realm));
1637 ads_msgfree(ads, res);
1638 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1641 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1642 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1643 ads_msgfree(ads, res);
1644 return ADS_ERROR(LDAP_NO_MEMORY);
1647 /* add short name spn */
1649 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1650 talloc_destroy(ctx);
1651 ads_msgfree(ads, res);
1652 return ADS_ERROR(LDAP_NO_MEMORY);
1654 strupper_m(psp1);
1655 strlower_m(&psp1[strlen(spn)]);
1656 servicePrincipalName[0] = psp1;
1658 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1659 psp1, machine_name));
1662 /* add fully qualified spn */
1664 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1665 ret = ADS_ERROR(LDAP_NO_MEMORY);
1666 goto out;
1668 strupper_m(psp2);
1669 strlower_m(&psp2[strlen(spn)]);
1670 servicePrincipalName[1] = psp2;
1672 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1673 psp2, machine_name));
1675 if ( (mods = ads_init_mods(ctx)) == NULL ) {
1676 ret = ADS_ERROR(LDAP_NO_MEMORY);
1677 goto out;
1680 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1681 if (!ADS_ERR_OK(ret)) {
1682 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1683 goto out;
1686 if ( (dn_string = ads_get_dn(ads, res)) == NULL ) {
1687 ret = ADS_ERROR(LDAP_NO_MEMORY);
1688 goto out;
1691 ret = ads_gen_mod(ads, dn_string, mods);
1692 ads_memfree(ads,dn_string);
1693 if (!ADS_ERR_OK(ret)) {
1694 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1695 goto out;
1698 out:
1699 TALLOC_FREE( ctx );
1700 ads_msgfree(ads, res);
1701 return ret;
1705 * adds a machine account to the ADS server
1706 * @param ads An intialized ADS_STRUCT
1707 * @param machine_name - the NetBIOS machine name of this account.
1708 * @param account_type A number indicating the type of account to create
1709 * @param org_unit The LDAP path in which to place this account
1710 * @return 0 upon success, or non-zero otherwise
1713 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1714 const char *org_unit)
1716 ADS_STATUS ret;
1717 char *samAccountName, *controlstr;
1718 TALLOC_CTX *ctx;
1719 ADS_MODLIST mods;
1720 char *machine_escaped = NULL;
1721 char *new_dn;
1722 const char *objectClass[] = {"top", "person", "organizationalPerson",
1723 "user", "computer", NULL};
1724 LDAPMessage *res = NULL;
1725 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
1726 UF_DONT_EXPIRE_PASSWD |\
1727 UF_ACCOUNTDISABLE );
1729 if (!(ctx = talloc_init("ads_add_machine_acct")))
1730 return ADS_ERROR(LDAP_NO_MEMORY);
1732 ret = ADS_ERROR(LDAP_NO_MEMORY);
1734 machine_escaped = escape_rdn_val_string_alloc(machine_name);
1735 if (!machine_escaped) {
1736 goto done;
1739 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
1740 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
1742 if ( !new_dn || !samAccountName ) {
1743 goto done;
1746 #ifndef ENCTYPE_ARCFOUR_HMAC
1747 acct_control |= UF_USE_DES_KEY_ONLY;
1748 #endif
1750 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
1751 goto done;
1754 if (!(mods = ads_init_mods(ctx))) {
1755 goto done;
1758 ads_mod_str(ctx, &mods, "cn", machine_name);
1759 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1760 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1761 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1763 ret = ads_gen_add(ads, new_dn, mods);
1765 done:
1766 SAFE_FREE(machine_escaped);
1767 ads_msgfree(ads, res);
1768 talloc_destroy(ctx);
1770 return ret;
1774 * move a machine account to another OU on the ADS server
1775 * @param ads - An intialized ADS_STRUCT
1776 * @param machine_name - the NetBIOS machine name of this account.
1777 * @param org_unit - The LDAP path in which to place this account
1778 * @param moved - whether we moved the machine account (optional)
1779 * @return 0 upon success, or non-zero otherwise
1782 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1783 const char *org_unit, bool *moved)
1785 ADS_STATUS rc;
1786 int ldap_status;
1787 LDAPMessage *res = NULL;
1788 char *filter = NULL;
1789 char *computer_dn = NULL;
1790 char *parent_dn;
1791 char *computer_rdn = NULL;
1792 bool need_move = False;
1794 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1795 rc = ADS_ERROR(LDAP_NO_MEMORY);
1796 goto done;
1799 /* Find pre-existing machine */
1800 rc = ads_search(ads, &res, filter, NULL);
1801 if (!ADS_ERR_OK(rc)) {
1802 goto done;
1805 computer_dn = ads_get_dn(ads, res);
1806 if (!computer_dn) {
1807 rc = ADS_ERROR(LDAP_NO_MEMORY);
1808 goto done;
1811 parent_dn = ads_parent_dn(computer_dn);
1812 if (strequal(parent_dn, org_unit)) {
1813 goto done;
1816 need_move = True;
1818 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
1819 rc = ADS_ERROR(LDAP_NO_MEMORY);
1820 goto done;
1823 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
1824 org_unit, 1, NULL, NULL);
1825 rc = ADS_ERROR(ldap_status);
1827 done:
1828 ads_msgfree(ads, res);
1829 SAFE_FREE(filter);
1830 SAFE_FREE(computer_dn);
1831 SAFE_FREE(computer_rdn);
1833 if (!ADS_ERR_OK(rc)) {
1834 need_move = False;
1837 if (moved) {
1838 *moved = need_move;
1841 return rc;
1845 dump a binary result from ldap
1847 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
1849 int i, j;
1850 for (i=0; values[i]; i++) {
1851 printf("%s: ", field);
1852 for (j=0; j<values[i]->bv_len; j++) {
1853 printf("%02X", (unsigned char)values[i]->bv_val[j]);
1855 printf("\n");
1859 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
1861 int i;
1862 for (i=0; values[i]; i++) {
1864 UUID_FLAT guid;
1865 struct GUID tmp;
1867 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1868 smb_uuid_unpack(guid, &tmp);
1869 printf("%s: %s\n", field, smb_uuid_string(talloc_tos(), tmp));
1874 dump a sid result from ldap
1876 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
1878 int i;
1879 for (i=0; values[i]; i++) {
1880 DOM_SID sid;
1881 fstring tmp;
1882 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1883 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
1888 dump ntSecurityDescriptor
1890 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
1892 TALLOC_CTX *frame = talloc_stackframe();
1893 struct security_descriptor *psd;
1894 NTSTATUS status;
1896 status = unmarshall_sec_desc(talloc_tos(), (uint8 *)values[0]->bv_val,
1897 values[0]->bv_len, &psd);
1898 if (!NT_STATUS_IS_OK(status)) {
1899 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
1900 nt_errstr(status)));
1901 TALLOC_FREE(frame);
1902 return;
1905 if (psd) {
1906 ads_disp_sd(ads, talloc_tos(), psd);
1909 TALLOC_FREE(frame);
1913 dump a string result from ldap
1915 static void dump_string(const char *field, char **values)
1917 int i;
1918 for (i=0; values[i]; i++) {
1919 printf("%s: %s\n", field, values[i]);
1924 dump a field from LDAP on stdout
1925 used for debugging
1928 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
1930 const struct {
1931 const char *name;
1932 bool string;
1933 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
1934 } handlers[] = {
1935 {"objectGUID", False, dump_guid},
1936 {"netbootGUID", False, dump_guid},
1937 {"nTSecurityDescriptor", False, dump_sd},
1938 {"dnsRecord", False, dump_binary},
1939 {"objectSid", False, dump_sid},
1940 {"tokenGroups", False, dump_sid},
1941 {"tokenGroupsNoGCAcceptable", False, dump_sid},
1942 {"tokengroupsGlobalandUniversal", False, dump_sid},
1943 {"mS-DS-CreatorSID", False, dump_sid},
1944 {"msExchMailboxGuid", False, dump_guid},
1945 {NULL, True, NULL}
1947 int i;
1949 if (!field) { /* must be end of an entry */
1950 printf("\n");
1951 return False;
1954 for (i=0; handlers[i].name; i++) {
1955 if (StrCaseCmp(handlers[i].name, field) == 0) {
1956 if (!values) /* first time, indicate string or not */
1957 return handlers[i].string;
1958 handlers[i].handler(ads, field, (struct berval **) values);
1959 break;
1962 if (!handlers[i].name) {
1963 if (!values) /* first time, indicate string conversion */
1964 return True;
1965 dump_string(field, (char **)values);
1967 return False;
1971 * Dump a result from LDAP on stdout
1972 * used for debugging
1973 * @param ads connection to ads server
1974 * @param res Results to dump
1977 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
1979 ads_process_results(ads, res, ads_dump_field, NULL);
1983 * Walk through results, calling a function for each entry found.
1984 * The function receives a field name, a berval * array of values,
1985 * and a data area passed through from the start. The function is
1986 * called once with null for field and values at the end of each
1987 * entry.
1988 * @param ads connection to ads server
1989 * @param res Results to process
1990 * @param fn Function for processing each result
1991 * @param data_area user-defined area to pass to function
1993 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
1994 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1995 void *data_area)
1997 LDAPMessage *msg;
1998 TALLOC_CTX *ctx;
2000 if (!(ctx = talloc_init("ads_process_results")))
2001 return;
2003 for (msg = ads_first_entry(ads, res); msg;
2004 msg = ads_next_entry(ads, msg)) {
2005 char *utf8_field;
2006 BerElement *b;
2008 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2009 (LDAPMessage *)msg,&b);
2010 utf8_field;
2011 utf8_field=ldap_next_attribute(ads->ldap.ld,
2012 (LDAPMessage *)msg,b)) {
2013 struct berval **ber_vals;
2014 char **str_vals, **utf8_vals;
2015 char *field;
2016 bool string;
2018 pull_utf8_talloc(ctx, &field, utf8_field);
2019 string = fn(ads, field, NULL, data_area);
2021 if (string) {
2022 utf8_vals = ldap_get_values(ads->ldap.ld,
2023 (LDAPMessage *)msg, field);
2024 str_vals = ads_pull_strvals(ctx,
2025 (const char **) utf8_vals);
2026 fn(ads, field, (void **) str_vals, data_area);
2027 ldap_value_free(utf8_vals);
2028 } else {
2029 ber_vals = ldap_get_values_len(ads->ldap.ld,
2030 (LDAPMessage *)msg, field);
2031 fn(ads, field, (void **) ber_vals, data_area);
2033 ldap_value_free_len(ber_vals);
2035 ldap_memfree(utf8_field);
2037 ber_free(b, 0);
2038 talloc_free_children(ctx);
2039 fn(ads, NULL, NULL, data_area); /* completed an entry */
2042 talloc_destroy(ctx);
2046 * count how many replies are in a LDAPMessage
2047 * @param ads connection to ads server
2048 * @param res Results to count
2049 * @return number of replies
2051 int ads_count_replies(ADS_STRUCT *ads, void *res)
2053 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2057 * pull the first entry from a ADS result
2058 * @param ads connection to ads server
2059 * @param res Results of search
2060 * @return first entry from result
2062 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2064 return ldap_first_entry(ads->ldap.ld, res);
2068 * pull the next entry from a ADS result
2069 * @param ads connection to ads server
2070 * @param res Results of search
2071 * @return next entry from result
2073 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2075 return ldap_next_entry(ads->ldap.ld, res);
2079 * pull a single string from a ADS result
2080 * @param ads connection to ads server
2081 * @param mem_ctx TALLOC_CTX to use for allocating result string
2082 * @param msg Results of search
2083 * @param field Attribute to retrieve
2084 * @return Result string in talloc context
2086 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2087 const char *field)
2089 char **values;
2090 char *ret = NULL;
2091 char *ux_string;
2092 size_t rc;
2094 values = ldap_get_values(ads->ldap.ld, msg, field);
2095 if (!values)
2096 return NULL;
2098 if (values[0]) {
2099 rc = pull_utf8_talloc(mem_ctx, &ux_string,
2100 values[0]);
2101 if (rc != (size_t)-1)
2102 ret = ux_string;
2105 ldap_value_free(values);
2106 return ret;
2110 * pull an array of strings from a ADS result
2111 * @param ads connection to ads server
2112 * @param mem_ctx TALLOC_CTX to use for allocating result string
2113 * @param msg Results of search
2114 * @param field Attribute to retrieve
2115 * @return Result strings in talloc context
2117 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2118 LDAPMessage *msg, const char *field,
2119 size_t *num_values)
2121 char **values;
2122 char **ret = NULL;
2123 int i;
2125 values = ldap_get_values(ads->ldap.ld, msg, field);
2126 if (!values)
2127 return NULL;
2129 *num_values = ldap_count_values(values);
2131 ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
2132 if (!ret) {
2133 ldap_value_free(values);
2134 return NULL;
2137 for (i=0;i<*num_values;i++) {
2138 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
2139 ldap_value_free(values);
2140 return NULL;
2143 ret[i] = NULL;
2145 ldap_value_free(values);
2146 return ret;
2150 * pull an array of strings from a ADS result
2151 * (handle large multivalue attributes with range retrieval)
2152 * @param ads connection to ads server
2153 * @param mem_ctx TALLOC_CTX to use for allocating result string
2154 * @param msg Results of search
2155 * @param field Attribute to retrieve
2156 * @param current_strings strings returned by a previous call to this function
2157 * @param next_attribute The next query should ask for this attribute
2158 * @param num_values How many values did we get this time?
2159 * @param more_values Are there more values to get?
2160 * @return Result strings in talloc context
2162 char **ads_pull_strings_range(ADS_STRUCT *ads,
2163 TALLOC_CTX *mem_ctx,
2164 LDAPMessage *msg, const char *field,
2165 char **current_strings,
2166 const char **next_attribute,
2167 size_t *num_strings,
2168 bool *more_strings)
2170 char *attr;
2171 char *expected_range_attrib, *range_attr;
2172 BerElement *ptr = NULL;
2173 char **strings;
2174 char **new_strings;
2175 size_t num_new_strings;
2176 unsigned long int range_start;
2177 unsigned long int range_end;
2179 /* we might have been given the whole lot anyway */
2180 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2181 *more_strings = False;
2182 return strings;
2185 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2187 /* look for Range result */
2188 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2189 attr;
2190 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2191 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2192 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2193 range_attr = attr;
2194 break;
2196 ldap_memfree(attr);
2198 if (!attr) {
2199 ber_free(ptr, 0);
2200 /* nothing here - this field is just empty */
2201 *more_strings = False;
2202 return NULL;
2205 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2206 &range_start, &range_end) == 2) {
2207 *more_strings = True;
2208 } else {
2209 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2210 &range_start) == 1) {
2211 *more_strings = False;
2212 } else {
2213 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2214 range_attr));
2215 ldap_memfree(range_attr);
2216 *more_strings = False;
2217 return NULL;
2221 if ((*num_strings) != range_start) {
2222 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2223 " - aborting range retreival\n",
2224 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2225 ldap_memfree(range_attr);
2226 *more_strings = False;
2227 return NULL;
2230 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2232 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2233 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2234 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2235 range_attr, (unsigned long int)range_end - range_start + 1,
2236 (unsigned long int)num_new_strings));
2237 ldap_memfree(range_attr);
2238 *more_strings = False;
2239 return NULL;
2242 strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2243 *num_strings + num_new_strings);
2245 if (strings == NULL) {
2246 ldap_memfree(range_attr);
2247 *more_strings = False;
2248 return NULL;
2251 if (new_strings && num_new_strings) {
2252 memcpy(&strings[*num_strings], new_strings,
2253 sizeof(*new_strings) * num_new_strings);
2256 (*num_strings) += num_new_strings;
2258 if (*more_strings) {
2259 *next_attribute = talloc_asprintf(mem_ctx,
2260 "%s;range=%d-*",
2261 field,
2262 (int)*num_strings);
2264 if (!*next_attribute) {
2265 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2266 ldap_memfree(range_attr);
2267 *more_strings = False;
2268 return NULL;
2272 ldap_memfree(range_attr);
2274 return strings;
2278 * pull a single uint32 from a ADS result
2279 * @param ads connection to ads server
2280 * @param msg Results of search
2281 * @param field Attribute to retrieve
2282 * @param v Pointer to int to store result
2283 * @return boolean inidicating success
2285 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2286 uint32 *v)
2288 char **values;
2290 values = ldap_get_values(ads->ldap.ld, msg, field);
2291 if (!values)
2292 return False;
2293 if (!values[0]) {
2294 ldap_value_free(values);
2295 return False;
2298 *v = atoi(values[0]);
2299 ldap_value_free(values);
2300 return True;
2304 * pull a single objectGUID from an ADS result
2305 * @param ads connection to ADS server
2306 * @param msg results of search
2307 * @param guid 37-byte area to receive text guid
2308 * @return boolean indicating success
2310 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2312 char **values;
2313 UUID_FLAT flat_guid;
2315 values = ldap_get_values(ads->ldap.ld, msg, "objectGUID");
2316 if (!values)
2317 return False;
2319 if (values[0]) {
2320 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2321 smb_uuid_unpack(flat_guid, guid);
2322 ldap_value_free(values);
2323 return True;
2325 ldap_value_free(values);
2326 return False;
2332 * pull a single DOM_SID from a ADS result
2333 * @param ads connection to ads server
2334 * @param msg Results of search
2335 * @param field Attribute to retrieve
2336 * @param sid Pointer to sid to store result
2337 * @return boolean inidicating success
2339 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2340 DOM_SID *sid)
2342 struct berval **values;
2343 bool ret = False;
2345 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2347 if (!values)
2348 return False;
2350 if (values[0])
2351 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2353 ldap_value_free_len(values);
2354 return ret;
2358 * pull an array of DOM_SIDs from a ADS result
2359 * @param ads connection to ads server
2360 * @param mem_ctx TALLOC_CTX for allocating sid array
2361 * @param msg Results of search
2362 * @param field Attribute to retrieve
2363 * @param sids pointer to sid array to allocate
2364 * @return the count of SIDs pulled
2366 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2367 LDAPMessage *msg, const char *field, DOM_SID **sids)
2369 struct berval **values;
2370 bool ret;
2371 int count, i;
2373 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2375 if (!values)
2376 return 0;
2378 for (i=0; values[i]; i++)
2379 /* nop */ ;
2381 if (i) {
2382 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2383 if (!(*sids)) {
2384 ldap_value_free_len(values);
2385 return 0;
2387 } else {
2388 (*sids) = NULL;
2391 count = 0;
2392 for (i=0; values[i]; i++) {
2393 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2394 if (ret) {
2395 DEBUG(10, ("pulling SID: %s\n",
2396 sid_string_dbg(&(*sids)[count])));
2397 count++;
2401 ldap_value_free_len(values);
2402 return count;
2406 * pull a SEC_DESC from a ADS result
2407 * @param ads connection to ads server
2408 * @param mem_ctx TALLOC_CTX for allocating sid array
2409 * @param msg Results of search
2410 * @param field Attribute to retrieve
2411 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2412 * @return boolean inidicating success
2414 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2415 LDAPMessage *msg, const char *field, SEC_DESC **sd)
2417 struct berval **values;
2418 bool ret = true;
2420 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2422 if (!values) return false;
2424 if (values[0]) {
2425 NTSTATUS status;
2426 status = unmarshall_sec_desc(mem_ctx,
2427 (uint8 *)values[0]->bv_val,
2428 values[0]->bv_len, sd);
2429 if (!NT_STATUS_IS_OK(status)) {
2430 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2431 nt_errstr(status)));
2432 ret = false;
2436 ldap_value_free_len(values);
2437 return ret;
2441 * in order to support usernames longer than 21 characters we need to
2442 * use both the sAMAccountName and the userPrincipalName attributes
2443 * It seems that not all users have the userPrincipalName attribute set
2445 * @param ads connection to ads server
2446 * @param mem_ctx TALLOC_CTX for allocating sid array
2447 * @param msg Results of search
2448 * @return the username
2450 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2451 LDAPMessage *msg)
2453 #if 0 /* JERRY */
2454 char *ret, *p;
2456 /* lookup_name() only works on the sAMAccountName to
2457 returning the username portion of userPrincipalName
2458 breaks winbindd_getpwnam() */
2460 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2461 if (ret && (p = strchr_m(ret, '@'))) {
2462 *p = 0;
2463 return ret;
2465 #endif
2466 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2471 * find the update serial number - this is the core of the ldap cache
2472 * @param ads connection to ads server
2473 * @param ads connection to ADS server
2474 * @param usn Pointer to retrieved update serial number
2475 * @return status of search
2477 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2479 const char *attrs[] = {"highestCommittedUSN", NULL};
2480 ADS_STATUS status;
2481 LDAPMessage *res;
2483 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2484 if (!ADS_ERR_OK(status))
2485 return status;
2487 if (ads_count_replies(ads, res) != 1) {
2488 ads_msgfree(ads, res);
2489 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2492 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2493 ads_msgfree(ads, res);
2494 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2497 ads_msgfree(ads, res);
2498 return ADS_SUCCESS;
2501 /* parse a ADS timestring - typical string is
2502 '20020917091222.0Z0' which means 09:12.22 17th September
2503 2002, timezone 0 */
2504 static time_t ads_parse_time(const char *str)
2506 struct tm tm;
2508 ZERO_STRUCT(tm);
2510 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2511 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2512 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2513 return 0;
2515 tm.tm_year -= 1900;
2516 tm.tm_mon -= 1;
2518 return timegm(&tm);
2521 /********************************************************************
2522 ********************************************************************/
2524 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2526 const char *attrs[] = {"currentTime", NULL};
2527 ADS_STATUS status;
2528 LDAPMessage *res;
2529 char *timestr;
2530 TALLOC_CTX *ctx;
2531 ADS_STRUCT *ads_s = ads;
2533 if (!(ctx = talloc_init("ads_current_time"))) {
2534 return ADS_ERROR(LDAP_NO_MEMORY);
2537 /* establish a new ldap tcp session if necessary */
2539 if ( !ads->ldap.ld ) {
2540 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2541 ads->server.ldap_server )) == NULL )
2543 goto done;
2545 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2546 status = ads_connect( ads_s );
2547 if ( !ADS_ERR_OK(status))
2548 goto done;
2551 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2552 if (!ADS_ERR_OK(status)) {
2553 goto done;
2556 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2557 if (!timestr) {
2558 ads_msgfree(ads_s, res);
2559 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2560 goto done;
2563 /* but save the time and offset in the original ADS_STRUCT */
2565 ads->config.current_time = ads_parse_time(timestr);
2567 if (ads->config.current_time != 0) {
2568 ads->auth.time_offset = ads->config.current_time - time(NULL);
2569 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2572 ads_msgfree(ads, res);
2574 status = ADS_SUCCESS;
2576 done:
2577 /* free any temporary ads connections */
2578 if ( ads_s != ads ) {
2579 ads_destroy( &ads_s );
2581 talloc_destroy(ctx);
2583 return status;
2586 /********************************************************************
2587 ********************************************************************/
2589 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2591 const char *attrs[] = {"domainFunctionality", NULL};
2592 ADS_STATUS status;
2593 LDAPMessage *res;
2594 ADS_STRUCT *ads_s = ads;
2596 *val = DS_DOMAIN_FUNCTION_2000;
2598 /* establish a new ldap tcp session if necessary */
2600 if ( !ads->ldap.ld ) {
2601 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2602 ads->server.ldap_server )) == NULL )
2604 goto done;
2606 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2607 status = ads_connect( ads_s );
2608 if ( !ADS_ERR_OK(status))
2609 goto done;
2612 /* If the attribute does not exist assume it is a Windows 2000
2613 functional domain */
2615 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2616 if (!ADS_ERR_OK(status)) {
2617 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2618 status = ADS_SUCCESS;
2620 goto done;
2623 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2624 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2626 DEBUG(3,("ads_domain_func_level: %d\n", *val));
2629 ads_msgfree(ads, res);
2631 done:
2632 /* free any temporary ads connections */
2633 if ( ads_s != ads ) {
2634 ads_destroy( &ads_s );
2637 return status;
2641 * find the domain sid for our domain
2642 * @param ads connection to ads server
2643 * @param sid Pointer to domain sid
2644 * @return status of search
2646 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2648 const char *attrs[] = {"objectSid", NULL};
2649 LDAPMessage *res;
2650 ADS_STATUS rc;
2652 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2653 attrs, &res);
2654 if (!ADS_ERR_OK(rc)) return rc;
2655 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2656 ads_msgfree(ads, res);
2657 return ADS_ERROR_SYSTEM(ENOENT);
2659 ads_msgfree(ads, res);
2661 return ADS_SUCCESS;
2665 * find our site name
2666 * @param ads connection to ads server
2667 * @param mem_ctx Pointer to talloc context
2668 * @param site_name Pointer to the sitename
2669 * @return status of search
2671 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2673 ADS_STATUS status;
2674 LDAPMessage *res;
2675 const char *dn, *service_name;
2676 const char *attrs[] = { "dsServiceName", NULL };
2678 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2679 if (!ADS_ERR_OK(status)) {
2680 return status;
2683 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2684 if (service_name == NULL) {
2685 ads_msgfree(ads, res);
2686 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2689 ads_msgfree(ads, res);
2691 /* go up three levels */
2692 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2693 if (dn == NULL) {
2694 return ADS_ERROR(LDAP_NO_MEMORY);
2697 *site_name = talloc_strdup(mem_ctx, dn);
2698 if (*site_name == NULL) {
2699 return ADS_ERROR(LDAP_NO_MEMORY);
2702 return status;
2704 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2709 * find the site dn where a machine resides
2710 * @param ads connection to ads server
2711 * @param mem_ctx Pointer to talloc context
2712 * @param computer_name name of the machine
2713 * @param site_name Pointer to the sitename
2714 * @return status of search
2716 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
2718 ADS_STATUS status;
2719 LDAPMessage *res;
2720 const char *parent, *filter;
2721 char *config_context = NULL;
2722 char *dn;
2724 /* shortcut a query */
2725 if (strequal(computer_name, ads->config.ldap_server_name)) {
2726 return ads_site_dn(ads, mem_ctx, site_dn);
2729 status = ads_config_path(ads, mem_ctx, &config_context);
2730 if (!ADS_ERR_OK(status)) {
2731 return status;
2734 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
2735 if (filter == NULL) {
2736 return ADS_ERROR(LDAP_NO_MEMORY);
2739 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
2740 filter, NULL, &res);
2741 if (!ADS_ERR_OK(status)) {
2742 return status;
2745 if (ads_count_replies(ads, res) != 1) {
2746 ads_msgfree(ads, res);
2747 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2750 dn = ads_get_dn(ads, res);
2751 if (dn == NULL) {
2752 ads_msgfree(ads, res);
2753 return ADS_ERROR(LDAP_NO_MEMORY);
2756 /* go up three levels */
2757 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
2758 if (parent == NULL) {
2759 ads_msgfree(ads, res);
2760 ads_memfree(ads, dn);
2761 return ADS_ERROR(LDAP_NO_MEMORY);
2764 *site_dn = talloc_strdup(mem_ctx, parent);
2765 if (*site_dn == NULL) {
2766 ads_msgfree(ads, res);
2767 ads_memfree(ads, dn);
2768 return ADS_ERROR(LDAP_NO_MEMORY);
2771 ads_memfree(ads, dn);
2772 ads_msgfree(ads, res);
2774 return status;
2778 * get the upn suffixes for a domain
2779 * @param ads connection to ads server
2780 * @param mem_ctx Pointer to talloc context
2781 * @param suffixes Pointer to an array of suffixes
2782 * @param num_suffixes Pointer to the number of suffixes
2783 * @return status of search
2785 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
2787 ADS_STATUS status;
2788 LDAPMessage *res;
2789 const char *base;
2790 char *config_context = NULL;
2791 const char *attrs[] = { "uPNSuffixes", NULL };
2793 status = ads_config_path(ads, mem_ctx, &config_context);
2794 if (!ADS_ERR_OK(status)) {
2795 return status;
2798 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
2799 if (base == NULL) {
2800 return ADS_ERROR(LDAP_NO_MEMORY);
2803 status = ads_search_dn(ads, &res, base, attrs);
2804 if (!ADS_ERR_OK(status)) {
2805 return status;
2808 if (ads_count_replies(ads, res) != 1) {
2809 ads_msgfree(ads, res);
2810 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2813 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
2814 if ((*suffixes) == NULL) {
2815 ads_msgfree(ads, res);
2816 return ADS_ERROR(LDAP_NO_MEMORY);
2819 ads_msgfree(ads, res);
2821 return status;
2825 * get the joinable ous for a domain
2826 * @param ads connection to ads server
2827 * @param mem_ctx Pointer to talloc context
2828 * @param ous Pointer to an array of ous
2829 * @param num_ous Pointer to the number of ous
2830 * @return status of search
2832 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
2833 TALLOC_CTX *mem_ctx,
2834 char ***ous,
2835 size_t *num_ous)
2837 ADS_STATUS status;
2838 LDAPMessage *res = NULL;
2839 LDAPMessage *msg = NULL;
2840 const char *attrs[] = { "dn", NULL };
2841 int count = 0;
2843 status = ads_search(ads, &res,
2844 "(|(objectClass=domain)(objectclass=organizationalUnit))",
2845 attrs);
2846 if (!ADS_ERR_OK(status)) {
2847 return status;
2850 count = ads_count_replies(ads, res);
2851 if (count < 1) {
2852 ads_msgfree(ads, res);
2853 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2856 for (msg = ads_first_entry(ads, res); msg;
2857 msg = ads_next_entry(ads, msg)) {
2859 char *dn = NULL;
2861 dn = ads_get_dn(ads, msg);
2862 if (!dn) {
2863 ads_msgfree(ads, res);
2864 return ADS_ERROR(LDAP_NO_MEMORY);
2867 if (!add_string_to_array(mem_ctx, dn,
2868 (const char ***)ous,
2869 (int *)num_ous)) {
2870 ads_memfree(ads, dn);
2871 ads_msgfree(ads, res);
2872 return ADS_ERROR(LDAP_NO_MEMORY);
2875 ads_memfree(ads, dn);
2878 ads_msgfree(ads, res);
2880 return status;
2885 * pull a DOM_SID from an extended dn string
2886 * @param mem_ctx TALLOC_CTX
2887 * @param extended_dn string
2888 * @param flags string type of extended_dn
2889 * @param sid pointer to a DOM_SID
2890 * @return boolean inidicating success
2892 bool ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
2893 const char *extended_dn,
2894 enum ads_extended_dn_flags flags,
2895 DOM_SID *sid)
2897 char *p, *q, *dn;
2899 if (!extended_dn) {
2900 return False;
2903 /* otherwise extended_dn gets stripped off */
2904 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
2905 return False;
2908 * ADS_EXTENDED_DN_HEX_STRING:
2909 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
2911 * ADS_EXTENDED_DN_STRING (only with w2k3):
2912 <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
2915 p = strchr(dn, ';');
2916 if (!p) {
2917 return False;
2920 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
2921 return False;
2924 p += strlen(";<SID=");
2926 q = strchr(p, '>');
2927 if (!q) {
2928 return False;
2931 *q = '\0';
2933 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
2935 switch (flags) {
2937 case ADS_EXTENDED_DN_STRING:
2938 if (!string_to_sid(sid, p)) {
2939 return False;
2941 break;
2942 case ADS_EXTENDED_DN_HEX_STRING: {
2943 fstring buf;
2944 size_t buf_len;
2946 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
2947 if (buf_len == 0) {
2948 return False;
2951 if (!sid_parse(buf, buf_len, sid)) {
2952 DEBUG(10,("failed to parse sid\n"));
2953 return False;
2955 break;
2957 default:
2958 DEBUG(10,("unknown extended dn format\n"));
2959 return False;
2962 return True;
2966 * pull an array of DOM_SIDs from a ADS result
2967 * @param ads connection to ads server
2968 * @param mem_ctx TALLOC_CTX for allocating sid array
2969 * @param msg Results of search
2970 * @param field Attribute to retrieve
2971 * @param flags string type of extended_dn
2972 * @param sids pointer to sid array to allocate
2973 * @return the count of SIDs pulled
2975 int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads,
2976 TALLOC_CTX *mem_ctx,
2977 LDAPMessage *msg,
2978 const char *field,
2979 enum ads_extended_dn_flags flags,
2980 DOM_SID **sids)
2982 int i;
2983 size_t dn_count;
2984 char **dn_strings;
2986 if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field,
2987 &dn_count)) == NULL) {
2988 return 0;
2991 (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, dn_count + 1);
2992 if (!(*sids)) {
2993 TALLOC_FREE(dn_strings);
2994 return 0;
2997 for (i=0; i<dn_count; i++) {
2999 if (!ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i],
3000 flags, &(*sids)[i])) {
3001 TALLOC_FREE(*sids);
3002 TALLOC_FREE(dn_strings);
3003 return 0;
3007 TALLOC_FREE(dn_strings);
3009 return dn_count;
3012 /********************************************************************
3013 ********************************************************************/
3015 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3017 LDAPMessage *res = NULL;
3018 ADS_STATUS status;
3019 int count = 0;
3020 char *name = NULL;
3022 status = ads_find_machine_acct(ads, &res, global_myname());
3023 if (!ADS_ERR_OK(status)) {
3024 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3025 global_myname()));
3026 goto out;
3029 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3030 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3031 goto out;
3034 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3035 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3038 out:
3039 ads_msgfree(ads, res);
3041 return name;
3044 /********************************************************************
3045 ********************************************************************/
3047 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3049 LDAPMessage *res = NULL;
3050 ADS_STATUS status;
3051 int count = 0;
3052 char *name = NULL;
3054 status = ads_find_machine_acct(ads, &res, machine_name);
3055 if (!ADS_ERR_OK(status)) {
3056 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3057 global_myname()));
3058 goto out;
3061 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3062 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3063 goto out;
3066 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3067 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3070 out:
3071 ads_msgfree(ads, res);
3073 return name;
3076 /********************************************************************
3077 ********************************************************************/
3079 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3081 LDAPMessage *res = NULL;
3082 ADS_STATUS status;
3083 int count = 0;
3084 char *name = NULL;
3086 status = ads_find_machine_acct(ads, &res, global_myname());
3087 if (!ADS_ERR_OK(status)) {
3088 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3089 global_myname()));
3090 goto out;
3093 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3094 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3095 goto out;
3098 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3099 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3102 out:
3103 ads_msgfree(ads, res);
3105 return name;
3108 #if 0
3110 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3113 * Join a machine to a realm
3114 * Creates the machine account and sets the machine password
3115 * @param ads connection to ads server
3116 * @param machine name of host to add
3117 * @param org_unit Organizational unit to place machine in
3118 * @return status of join
3120 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3121 uint32 account_type, const char *org_unit)
3123 ADS_STATUS status;
3124 LDAPMessage *res = NULL;
3125 char *machine;
3127 /* machine name must be lowercase */
3128 machine = SMB_STRDUP(machine_name);
3129 strlower_m(machine);
3132 status = ads_find_machine_acct(ads, (void **)&res, machine);
3133 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3134 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3135 status = ads_leave_realm(ads, machine);
3136 if (!ADS_ERR_OK(status)) {
3137 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3138 machine, ads->config.realm));
3139 return status;
3143 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3144 if (!ADS_ERR_OK(status)) {
3145 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3146 SAFE_FREE(machine);
3147 return status;
3150 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3151 if (!ADS_ERR_OK(status)) {
3152 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3153 SAFE_FREE(machine);
3154 return status;
3157 SAFE_FREE(machine);
3158 ads_msgfree(ads, res);
3160 return status;
3162 #endif
3165 * Delete a machine from the realm
3166 * @param ads connection to ads server
3167 * @param hostname Machine to remove
3168 * @return status of delete
3170 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3172 ADS_STATUS status;
3173 void *msg;
3174 LDAPMessage *res;
3175 char *hostnameDN, *host;
3176 int rc;
3177 LDAPControl ldap_control;
3178 LDAPControl * pldap_control[2] = {NULL, NULL};
3180 pldap_control[0] = &ldap_control;
3181 memset(&ldap_control, 0, sizeof(LDAPControl));
3182 ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
3184 /* hostname must be lowercase */
3185 host = SMB_STRDUP(hostname);
3186 strlower_m(host);
3188 status = ads_find_machine_acct(ads, &res, host);
3189 if (!ADS_ERR_OK(status)) {
3190 DEBUG(0, ("Host account for %s does not exist.\n", host));
3191 SAFE_FREE(host);
3192 return status;
3195 msg = ads_first_entry(ads, res);
3196 if (!msg) {
3197 SAFE_FREE(host);
3198 return ADS_ERROR_SYSTEM(ENOENT);
3201 hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
3203 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3204 if (rc) {
3205 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3206 }else {
3207 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3210 if (rc != LDAP_SUCCESS) {
3211 const char *attrs[] = { "cn", NULL };
3212 LDAPMessage *msg_sub;
3214 /* we only search with scope ONE, we do not expect any further
3215 * objects to be created deeper */
3217 status = ads_do_search_retry(ads, hostnameDN,
3218 LDAP_SCOPE_ONELEVEL,
3219 "(objectclass=*)", attrs, &res);
3221 if (!ADS_ERR_OK(status)) {
3222 SAFE_FREE(host);
3223 ads_memfree(ads, hostnameDN);
3224 return status;
3227 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3228 msg_sub = ads_next_entry(ads, msg_sub)) {
3230 char *dn = NULL;
3232 if ((dn = ads_get_dn(ads, msg_sub)) == NULL) {
3233 SAFE_FREE(host);
3234 ads_memfree(ads, hostnameDN);
3235 return ADS_ERROR(LDAP_NO_MEMORY);
3238 status = ads_del_dn(ads, dn);
3239 if (!ADS_ERR_OK(status)) {
3240 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3241 SAFE_FREE(host);
3242 ads_memfree(ads, dn);
3243 ads_memfree(ads, hostnameDN);
3244 return status;
3247 ads_memfree(ads, dn);
3250 /* there should be no subordinate objects anymore */
3251 status = ads_do_search_retry(ads, hostnameDN,
3252 LDAP_SCOPE_ONELEVEL,
3253 "(objectclass=*)", attrs, &res);
3255 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3256 SAFE_FREE(host);
3257 ads_memfree(ads, hostnameDN);
3258 return status;
3261 /* delete hostnameDN now */
3262 status = ads_del_dn(ads, hostnameDN);
3263 if (!ADS_ERR_OK(status)) {
3264 SAFE_FREE(host);
3265 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3266 ads_memfree(ads, hostnameDN);
3267 return status;
3271 ads_memfree(ads, hostnameDN);
3273 status = ads_find_machine_acct(ads, &res, host);
3274 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3275 DEBUG(3, ("Failed to remove host account.\n"));
3276 SAFE_FREE(host);
3277 return status;
3280 SAFE_FREE(host);
3281 return status;
3285 * pull all token-sids from an LDAP dn
3286 * @param ads connection to ads server
3287 * @param mem_ctx TALLOC_CTX for allocating sid array
3288 * @param dn of LDAP object
3289 * @param user_sid pointer to DOM_SID (objectSid)
3290 * @param primary_group_sid pointer to DOM_SID (self composed)
3291 * @param sids pointer to sid array to allocate
3292 * @param num_sids counter of SIDs pulled
3293 * @return status of token query
3295 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3296 TALLOC_CTX *mem_ctx,
3297 const char *dn,
3298 DOM_SID *user_sid,
3299 DOM_SID *primary_group_sid,
3300 DOM_SID **sids,
3301 size_t *num_sids)
3303 ADS_STATUS status;
3304 LDAPMessage *res = NULL;
3305 int count = 0;
3306 size_t tmp_num_sids;
3307 DOM_SID *tmp_sids;
3308 DOM_SID tmp_user_sid;
3309 DOM_SID tmp_primary_group_sid;
3310 uint32 pgid;
3311 const char *attrs[] = {
3312 "objectSid",
3313 "tokenGroups",
3314 "primaryGroupID",
3315 NULL
3318 status = ads_search_retry_dn(ads, &res, dn, attrs);
3319 if (!ADS_ERR_OK(status)) {
3320 return status;
3323 count = ads_count_replies(ads, res);
3324 if (count != 1) {
3325 ads_msgfree(ads, res);
3326 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3329 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3330 ads_msgfree(ads, res);
3331 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3334 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3335 ads_msgfree(ads, res);
3336 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3340 /* hack to compose the primary group sid without knowing the
3341 * domsid */
3343 DOM_SID domsid;
3344 uint32 dummy_rid;
3346 sid_copy(&domsid, &tmp_user_sid);
3348 if (!sid_split_rid(&domsid, &dummy_rid)) {
3349 ads_msgfree(ads, res);
3350 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3353 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3354 ads_msgfree(ads, res);
3355 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3359 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3361 if (tmp_num_sids == 0 || !tmp_sids) {
3362 ads_msgfree(ads, res);
3363 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3366 if (num_sids) {
3367 *num_sids = tmp_num_sids;
3370 if (sids) {
3371 *sids = tmp_sids;
3374 if (user_sid) {
3375 *user_sid = tmp_user_sid;
3378 if (primary_group_sid) {
3379 *primary_group_sid = tmp_primary_group_sid;
3382 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3384 ads_msgfree(ads, res);
3385 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3389 * Find a sAMAccoutName in LDAP
3390 * @param ads connection to ads server
3391 * @param mem_ctx TALLOC_CTX for allocating sid array
3392 * @param samaccountname to search
3393 * @param uac_ret uint32 pointer userAccountControl attribute value
3394 * @param dn_ret pointer to dn
3395 * @return status of token query
3397 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3398 TALLOC_CTX *mem_ctx,
3399 const char *samaccountname,
3400 uint32 *uac_ret,
3401 const char **dn_ret)
3403 ADS_STATUS status;
3404 const char *attrs[] = { "userAccountControl", NULL };
3405 const char *filter;
3406 LDAPMessage *res = NULL;
3407 char *dn = NULL;
3408 uint32 uac = 0;
3410 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3411 samaccountname);
3412 if (filter == NULL) {
3413 goto out;
3416 status = ads_do_search_all(ads, ads->config.bind_path,
3417 LDAP_SCOPE_SUBTREE,
3418 filter, attrs, &res);
3420 if (!ADS_ERR_OK(status)) {
3421 goto out;
3424 if (ads_count_replies(ads, res) != 1) {
3425 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3426 goto out;
3429 dn = ads_get_dn(ads, res);
3430 if (dn == NULL) {
3431 status = ADS_ERROR(LDAP_NO_MEMORY);
3432 goto out;
3435 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3436 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3437 goto out;
3440 if (uac_ret) {
3441 *uac_ret = uac;
3444 if (dn_ret) {
3445 *dn_ret = talloc_strdup(mem_ctx, dn);
3446 if (!*dn_ret) {
3447 status = ADS_ERROR(LDAP_NO_MEMORY);
3448 goto out;
3451 out:
3452 ads_memfree(ads, dn);
3453 ads_msgfree(ads, res);
3455 return status;
3459 * find our configuration path
3460 * @param ads connection to ads server
3461 * @param mem_ctx Pointer to talloc context
3462 * @param config_path Pointer to the config path
3463 * @return status of search
3465 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3466 TALLOC_CTX *mem_ctx,
3467 char **config_path)
3469 ADS_STATUS status;
3470 LDAPMessage *res = NULL;
3471 const char *config_context = NULL;
3472 const char *attrs[] = { "configurationNamingContext", NULL };
3474 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3475 "(objectclass=*)", attrs, &res);
3476 if (!ADS_ERR_OK(status)) {
3477 return status;
3480 config_context = ads_pull_string(ads, mem_ctx, res,
3481 "configurationNamingContext");
3482 ads_msgfree(ads, res);
3483 if (!config_context) {
3484 return ADS_ERROR(LDAP_NO_MEMORY);
3487 if (config_path) {
3488 *config_path = talloc_strdup(mem_ctx, config_context);
3489 if (!*config_path) {
3490 return ADS_ERROR(LDAP_NO_MEMORY);
3494 return ADS_ERROR(LDAP_SUCCESS);
3498 * find the displayName of an extended right
3499 * @param ads connection to ads server
3500 * @param config_path The config path
3501 * @param mem_ctx Pointer to talloc context
3502 * @param GUID struct of the rightsGUID
3503 * @return status of search
3505 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3506 const char *config_path,
3507 TALLOC_CTX *mem_ctx,
3508 const struct GUID *rights_guid)
3510 ADS_STATUS rc;
3511 LDAPMessage *res = NULL;
3512 char *expr = NULL;
3513 const char *attrs[] = { "displayName", NULL };
3514 const char *result = NULL;
3515 const char *path;
3517 if (!ads || !mem_ctx || !rights_guid) {
3518 goto done;
3521 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3522 smb_uuid_string(mem_ctx, *rights_guid));
3523 if (!expr) {
3524 goto done;
3527 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3528 if (!path) {
3529 goto done;
3532 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3533 expr, attrs, &res);
3534 if (!ADS_ERR_OK(rc)) {
3535 goto done;
3538 if (ads_count_replies(ads, res) != 1) {
3539 goto done;
3542 result = ads_pull_string(ads, mem_ctx, res, "displayName");
3544 done:
3545 ads_msgfree(ads, res);
3546 return result;
3550 #endif