libads/cldap: store client sitename also keyed by dns domain name.
[Samba.git] / source / libads / ldap.c
blob063645febf75cc13cecf0c0a6b5ed764c41f8118
1 /*
2 Unix SMB/CIFS implementation.
3 ads (active directory) utility library
4 Copyright (C) Andrew Tridgell 2001
5 Copyright (C) Remus Koos 2001
6 Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002
7 Copyright (C) Guenther Deschner 2005
8 Copyright (C) Gerald Carter 2006
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "includes.h"
25 #include "lib/ldb/include/includes.h"
27 #ifdef HAVE_LDAP
29 /**
30 * @file ldap.c
31 * @brief basic ldap client-side routines for ads server communications
33 * The routines contained here should do the necessary ldap calls for
34 * ads setups.
36 * Important note: attribute names passed into ads_ routines must
37 * already be in UTF-8 format. We do not convert them because in almost
38 * all cases, they are just ascii (which is represented with the same
39 * codepoints in UTF-8). This may have to change at some point
40 **/
43 #define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"
45 static SIG_ATOMIC_T gotalarm;
47 /***************************************************************
48 Signal function to tell us we timed out.
49 ****************************************************************/
51 static void gotalarm_sig(void)
53 gotalarm = 1;
56 LDAP *ldap_open_with_timeout(const char *server, int port, unsigned int to)
58 LDAP *ldp = NULL;
61 DEBUG(10, ("Opening connection to LDAP server '%s:%d', timeout "
62 "%u seconds\n", server, port, to));
64 /* Setup timeout */
65 gotalarm = 0;
66 CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
67 alarm(to);
68 /* End setup timeout. */
70 ldp = ldap_open(server, port);
72 if (ldp == NULL) {
73 DEBUG(2,("Could not open connection to LDAP server %s:%d: %s\n",
74 server, port, strerror(errno)));
75 } else {
76 DEBUG(10, ("Connected to LDAP server '%s:%d'\n", server, port));
79 /* Teardown timeout. */
80 CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
81 alarm(0);
83 return ldp;
86 static int ldap_search_with_timeout(LDAP *ld,
87 LDAP_CONST char *base,
88 int scope,
89 LDAP_CONST char *filter,
90 char **attrs,
91 int attrsonly,
92 LDAPControl **sctrls,
93 LDAPControl **cctrls,
94 int sizelimit,
95 LDAPMessage **res )
97 struct timeval timeout;
98 int result;
100 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
101 timeout.tv_sec = lp_ldap_timeout();
102 timeout.tv_usec = 0;
104 /* Setup alarm timeout.... Do we need both of these ? JRA. */
105 gotalarm = 0;
106 CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
107 alarm(lp_ldap_timeout());
108 /* End setup timeout. */
110 result = ldap_search_ext_s(ld, base, scope, filter, attrs,
111 attrsonly, sctrls, cctrls, &timeout,
112 sizelimit, res);
114 /* Teardown timeout. */
115 CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
116 alarm(0);
118 if (gotalarm != 0)
119 return LDAP_TIMELIMIT_EXCEEDED;
121 return result;
124 /**********************************************
125 Do client and server sitename match ?
126 **********************************************/
128 bool ads_sitename_match(ADS_STRUCT *ads)
130 if (ads->config.server_site_name == NULL &&
131 ads->config.client_site_name == NULL ) {
132 DEBUG(10,("ads_sitename_match: both null\n"));
133 return True;
135 if (ads->config.server_site_name &&
136 ads->config.client_site_name &&
137 strequal(ads->config.server_site_name,
138 ads->config.client_site_name)) {
139 DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
140 return True;
142 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
143 ads->config.server_site_name ? ads->config.server_site_name : "NULL",
144 ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
145 return False;
148 /**********************************************
149 Is this the closest DC ?
150 **********************************************/
152 bool ads_closest_dc(ADS_STRUCT *ads)
154 if (ads->config.flags & NBT_SERVER_CLOSEST) {
155 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag set\n"));
156 return True;
159 /* not sure if this can ever happen */
160 if (ads_sitename_match(ads)) {
161 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag not set but sites match\n"));
162 return True;
165 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
166 ads->config.ldap_server_name));
168 return False;
173 try a connection to a given ldap server, returning True and setting the servers IP
174 in the ads struct if successful
176 bool ads_try_connect(ADS_STRUCT *ads, const char *server )
178 char *srv;
179 struct nbt_cldap_netlogon_5 cldap_reply;
180 TALLOC_CTX *mem_ctx = NULL;
181 bool ret = false;
183 if (!server || !*server) {
184 return False;
187 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
188 server, ads->server.realm));
190 mem_ctx = talloc_init("ads_try_connect");
191 if (!mem_ctx) {
192 DEBUG(0,("out of memory\n"));
193 return false;
196 /* this copes with inet_ntoa brokenness */
198 srv = SMB_STRDUP(server);
200 ZERO_STRUCT( cldap_reply );
202 if ( !ads_cldap_netlogon_5(mem_ctx, srv, ads->server.realm, &cldap_reply ) ) {
203 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", srv));
204 ret = false;
205 goto out;
208 /* Check the CLDAP reply flags */
210 if ( !(cldap_reply.server_type & NBT_SERVER_LDAP) ) {
211 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
212 srv));
213 ret = false;
214 goto out;
217 /* Fill in the ads->config values */
219 SAFE_FREE(ads->config.realm);
220 SAFE_FREE(ads->config.bind_path);
221 SAFE_FREE(ads->config.ldap_server_name);
222 SAFE_FREE(ads->config.server_site_name);
223 SAFE_FREE(ads->config.client_site_name);
224 SAFE_FREE(ads->server.workgroup);
226 ads->config.flags = cldap_reply.server_type;
227 ads->config.ldap_server_name = SMB_STRDUP(cldap_reply.pdc_dns_name);
228 ads->config.realm = SMB_STRDUP(cldap_reply.dns_domain);
229 strupper_m(ads->config.realm);
230 ads->config.bind_path = ads_build_dn(ads->config.realm);
231 if (*cldap_reply.server_site) {
232 ads->config.server_site_name =
233 SMB_STRDUP(cldap_reply.server_site);
235 if (*cldap_reply.client_site) {
236 ads->config.client_site_name =
237 SMB_STRDUP(cldap_reply.client_site);
239 ads->server.workgroup = SMB_STRDUP(cldap_reply.domain);
241 ads->ldap.port = LDAP_PORT;
242 if (!interpret_string_addr(&ads->ldap.ss, srv, 0)) {
243 DEBUG(1,("ads_try_connect: unable to convert %s "
244 "to an address\n",
245 srv));
246 ret = false;
247 goto out;
250 /* Store our site name. */
251 sitename_store( cldap_reply.domain, cldap_reply.client_site);
252 sitename_store( cldap_reply.dns_domain, cldap_reply.client_site);
254 ret = true;
255 out:
256 SAFE_FREE(srv);
257 TALLOC_FREE(mem_ctx);
259 return ret;
262 /**********************************************************************
263 Try to find an AD dc using our internal name resolution routines
264 Try the realm first and then then workgroup name if netbios is not
265 disabled
266 **********************************************************************/
268 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
270 const char *c_realm;
271 int count, i=0;
272 struct ip_service *ip_list;
273 const char *realm;
274 bool got_realm = False;
275 bool use_own_domain = False;
276 char *sitename;
277 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
279 /* if the realm and workgroup are both empty, assume they are ours */
281 /* realm */
282 c_realm = ads->server.realm;
284 if ( !c_realm || !*c_realm ) {
285 /* special case where no realm and no workgroup means our own */
286 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
287 use_own_domain = True;
288 c_realm = lp_realm();
292 if (c_realm && *c_realm)
293 got_realm = True;
295 /* we need to try once with the realm name and fallback to the
296 netbios domain name if we fail (if netbios has not been disabled */
298 if ( !got_realm && !lp_disable_netbios() ) {
299 c_realm = ads->server.workgroup;
300 if (!c_realm || !*c_realm) {
301 if ( use_own_domain )
302 c_realm = lp_workgroup();
305 if ( !c_realm || !*c_realm ) {
306 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
307 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
311 realm = c_realm;
313 sitename = sitename_fetch(realm);
315 again:
317 DEBUG(6,("ads_find_dc: looking for %s '%s'\n",
318 (got_realm ? "realm" : "domain"), realm));
320 status = get_sorted_dc_list(realm, sitename, &ip_list, &count, got_realm);
321 if (!NT_STATUS_IS_OK(status)) {
322 /* fall back to netbios if we can */
323 if ( got_realm && !lp_disable_netbios() ) {
324 got_realm = False;
325 goto again;
328 SAFE_FREE(sitename);
329 return status;
332 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
333 for ( i=0; i<count; i++ ) {
334 char server[INET6_ADDRSTRLEN];
336 print_sockaddr(server, sizeof(server), &ip_list[i].ss);
338 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
339 continue;
341 if (!got_realm) {
342 /* realm in this case is a workgroup name. We need
343 to ignore any IP addresses in the negative connection
344 cache that match ip addresses returned in the ad realm
345 case. It sucks that I have to reproduce the logic above... */
346 c_realm = ads->server.realm;
347 if ( !c_realm || !*c_realm ) {
348 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
349 c_realm = lp_realm();
352 if (c_realm && *c_realm &&
353 !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm, server))) {
354 /* Ensure we add the workgroup name for this
355 IP address as negative too. */
356 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
357 continue;
361 if ( ads_try_connect(ads, server) ) {
362 SAFE_FREE(ip_list);
363 SAFE_FREE(sitename);
364 return NT_STATUS_OK;
367 /* keep track of failures */
368 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
371 SAFE_FREE(ip_list);
373 /* In case we failed to contact one of our closest DC on our site we
374 * need to try to find another DC, retry with a site-less SRV DNS query
375 * - Guenther */
377 if (sitename) {
378 DEBUG(1,("ads_find_dc: failed to find a valid DC on our site (%s), "
379 "trying to find another DC\n", sitename));
380 SAFE_FREE(sitename);
381 namecache_delete(realm, 0x1C);
382 goto again;
385 return NT_STATUS_NO_LOGON_SERVERS;
390 * Connect to the LDAP server
391 * @param ads Pointer to an existing ADS_STRUCT
392 * @return status of connection
394 ADS_STATUS ads_connect(ADS_STRUCT *ads)
396 int version = LDAP_VERSION3;
397 ADS_STATUS status;
398 NTSTATUS ntstatus;
399 char addr[INET6_ADDRSTRLEN];
401 ZERO_STRUCT(ads->ldap);
402 ads->ldap.last_attempt = time(NULL);
403 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
405 /* try with a user specified server */
407 if (DEBUGLEVEL >= 11) {
408 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
409 DEBUG(11,("ads_connect: entering\n"));
410 DEBUGADD(11,("%s\n", s));
411 TALLOC_FREE(s);
414 if (ads->server.ldap_server &&
415 ads_try_connect(ads, ads->server.ldap_server)) {
416 goto got_connection;
419 ntstatus = ads_find_dc(ads);
420 if (NT_STATUS_IS_OK(ntstatus)) {
421 goto got_connection;
424 status = ADS_ERROR_NT(ntstatus);
425 goto out;
427 got_connection:
429 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
430 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
432 if (!ads->auth.user_name) {
433 /* Must use the userPrincipalName value here or sAMAccountName
434 and not servicePrincipalName; found by Guenther Deschner */
436 asprintf(&ads->auth.user_name, "%s$", global_myname() );
439 if (!ads->auth.realm) {
440 ads->auth.realm = SMB_STRDUP(ads->config.realm);
443 if (!ads->auth.kdc_server) {
444 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
445 ads->auth.kdc_server = SMB_STRDUP(addr);
448 #if KRB5_DNS_HACK
449 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
450 to MIT kerberos to work (tridge) */
452 char *env;
453 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
454 setenv(env, ads->auth.kdc_server, 1);
455 free(env);
457 #endif
459 /* If the caller() requested no LDAP bind, then we are done */
461 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
462 status = ADS_SUCCESS;
463 goto out;
466 ads->ldap.mem_ctx = talloc_init("ads LDAP connection memory");
467 if (!ads->ldap.mem_ctx) {
468 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
469 goto out;
472 /* Otherwise setup the TCP LDAP session */
474 ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name,
475 LDAP_PORT, lp_ldap_timeout());
476 if (ads->ldap.ld == NULL) {
477 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
478 goto out;
480 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
482 /* cache the successful connection for workgroup and realm */
483 if (ads_closest_dc(ads)) {
484 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
485 saf_store( ads->server.workgroup, addr);
486 saf_store( ads->server.realm, addr);
489 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
491 status = ADS_ERROR(smb_ldap_start_tls(ads->ldap.ld, version));
492 if (!ADS_ERR_OK(status)) {
493 goto out;
496 /* fill in the current time and offsets */
498 status = ads_current_time( ads );
499 if ( !ADS_ERR_OK(status) ) {
500 goto out;
503 /* Now do the bind */
505 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
506 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
507 goto out;
510 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
511 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
512 goto out;
515 status = ads_sasl_bind(ads);
517 out:
518 if (DEBUGLEVEL >= 11) {
519 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
520 DEBUG(11,("ads_connect: leaving with: %s\n",
521 ads_errstr(status)));
522 DEBUGADD(11,("%s\n", s));
523 TALLOC_FREE(s);
526 return status;
530 * Disconnect the LDAP server
531 * @param ads Pointer to an existing ADS_STRUCT
533 void ads_disconnect(ADS_STRUCT *ads)
535 if (ads->ldap.ld) {
536 ldap_unbind(ads->ldap.ld);
537 ads->ldap.ld = NULL;
539 if (ads->ldap.wrap_ops && ads->ldap.wrap_ops->disconnect) {
540 ads->ldap.wrap_ops->disconnect(ads);
542 if (ads->ldap.mem_ctx) {
543 talloc_free(ads->ldap.mem_ctx);
545 ZERO_STRUCT(ads->ldap);
549 Duplicate a struct berval into talloc'ed memory
551 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
553 struct berval *value;
555 if (!in_val) return NULL;
557 value = TALLOC_ZERO_P(ctx, struct berval);
558 if (value == NULL)
559 return NULL;
560 if (in_val->bv_len == 0) return value;
562 value->bv_len = in_val->bv_len;
563 value->bv_val = (char *)TALLOC_MEMDUP(ctx, in_val->bv_val,
564 in_val->bv_len);
565 return value;
569 Make a values list out of an array of (struct berval *)
571 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
572 const struct berval **in_vals)
574 struct berval **values;
575 int i;
577 if (!in_vals) return NULL;
578 for (i=0; in_vals[i]; i++)
579 ; /* count values */
580 values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
581 if (!values) return NULL;
583 for (i=0; in_vals[i]; i++) {
584 values[i] = dup_berval(ctx, in_vals[i]);
586 return values;
590 UTF8-encode a values list out of an array of (char *)
592 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
594 char **values;
595 int i;
597 if (!in_vals) return NULL;
598 for (i=0; in_vals[i]; i++)
599 ; /* count values */
600 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
601 if (!values) return NULL;
603 for (i=0; in_vals[i]; i++) {
604 if (push_utf8_talloc(ctx, &values[i], in_vals[i]) == (size_t) -1) {
605 TALLOC_FREE(values);
606 return NULL;
609 return values;
613 Pull a (char *) array out of a UTF8-encoded values list
615 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
617 char **values;
618 int i;
620 if (!in_vals) return NULL;
621 for (i=0; in_vals[i]; i++)
622 ; /* count values */
623 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
624 if (!values) return NULL;
626 for (i=0; in_vals[i]; i++) {
627 pull_utf8_talloc(ctx, &values[i], in_vals[i]);
629 return values;
633 * Do a search with paged results. cookie must be null on the first
634 * call, and then returned on each subsequent call. It will be null
635 * again when the entire search is complete
636 * @param ads connection to ads server
637 * @param bind_path Base dn for the search
638 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
639 * @param expr Search expression - specified in local charset
640 * @param attrs Attributes to retrieve - specified in utf8 or ascii
641 * @param res ** which will contain results - free res* with ads_msgfree()
642 * @param count Number of entries retrieved on this page
643 * @param cookie The paged results cookie to be returned on subsequent calls
644 * @return status of search
646 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
647 const char *bind_path,
648 int scope, const char *expr,
649 const char **attrs, void *args,
650 LDAPMessage **res,
651 int *count, struct berval **cookie)
653 int rc, i, version;
654 char *utf8_expr, *utf8_path, **search_attrs;
655 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
656 BerElement *cookie_be = NULL;
657 struct berval *cookie_bv= NULL;
658 BerElement *ext_be = NULL;
659 struct berval *ext_bv= NULL;
661 TALLOC_CTX *ctx;
662 ads_control *external_control = (ads_control *) args;
664 *res = NULL;
666 if (!(ctx = talloc_init("ads_do_paged_search_args")))
667 return ADS_ERROR(LDAP_NO_MEMORY);
669 /* 0 means the conversion worked but the result was empty
670 so we only fail if it's -1. In any case, it always
671 at least nulls out the dest */
672 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
673 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
674 rc = LDAP_NO_MEMORY;
675 goto done;
678 if (!attrs || !(*attrs))
679 search_attrs = NULL;
680 else {
681 /* This would be the utf8-encoded version...*/
682 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
683 if (!(str_list_copy(talloc_tos(), &search_attrs, attrs))) {
684 rc = LDAP_NO_MEMORY;
685 goto done;
689 /* Paged results only available on ldap v3 or later */
690 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
691 if (version < LDAP_VERSION3) {
692 rc = LDAP_NOT_SUPPORTED;
693 goto done;
696 cookie_be = ber_alloc_t(LBER_USE_DER);
697 if (*cookie) {
698 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
699 ber_bvfree(*cookie); /* don't need it from last time */
700 *cookie = NULL;
701 } else {
702 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
704 ber_flatten(cookie_be, &cookie_bv);
705 PagedResults.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID);
706 PagedResults.ldctl_iscritical = (char) 1;
707 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
708 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
710 NoReferrals.ldctl_oid = CONST_DISCARD(char *, ADS_NO_REFERRALS_OID);
711 NoReferrals.ldctl_iscritical = (char) 0;
712 NoReferrals.ldctl_value.bv_len = 0;
713 NoReferrals.ldctl_value.bv_val = CONST_DISCARD(char *, "");
715 if (external_control &&
716 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
717 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
719 ExternalCtrl.ldctl_oid = CONST_DISCARD(char *, external_control->control);
720 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
722 /* win2k does not accept a ldctl_value beeing passed in */
724 if (external_control->val != 0) {
726 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
727 rc = LDAP_NO_MEMORY;
728 goto done;
731 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
732 rc = LDAP_NO_MEMORY;
733 goto done;
735 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
736 rc = LDAP_NO_MEMORY;
737 goto done;
740 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
741 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
743 } else {
744 ExternalCtrl.ldctl_value.bv_len = 0;
745 ExternalCtrl.ldctl_value.bv_val = NULL;
748 controls[0] = &NoReferrals;
749 controls[1] = &PagedResults;
750 controls[2] = &ExternalCtrl;
751 controls[3] = NULL;
753 } else {
754 controls[0] = &NoReferrals;
755 controls[1] = &PagedResults;
756 controls[2] = NULL;
759 /* we need to disable referrals as the openldap libs don't
760 handle them and paged results at the same time. Using them
761 together results in the result record containing the server
762 page control being removed from the result list (tridge/jmcd)
764 leaving this in despite the control that says don't generate
765 referrals, in case the server doesn't support it (jmcd)
767 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
769 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
770 search_attrs, 0, controls,
771 NULL, LDAP_NO_LIMIT,
772 (LDAPMessage **)res);
774 ber_free(cookie_be, 1);
775 ber_bvfree(cookie_bv);
777 if (rc) {
778 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
779 ldap_err2string(rc)));
780 goto done;
783 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
784 NULL, &rcontrols, 0);
786 if (!rcontrols) {
787 goto done;
790 for (i=0; rcontrols[i]; i++) {
791 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
792 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
793 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
794 &cookie_bv);
795 /* the berval is the cookie, but must be freed when
796 it is all done */
797 if (cookie_bv->bv_len) /* still more to do */
798 *cookie=ber_bvdup(cookie_bv);
799 else
800 *cookie=NULL;
801 ber_bvfree(cookie_bv);
802 ber_free(cookie_be, 1);
803 break;
806 ldap_controls_free(rcontrols);
808 done:
809 talloc_destroy(ctx);
811 if (ext_be) {
812 ber_free(ext_be, 1);
815 if (ext_bv) {
816 ber_bvfree(ext_bv);
819 /* if/when we decide to utf8-encode attrs, take out this next line */
820 TALLOC_FREE(search_attrs);
822 return ADS_ERROR(rc);
825 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
826 int scope, const char *expr,
827 const char **attrs, LDAPMessage **res,
828 int *count, struct berval **cookie)
830 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
835 * Get all results for a search. This uses ads_do_paged_search() to return
836 * all entries in a large search.
837 * @param ads connection to ads server
838 * @param bind_path Base dn for the search
839 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
840 * @param expr Search expression
841 * @param attrs Attributes to retrieve
842 * @param res ** which will contain results - free res* with ads_msgfree()
843 * @return status of search
845 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
846 int scope, const char *expr,
847 const char **attrs, void *args,
848 LDAPMessage **res)
850 struct berval *cookie = NULL;
851 int count = 0;
852 ADS_STATUS status;
854 *res = NULL;
855 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
856 &count, &cookie);
858 if (!ADS_ERR_OK(status))
859 return status;
861 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
862 while (cookie) {
863 LDAPMessage *res2 = NULL;
864 ADS_STATUS status2;
865 LDAPMessage *msg, *next;
867 status2 = ads_do_paged_search_args(ads, bind_path, scope, expr,
868 attrs, args, &res2, &count, &cookie);
870 if (!ADS_ERR_OK(status2)) break;
872 /* this relies on the way that ldap_add_result_entry() works internally. I hope
873 that this works on all ldap libs, but I have only tested with openldap */
874 for (msg = ads_first_message(ads, res2); msg; msg = next) {
875 next = ads_next_message(ads, msg);
876 ldap_add_result_entry((LDAPMessage **)res, msg);
878 /* note that we do not free res2, as the memory is now
879 part of the main returned list */
881 #else
882 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
883 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
884 #endif
886 return status;
889 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
890 int scope, const char *expr,
891 const char **attrs, LDAPMessage **res)
893 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
896 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
897 int scope, const char *expr,
898 const char **attrs, uint32 sd_flags,
899 LDAPMessage **res)
901 ads_control args;
903 args.control = ADS_SD_FLAGS_OID;
904 args.val = sd_flags;
905 args.critical = True;
907 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
912 * Run a function on all results for a search. Uses ads_do_paged_search() and
913 * runs the function as each page is returned, using ads_process_results()
914 * @param ads connection to ads server
915 * @param bind_path Base dn for the search
916 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
917 * @param expr Search expression - specified in local charset
918 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
919 * @param fn Function which takes attr name, values list, and data_area
920 * @param data_area Pointer which is passed to function on each call
921 * @return status of search
923 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
924 int scope, const char *expr, const char **attrs,
925 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
926 void *data_area)
928 struct berval *cookie = NULL;
929 int count = 0;
930 ADS_STATUS status;
931 LDAPMessage *res;
933 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
934 &count, &cookie);
936 if (!ADS_ERR_OK(status)) return status;
938 ads_process_results(ads, res, fn, data_area);
939 ads_msgfree(ads, res);
941 while (cookie) {
942 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
943 &res, &count, &cookie);
945 if (!ADS_ERR_OK(status)) break;
947 ads_process_results(ads, res, fn, data_area);
948 ads_msgfree(ads, res);
951 return status;
955 * Do a search with a timeout.
956 * @param ads connection to ads server
957 * @param bind_path Base dn for the search
958 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
959 * @param expr Search expression
960 * @param attrs Attributes to retrieve
961 * @param res ** which will contain results - free res* with ads_msgfree()
962 * @return status of search
964 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
965 const char *expr,
966 const char **attrs, LDAPMessage **res)
968 int rc;
969 char *utf8_expr, *utf8_path, **search_attrs = NULL;
970 TALLOC_CTX *ctx;
972 *res = NULL;
973 if (!(ctx = talloc_init("ads_do_search"))) {
974 DEBUG(1,("ads_do_search: talloc_init() failed!"));
975 return ADS_ERROR(LDAP_NO_MEMORY);
978 /* 0 means the conversion worked but the result was empty
979 so we only fail if it's negative. In any case, it always
980 at least nulls out the dest */
981 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
982 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
983 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
984 rc = LDAP_NO_MEMORY;
985 goto done;
988 if (!attrs || !(*attrs))
989 search_attrs = NULL;
990 else {
991 /* This would be the utf8-encoded version...*/
992 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
993 if (!(str_list_copy(talloc_tos(), &search_attrs, attrs)))
995 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
996 rc = LDAP_NO_MEMORY;
997 goto done;
1001 /* see the note in ads_do_paged_search - we *must* disable referrals */
1002 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1004 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1005 search_attrs, 0, NULL, NULL,
1006 LDAP_NO_LIMIT,
1007 (LDAPMessage **)res);
1009 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1010 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1011 rc = 0;
1014 done:
1015 talloc_destroy(ctx);
1016 /* if/when we decide to utf8-encode attrs, take out this next line */
1017 TALLOC_FREE(search_attrs);
1018 return ADS_ERROR(rc);
1021 * Do a general ADS search
1022 * @param ads connection to ads server
1023 * @param res ** which will contain results - free res* with ads_msgfree()
1024 * @param expr Search expression
1025 * @param attrs Attributes to retrieve
1026 * @return status of search
1028 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1029 const char *expr, const char **attrs)
1031 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1032 expr, attrs, res);
1036 * Do a search on a specific DistinguishedName
1037 * @param ads connection to ads server
1038 * @param res ** which will contain results - free res* with ads_msgfree()
1039 * @param dn DistinguishName to search
1040 * @param attrs Attributes to retrieve
1041 * @return status of search
1043 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1044 const char *dn, const char **attrs)
1046 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1047 attrs, res);
1051 * Free up memory from a ads_search
1052 * @param ads connection to ads server
1053 * @param msg Search results to free
1055 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1057 if (!msg) return;
1058 ldap_msgfree(msg);
1062 * Free up memory from various ads requests
1063 * @param ads connection to ads server
1064 * @param mem Area to free
1066 void ads_memfree(ADS_STRUCT *ads, void *mem)
1068 SAFE_FREE(mem);
1072 * Get a dn from search results
1073 * @param ads connection to ads server
1074 * @param msg Search result
1075 * @return dn string
1077 char *ads_get_dn(ADS_STRUCT *ads, LDAPMessage *msg)
1079 char *utf8_dn, *unix_dn;
1081 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1083 if (!utf8_dn) {
1084 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1085 return NULL;
1088 if (pull_utf8_allocate(&unix_dn, utf8_dn) == (size_t)-1) {
1089 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1090 utf8_dn ));
1091 return NULL;
1093 ldap_memfree(utf8_dn);
1094 return unix_dn;
1098 * Get the parent from a dn
1099 * @param dn the dn to return the parent from
1100 * @return parent dn string
1102 char *ads_parent_dn(const char *dn)
1104 char *p;
1106 if (dn == NULL) {
1107 return NULL;
1110 p = strchr(dn, ',');
1112 if (p == NULL) {
1113 return NULL;
1116 return p+1;
1120 * Find a machine account given a hostname
1121 * @param ads connection to ads server
1122 * @param res ** which will contain results - free res* with ads_msgfree()
1123 * @param host Hostname to search for
1124 * @return status of search
1126 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1127 const char *machine)
1129 ADS_STATUS status;
1130 char *expr;
1131 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1133 *res = NULL;
1135 /* the easiest way to find a machine account anywhere in the tree
1136 is to look for hostname$ */
1137 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1138 DEBUG(1, ("asprintf failed!\n"));
1139 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1142 status = ads_search(ads, res, expr, attrs);
1143 SAFE_FREE(expr);
1144 return status;
1148 * Initialize a list of mods to be used in a modify request
1149 * @param ctx An initialized TALLOC_CTX
1150 * @return allocated ADS_MODLIST
1152 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1154 #define ADS_MODLIST_ALLOC_SIZE 10
1155 LDAPMod **mods;
1157 if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1158 /* -1 is safety to make sure we don't go over the end.
1159 need to reset it to NULL before doing ldap modify */
1160 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1162 return (ADS_MODLIST)mods;
1167 add an attribute to the list, with values list already constructed
1169 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1170 int mod_op, const char *name,
1171 const void *_invals)
1173 const void **invals = (const void **)_invals;
1174 int curmod;
1175 LDAPMod **modlist = (LDAPMod **) *mods;
1176 struct berval **ber_values = NULL;
1177 char **char_values = NULL;
1179 if (!invals) {
1180 mod_op = LDAP_MOD_DELETE;
1181 } else {
1182 if (mod_op & LDAP_MOD_BVALUES)
1183 ber_values = ads_dup_values(ctx,
1184 (const struct berval **)invals);
1185 else
1186 char_values = ads_push_strvals(ctx,
1187 (const char **) invals);
1190 /* find the first empty slot */
1191 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1192 curmod++);
1193 if (modlist[curmod] == (LDAPMod *) -1) {
1194 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
1195 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1196 return ADS_ERROR(LDAP_NO_MEMORY);
1197 memset(&modlist[curmod], 0,
1198 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1199 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1200 *mods = (ADS_MODLIST)modlist;
1203 if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
1204 return ADS_ERROR(LDAP_NO_MEMORY);
1205 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1206 if (mod_op & LDAP_MOD_BVALUES) {
1207 modlist[curmod]->mod_bvalues = ber_values;
1208 } else if (mod_op & LDAP_MOD_DELETE) {
1209 modlist[curmod]->mod_values = NULL;
1210 } else {
1211 modlist[curmod]->mod_values = char_values;
1214 modlist[curmod]->mod_op = mod_op;
1215 return ADS_ERROR(LDAP_SUCCESS);
1219 * Add a single string value to a mod list
1220 * @param ctx An initialized TALLOC_CTX
1221 * @param mods An initialized ADS_MODLIST
1222 * @param name The attribute name to add
1223 * @param val The value to add - NULL means DELETE
1224 * @return ADS STATUS indicating success of add
1226 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1227 const char *name, const char *val)
1229 const char *values[2];
1231 values[0] = val;
1232 values[1] = NULL;
1234 if (!val)
1235 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1236 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1240 * Add an array of string values to a mod list
1241 * @param ctx An initialized TALLOC_CTX
1242 * @param mods An initialized ADS_MODLIST
1243 * @param name The attribute name to add
1244 * @param vals The array of string values to add - NULL means DELETE
1245 * @return ADS STATUS indicating success of add
1247 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1248 const char *name, const char **vals)
1250 if (!vals)
1251 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1252 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1253 name, (const void **) vals);
1256 #if 0
1258 * Add a single ber-encoded value to a mod list
1259 * @param ctx An initialized TALLOC_CTX
1260 * @param mods An initialized ADS_MODLIST
1261 * @param name The attribute name to add
1262 * @param val The value to add - NULL means DELETE
1263 * @return ADS STATUS indicating success of add
1265 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1266 const char *name, const struct berval *val)
1268 const struct berval *values[2];
1270 values[0] = val;
1271 values[1] = NULL;
1272 if (!val)
1273 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1274 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1275 name, (const void **) values);
1277 #endif
1280 * Perform an ldap modify
1281 * @param ads connection to ads server
1282 * @param mod_dn DistinguishedName to modify
1283 * @param mods list of modifications to perform
1284 * @return status of modify
1286 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1288 int ret,i;
1289 char *utf8_dn = NULL;
1291 this control is needed to modify that contains a currently
1292 non-existent attribute (but allowable for the object) to run
1294 LDAPControl PermitModify = {
1295 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
1296 {0, NULL},
1297 (char) 1};
1298 LDAPControl *controls[2];
1300 controls[0] = &PermitModify;
1301 controls[1] = NULL;
1303 if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) {
1304 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1307 /* find the end of the list, marked by NULL or -1 */
1308 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1309 /* make sure the end of the list is NULL */
1310 mods[i] = NULL;
1311 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1312 (LDAPMod **) mods, controls, NULL);
1313 SAFE_FREE(utf8_dn);
1314 return ADS_ERROR(ret);
1318 * Perform an ldap add
1319 * @param ads connection to ads server
1320 * @param new_dn DistinguishedName to add
1321 * @param mods list of attributes and values for DN
1322 * @return status of add
1324 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1326 int ret, i;
1327 char *utf8_dn = NULL;
1329 if (push_utf8_allocate(&utf8_dn, new_dn) == -1) {
1330 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1331 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1334 /* find the end of the list, marked by NULL or -1 */
1335 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1336 /* make sure the end of the list is NULL */
1337 mods[i] = NULL;
1339 ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1340 SAFE_FREE(utf8_dn);
1341 return ADS_ERROR(ret);
1345 * Delete a DistinguishedName
1346 * @param ads connection to ads server
1347 * @param new_dn DistinguishedName to delete
1348 * @return status of delete
1350 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1352 int ret;
1353 char *utf8_dn = NULL;
1354 if (push_utf8_allocate(&utf8_dn, del_dn) == -1) {
1355 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1356 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1359 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1360 SAFE_FREE(utf8_dn);
1361 return ADS_ERROR(ret);
1365 * Build an org unit string
1366 * if org unit is Computers or blank then assume a container, otherwise
1367 * assume a / separated list of organisational units.
1368 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1369 * @param ads connection to ads server
1370 * @param org_unit Organizational unit
1371 * @return org unit string - caller must free
1373 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1375 char *ret = NULL;
1377 if (!org_unit || !*org_unit) {
1379 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1381 /* samba4 might not yet respond to a wellknownobject-query */
1382 return ret ? ret : SMB_STRDUP("cn=Computers");
1385 if (strequal(org_unit, "Computers")) {
1386 return SMB_STRDUP("cn=Computers");
1389 /* jmcd: removed "\\" from the separation chars, because it is
1390 needed as an escape for chars like '#' which are valid in an
1391 OU name */
1392 return ads_build_path(org_unit, "/", "ou=", 1);
1396 * Get a org unit string for a well-known GUID
1397 * @param ads connection to ads server
1398 * @param wknguid Well known GUID
1399 * @return org unit string - caller must free
1401 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1403 ADS_STATUS status;
1404 LDAPMessage *res = NULL;
1405 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1406 **bind_dn_exp = NULL;
1407 const char *attrs[] = {"distinguishedName", NULL};
1408 int new_ln, wkn_ln, bind_ln, i;
1410 if (wknguid == NULL) {
1411 return NULL;
1414 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1415 DEBUG(1, ("asprintf failed!\n"));
1416 return NULL;
1419 status = ads_search_dn(ads, &res, base, attrs);
1420 if (!ADS_ERR_OK(status)) {
1421 DEBUG(1,("Failed while searching for: %s\n", base));
1422 goto out;
1425 if (ads_count_replies(ads, res) != 1) {
1426 goto out;
1429 /* substitute the bind-path from the well-known-guid-search result */
1430 wkn_dn = ads_get_dn(ads, res);
1431 if (!wkn_dn) {
1432 goto out;
1435 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1436 if (!wkn_dn_exp) {
1437 goto out;
1440 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1441 if (!bind_dn_exp) {
1442 goto out;
1445 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1447 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1450 new_ln = wkn_ln - bind_ln;
1452 ret = SMB_STRDUP(wkn_dn_exp[0]);
1453 if (!ret) {
1454 goto out;
1457 for (i=1; i < new_ln; i++) {
1458 char *s = NULL;
1460 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1461 SAFE_FREE(ret);
1462 goto out;
1465 SAFE_FREE(ret);
1466 ret = SMB_STRDUP(s);
1467 free(s);
1468 if (!ret) {
1469 goto out;
1473 out:
1474 SAFE_FREE(base);
1475 ads_msgfree(ads, res);
1476 ads_memfree(ads, wkn_dn);
1477 if (wkn_dn_exp) {
1478 ldap_value_free(wkn_dn_exp);
1480 if (bind_dn_exp) {
1481 ldap_value_free(bind_dn_exp);
1484 return ret;
1488 * Adds (appends) an item to an attribute array, rather then
1489 * replacing the whole list
1490 * @param ctx An initialized TALLOC_CTX
1491 * @param mods An initialized ADS_MODLIST
1492 * @param name name of the ldap attribute to append to
1493 * @param vals an array of values to add
1494 * @return status of addition
1497 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1498 const char *name, const char **vals)
1500 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1501 (const void *) vals);
1505 * Determines the computer account's current KVNO via an LDAP lookup
1506 * @param ads An initialized ADS_STRUCT
1507 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1508 * @return the kvno for the computer account, or -1 in case of a failure.
1511 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *machine_name)
1513 LDAPMessage *res = NULL;
1514 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1515 char *filter;
1516 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1517 char *dn_string = NULL;
1518 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1520 DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name));
1521 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1522 return kvno;
1524 ret = ads_search(ads, &res, filter, attrs);
1525 SAFE_FREE(filter);
1526 if (!ADS_ERR_OK(ret) && ads_count_replies(ads, res)) {
1527 DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name));
1528 ads_msgfree(ads, res);
1529 return kvno;
1532 dn_string = ads_get_dn(ads, res);
1533 if (!dn_string) {
1534 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1535 ads_msgfree(ads, res);
1536 return kvno;
1538 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1539 ads_memfree(ads, dn_string);
1541 /* ---------------------------------------------------------
1542 * 0 is returned as a default KVNO from this point on...
1543 * This is done because Windows 2000 does not support key
1544 * version numbers. Chances are that a failure in the next
1545 * step is simply due to Windows 2000 being used for a
1546 * domain controller. */
1547 kvno = 0;
1549 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1550 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1551 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1552 ads_msgfree(ads, res);
1553 return kvno;
1556 /* Success */
1557 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1558 ads_msgfree(ads, res);
1559 return kvno;
1563 * This clears out all registered spn's for a given hostname
1564 * @param ads An initilaized ADS_STRUCT
1565 * @param machine_name the NetBIOS name of the computer.
1566 * @return 0 upon success, non-zero otherwise.
1569 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1571 TALLOC_CTX *ctx;
1572 LDAPMessage *res = NULL;
1573 ADS_MODLIST mods;
1574 const char *servicePrincipalName[1] = {NULL};
1575 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1576 char *dn_string = NULL;
1578 ret = ads_find_machine_acct(ads, &res, machine_name);
1579 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1580 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1581 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1582 ads_msgfree(ads, res);
1583 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1586 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1587 ctx = talloc_init("ads_clear_service_principal_names");
1588 if (!ctx) {
1589 ads_msgfree(ads, res);
1590 return ADS_ERROR(LDAP_NO_MEMORY);
1593 if (!(mods = ads_init_mods(ctx))) {
1594 talloc_destroy(ctx);
1595 ads_msgfree(ads, res);
1596 return ADS_ERROR(LDAP_NO_MEMORY);
1598 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1599 if (!ADS_ERR_OK(ret)) {
1600 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1601 ads_msgfree(ads, res);
1602 talloc_destroy(ctx);
1603 return ret;
1605 dn_string = ads_get_dn(ads, res);
1606 if (!dn_string) {
1607 talloc_destroy(ctx);
1608 ads_msgfree(ads, res);
1609 return ADS_ERROR(LDAP_NO_MEMORY);
1611 ret = ads_gen_mod(ads, dn_string, mods);
1612 ads_memfree(ads,dn_string);
1613 if (!ADS_ERR_OK(ret)) {
1614 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1615 machine_name));
1616 ads_msgfree(ads, res);
1617 talloc_destroy(ctx);
1618 return ret;
1621 ads_msgfree(ads, res);
1622 talloc_destroy(ctx);
1623 return ret;
1627 * This adds a service principal name to an existing computer account
1628 * (found by hostname) in AD.
1629 * @param ads An initialized ADS_STRUCT
1630 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1631 * @param my_fqdn The fully qualified DNS name of the machine
1632 * @param spn A string of the service principal to add, i.e. 'host'
1633 * @return 0 upon sucess, or non-zero if a failure occurs
1636 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1637 const char *my_fqdn, const char *spn)
1639 ADS_STATUS ret;
1640 TALLOC_CTX *ctx;
1641 LDAPMessage *res = NULL;
1642 char *psp1, *psp2;
1643 ADS_MODLIST mods;
1644 char *dn_string = NULL;
1645 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1647 ret = ads_find_machine_acct(ads, &res, machine_name);
1648 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1649 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1650 machine_name));
1651 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1652 spn, machine_name, ads->config.realm));
1653 ads_msgfree(ads, res);
1654 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1657 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1658 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1659 ads_msgfree(ads, res);
1660 return ADS_ERROR(LDAP_NO_MEMORY);
1663 /* add short name spn */
1665 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1666 talloc_destroy(ctx);
1667 ads_msgfree(ads, res);
1668 return ADS_ERROR(LDAP_NO_MEMORY);
1670 strupper_m(psp1);
1671 strlower_m(&psp1[strlen(spn)]);
1672 servicePrincipalName[0] = psp1;
1674 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1675 psp1, machine_name));
1678 /* add fully qualified spn */
1680 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1681 ret = ADS_ERROR(LDAP_NO_MEMORY);
1682 goto out;
1684 strupper_m(psp2);
1685 strlower_m(&psp2[strlen(spn)]);
1686 servicePrincipalName[1] = psp2;
1688 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1689 psp2, machine_name));
1691 if ( (mods = ads_init_mods(ctx)) == NULL ) {
1692 ret = ADS_ERROR(LDAP_NO_MEMORY);
1693 goto out;
1696 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1697 if (!ADS_ERR_OK(ret)) {
1698 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1699 goto out;
1702 if ( (dn_string = ads_get_dn(ads, res)) == NULL ) {
1703 ret = ADS_ERROR(LDAP_NO_MEMORY);
1704 goto out;
1707 ret = ads_gen_mod(ads, dn_string, mods);
1708 ads_memfree(ads,dn_string);
1709 if (!ADS_ERR_OK(ret)) {
1710 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1711 goto out;
1714 out:
1715 TALLOC_FREE( ctx );
1716 ads_msgfree(ads, res);
1717 return ret;
1721 * adds a machine account to the ADS server
1722 * @param ads An intialized ADS_STRUCT
1723 * @param machine_name - the NetBIOS machine name of this account.
1724 * @param account_type A number indicating the type of account to create
1725 * @param org_unit The LDAP path in which to place this account
1726 * @return 0 upon success, or non-zero otherwise
1729 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1730 const char *org_unit)
1732 ADS_STATUS ret;
1733 char *samAccountName, *controlstr;
1734 TALLOC_CTX *ctx;
1735 ADS_MODLIST mods;
1736 char *machine_escaped = NULL;
1737 char *new_dn;
1738 const char *objectClass[] = {"top", "person", "organizationalPerson",
1739 "user", "computer", NULL};
1740 LDAPMessage *res = NULL;
1741 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
1742 UF_DONT_EXPIRE_PASSWD |\
1743 UF_ACCOUNTDISABLE );
1745 if (!(ctx = talloc_init("ads_add_machine_acct")))
1746 return ADS_ERROR(LDAP_NO_MEMORY);
1748 ret = ADS_ERROR(LDAP_NO_MEMORY);
1750 machine_escaped = escape_rdn_val_string_alloc(machine_name);
1751 if (!machine_escaped) {
1752 goto done;
1755 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
1756 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
1758 if ( !new_dn || !samAccountName ) {
1759 goto done;
1762 #ifndef ENCTYPE_ARCFOUR_HMAC
1763 acct_control |= UF_USE_DES_KEY_ONLY;
1764 #endif
1766 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
1767 goto done;
1770 if (!(mods = ads_init_mods(ctx))) {
1771 goto done;
1774 ads_mod_str(ctx, &mods, "cn", machine_name);
1775 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1776 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1777 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1779 ret = ads_gen_add(ads, new_dn, mods);
1781 done:
1782 SAFE_FREE(machine_escaped);
1783 ads_msgfree(ads, res);
1784 talloc_destroy(ctx);
1786 return ret;
1790 * move a machine account to another OU on the ADS server
1791 * @param ads - An intialized ADS_STRUCT
1792 * @param machine_name - the NetBIOS machine name of this account.
1793 * @param org_unit - The LDAP path in which to place this account
1794 * @param moved - whether we moved the machine account (optional)
1795 * @return 0 upon success, or non-zero otherwise
1798 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1799 const char *org_unit, bool *moved)
1801 ADS_STATUS rc;
1802 int ldap_status;
1803 LDAPMessage *res = NULL;
1804 char *filter = NULL;
1805 char *computer_dn = NULL;
1806 char *parent_dn;
1807 char *computer_rdn = NULL;
1808 bool need_move = False;
1810 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1811 rc = ADS_ERROR(LDAP_NO_MEMORY);
1812 goto done;
1815 /* Find pre-existing machine */
1816 rc = ads_search(ads, &res, filter, NULL);
1817 if (!ADS_ERR_OK(rc)) {
1818 goto done;
1821 computer_dn = ads_get_dn(ads, res);
1822 if (!computer_dn) {
1823 rc = ADS_ERROR(LDAP_NO_MEMORY);
1824 goto done;
1827 parent_dn = ads_parent_dn(computer_dn);
1828 if (strequal(parent_dn, org_unit)) {
1829 goto done;
1832 need_move = True;
1834 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
1835 rc = ADS_ERROR(LDAP_NO_MEMORY);
1836 goto done;
1839 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
1840 org_unit, 1, NULL, NULL);
1841 rc = ADS_ERROR(ldap_status);
1843 done:
1844 ads_msgfree(ads, res);
1845 SAFE_FREE(filter);
1846 SAFE_FREE(computer_dn);
1847 SAFE_FREE(computer_rdn);
1849 if (!ADS_ERR_OK(rc)) {
1850 need_move = False;
1853 if (moved) {
1854 *moved = need_move;
1857 return rc;
1861 dump a binary result from ldap
1863 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
1865 int i, j;
1866 for (i=0; values[i]; i++) {
1867 printf("%s: ", field);
1868 for (j=0; j<values[i]->bv_len; j++) {
1869 printf("%02X", (unsigned char)values[i]->bv_val[j]);
1871 printf("\n");
1875 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
1877 int i;
1878 for (i=0; values[i]; i++) {
1880 UUID_FLAT guid;
1881 struct GUID tmp;
1883 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1884 smb_uuid_unpack(guid, &tmp);
1885 printf("%s: %s\n", field, smb_uuid_string(talloc_tos(), tmp));
1890 dump a sid result from ldap
1892 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
1894 int i;
1895 for (i=0; values[i]; i++) {
1896 DOM_SID sid;
1897 fstring tmp;
1898 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1899 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
1904 dump ntSecurityDescriptor
1906 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
1908 TALLOC_CTX *frame = talloc_stackframe();
1909 struct security_descriptor *psd;
1910 NTSTATUS status;
1912 status = unmarshall_sec_desc(talloc_tos(), (uint8 *)values[0]->bv_val,
1913 values[0]->bv_len, &psd);
1914 if (!NT_STATUS_IS_OK(status)) {
1915 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
1916 nt_errstr(status)));
1917 TALLOC_FREE(frame);
1918 return;
1921 if (psd) {
1922 ads_disp_sd(ads, talloc_tos(), psd);
1925 TALLOC_FREE(frame);
1929 dump a string result from ldap
1931 static void dump_string(const char *field, char **values)
1933 int i;
1934 for (i=0; values[i]; i++) {
1935 printf("%s: %s\n", field, values[i]);
1940 dump a field from LDAP on stdout
1941 used for debugging
1944 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
1946 const struct {
1947 const char *name;
1948 bool string;
1949 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
1950 } handlers[] = {
1951 {"objectGUID", False, dump_guid},
1952 {"netbootGUID", False, dump_guid},
1953 {"nTSecurityDescriptor", False, dump_sd},
1954 {"dnsRecord", False, dump_binary},
1955 {"objectSid", False, dump_sid},
1956 {"tokenGroups", False, dump_sid},
1957 {"tokenGroupsNoGCAcceptable", False, dump_sid},
1958 {"tokengroupsGlobalandUniversal", False, dump_sid},
1959 {"mS-DS-CreatorSID", False, dump_sid},
1960 {"msExchMailboxGuid", False, dump_guid},
1961 {NULL, True, NULL}
1963 int i;
1965 if (!field) { /* must be end of an entry */
1966 printf("\n");
1967 return False;
1970 for (i=0; handlers[i].name; i++) {
1971 if (StrCaseCmp(handlers[i].name, field) == 0) {
1972 if (!values) /* first time, indicate string or not */
1973 return handlers[i].string;
1974 handlers[i].handler(ads, field, (struct berval **) values);
1975 break;
1978 if (!handlers[i].name) {
1979 if (!values) /* first time, indicate string conversion */
1980 return True;
1981 dump_string(field, (char **)values);
1983 return False;
1987 * Dump a result from LDAP on stdout
1988 * used for debugging
1989 * @param ads connection to ads server
1990 * @param res Results to dump
1993 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
1995 ads_process_results(ads, res, ads_dump_field, NULL);
1999 * Walk through results, calling a function for each entry found.
2000 * The function receives a field name, a berval * array of values,
2001 * and a data area passed through from the start. The function is
2002 * called once with null for field and values at the end of each
2003 * entry.
2004 * @param ads connection to ads server
2005 * @param res Results to process
2006 * @param fn Function for processing each result
2007 * @param data_area user-defined area to pass to function
2009 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2010 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2011 void *data_area)
2013 LDAPMessage *msg;
2014 TALLOC_CTX *ctx;
2016 if (!(ctx = talloc_init("ads_process_results")))
2017 return;
2019 for (msg = ads_first_entry(ads, res); msg;
2020 msg = ads_next_entry(ads, msg)) {
2021 char *utf8_field;
2022 BerElement *b;
2024 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2025 (LDAPMessage *)msg,&b);
2026 utf8_field;
2027 utf8_field=ldap_next_attribute(ads->ldap.ld,
2028 (LDAPMessage *)msg,b)) {
2029 struct berval **ber_vals;
2030 char **str_vals, **utf8_vals;
2031 char *field;
2032 bool string;
2034 pull_utf8_talloc(ctx, &field, utf8_field);
2035 string = fn(ads, field, NULL, data_area);
2037 if (string) {
2038 utf8_vals = ldap_get_values(ads->ldap.ld,
2039 (LDAPMessage *)msg, field);
2040 str_vals = ads_pull_strvals(ctx,
2041 (const char **) utf8_vals);
2042 fn(ads, field, (void **) str_vals, data_area);
2043 ldap_value_free(utf8_vals);
2044 } else {
2045 ber_vals = ldap_get_values_len(ads->ldap.ld,
2046 (LDAPMessage *)msg, field);
2047 fn(ads, field, (void **) ber_vals, data_area);
2049 ldap_value_free_len(ber_vals);
2051 ldap_memfree(utf8_field);
2053 ber_free(b, 0);
2054 talloc_free_children(ctx);
2055 fn(ads, NULL, NULL, data_area); /* completed an entry */
2058 talloc_destroy(ctx);
2062 * count how many replies are in a LDAPMessage
2063 * @param ads connection to ads server
2064 * @param res Results to count
2065 * @return number of replies
2067 int ads_count_replies(ADS_STRUCT *ads, void *res)
2069 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2073 * pull the first entry from a ADS result
2074 * @param ads connection to ads server
2075 * @param res Results of search
2076 * @return first entry from result
2078 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2080 return ldap_first_entry(ads->ldap.ld, res);
2084 * pull the next entry from a ADS result
2085 * @param ads connection to ads server
2086 * @param res Results of search
2087 * @return next entry from result
2089 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2091 return ldap_next_entry(ads->ldap.ld, res);
2095 * pull the first message from a ADS result
2096 * @param ads connection to ads server
2097 * @param res Results of search
2098 * @return first message from result
2100 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2102 return ldap_first_message(ads->ldap.ld, res);
2106 * pull the next message from a ADS result
2107 * @param ads connection to ads server
2108 * @param res Results of search
2109 * @return next message from result
2111 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2113 return ldap_next_message(ads->ldap.ld, res);
2117 * pull a single string from a ADS result
2118 * @param ads connection to ads server
2119 * @param mem_ctx TALLOC_CTX to use for allocating result string
2120 * @param msg Results of search
2121 * @param field Attribute to retrieve
2122 * @return Result string in talloc context
2124 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2125 const char *field)
2127 char **values;
2128 char *ret = NULL;
2129 char *ux_string;
2130 size_t rc;
2132 values = ldap_get_values(ads->ldap.ld, msg, field);
2133 if (!values)
2134 return NULL;
2136 if (values[0]) {
2137 rc = pull_utf8_talloc(mem_ctx, &ux_string,
2138 values[0]);
2139 if (rc != (size_t)-1)
2140 ret = ux_string;
2143 ldap_value_free(values);
2144 return ret;
2148 * pull an array of strings from a ADS result
2149 * @param ads connection to ads server
2150 * @param mem_ctx TALLOC_CTX to use for allocating result string
2151 * @param msg Results of search
2152 * @param field Attribute to retrieve
2153 * @return Result strings in talloc context
2155 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2156 LDAPMessage *msg, const char *field,
2157 size_t *num_values)
2159 char **values;
2160 char **ret = NULL;
2161 int i;
2163 values = ldap_get_values(ads->ldap.ld, msg, field);
2164 if (!values)
2165 return NULL;
2167 *num_values = ldap_count_values(values);
2169 ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
2170 if (!ret) {
2171 ldap_value_free(values);
2172 return NULL;
2175 for (i=0;i<*num_values;i++) {
2176 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
2177 ldap_value_free(values);
2178 return NULL;
2181 ret[i] = NULL;
2183 ldap_value_free(values);
2184 return ret;
2188 * pull an array of strings from a ADS result
2189 * (handle large multivalue attributes with range retrieval)
2190 * @param ads connection to ads server
2191 * @param mem_ctx TALLOC_CTX to use for allocating result string
2192 * @param msg Results of search
2193 * @param field Attribute to retrieve
2194 * @param current_strings strings returned by a previous call to this function
2195 * @param next_attribute The next query should ask for this attribute
2196 * @param num_values How many values did we get this time?
2197 * @param more_values Are there more values to get?
2198 * @return Result strings in talloc context
2200 char **ads_pull_strings_range(ADS_STRUCT *ads,
2201 TALLOC_CTX *mem_ctx,
2202 LDAPMessage *msg, const char *field,
2203 char **current_strings,
2204 const char **next_attribute,
2205 size_t *num_strings,
2206 bool *more_strings)
2208 char *attr;
2209 char *expected_range_attrib, *range_attr;
2210 BerElement *ptr = NULL;
2211 char **strings;
2212 char **new_strings;
2213 size_t num_new_strings;
2214 unsigned long int range_start;
2215 unsigned long int range_end;
2217 /* we might have been given the whole lot anyway */
2218 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2219 *more_strings = False;
2220 return strings;
2223 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2225 /* look for Range result */
2226 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2227 attr;
2228 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2229 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2230 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2231 range_attr = attr;
2232 break;
2234 ldap_memfree(attr);
2236 if (!attr) {
2237 ber_free(ptr, 0);
2238 /* nothing here - this field is just empty */
2239 *more_strings = False;
2240 return NULL;
2243 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2244 &range_start, &range_end) == 2) {
2245 *more_strings = True;
2246 } else {
2247 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2248 &range_start) == 1) {
2249 *more_strings = False;
2250 } else {
2251 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2252 range_attr));
2253 ldap_memfree(range_attr);
2254 *more_strings = False;
2255 return NULL;
2259 if ((*num_strings) != range_start) {
2260 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2261 " - aborting range retreival\n",
2262 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2263 ldap_memfree(range_attr);
2264 *more_strings = False;
2265 return NULL;
2268 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2270 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2271 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2272 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2273 range_attr, (unsigned long int)range_end - range_start + 1,
2274 (unsigned long int)num_new_strings));
2275 ldap_memfree(range_attr);
2276 *more_strings = False;
2277 return NULL;
2280 strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2281 *num_strings + num_new_strings);
2283 if (strings == NULL) {
2284 ldap_memfree(range_attr);
2285 *more_strings = False;
2286 return NULL;
2289 if (new_strings && num_new_strings) {
2290 memcpy(&strings[*num_strings], new_strings,
2291 sizeof(*new_strings) * num_new_strings);
2294 (*num_strings) += num_new_strings;
2296 if (*more_strings) {
2297 *next_attribute = talloc_asprintf(mem_ctx,
2298 "%s;range=%d-*",
2299 field,
2300 (int)*num_strings);
2302 if (!*next_attribute) {
2303 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2304 ldap_memfree(range_attr);
2305 *more_strings = False;
2306 return NULL;
2310 ldap_memfree(range_attr);
2312 return strings;
2316 * pull a single uint32 from a ADS result
2317 * @param ads connection to ads server
2318 * @param msg Results of search
2319 * @param field Attribute to retrieve
2320 * @param v Pointer to int to store result
2321 * @return boolean inidicating success
2323 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2324 uint32 *v)
2326 char **values;
2328 values = ldap_get_values(ads->ldap.ld, msg, field);
2329 if (!values)
2330 return False;
2331 if (!values[0]) {
2332 ldap_value_free(values);
2333 return False;
2336 *v = atoi(values[0]);
2337 ldap_value_free(values);
2338 return True;
2342 * pull a single objectGUID from an ADS result
2343 * @param ads connection to ADS server
2344 * @param msg results of search
2345 * @param guid 37-byte area to receive text guid
2346 * @return boolean indicating success
2348 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2350 char **values;
2351 UUID_FLAT flat_guid;
2353 values = ldap_get_values(ads->ldap.ld, msg, "objectGUID");
2354 if (!values)
2355 return False;
2357 if (values[0]) {
2358 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2359 smb_uuid_unpack(flat_guid, guid);
2360 ldap_value_free(values);
2361 return True;
2363 ldap_value_free(values);
2364 return False;
2370 * pull a single DOM_SID from a ADS result
2371 * @param ads connection to ads server
2372 * @param msg Results of search
2373 * @param field Attribute to retrieve
2374 * @param sid Pointer to sid to store result
2375 * @return boolean inidicating success
2377 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2378 DOM_SID *sid)
2380 struct berval **values;
2381 bool ret = False;
2383 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2385 if (!values)
2386 return False;
2388 if (values[0])
2389 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2391 ldap_value_free_len(values);
2392 return ret;
2396 * pull an array of DOM_SIDs from a ADS result
2397 * @param ads connection to ads server
2398 * @param mem_ctx TALLOC_CTX for allocating sid array
2399 * @param msg Results of search
2400 * @param field Attribute to retrieve
2401 * @param sids pointer to sid array to allocate
2402 * @return the count of SIDs pulled
2404 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2405 LDAPMessage *msg, const char *field, DOM_SID **sids)
2407 struct berval **values;
2408 bool ret;
2409 int count, i;
2411 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2413 if (!values)
2414 return 0;
2416 for (i=0; values[i]; i++)
2417 /* nop */ ;
2419 if (i) {
2420 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2421 if (!(*sids)) {
2422 ldap_value_free_len(values);
2423 return 0;
2425 } else {
2426 (*sids) = NULL;
2429 count = 0;
2430 for (i=0; values[i]; i++) {
2431 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2432 if (ret) {
2433 DEBUG(10, ("pulling SID: %s\n",
2434 sid_string_dbg(&(*sids)[count])));
2435 count++;
2439 ldap_value_free_len(values);
2440 return count;
2444 * pull a SEC_DESC from a ADS result
2445 * @param ads connection to ads server
2446 * @param mem_ctx TALLOC_CTX for allocating sid array
2447 * @param msg Results of search
2448 * @param field Attribute to retrieve
2449 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2450 * @return boolean inidicating success
2452 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2453 LDAPMessage *msg, const char *field, SEC_DESC **sd)
2455 struct berval **values;
2456 bool ret = true;
2458 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2460 if (!values) return false;
2462 if (values[0]) {
2463 NTSTATUS status;
2464 status = unmarshall_sec_desc(mem_ctx,
2465 (uint8 *)values[0]->bv_val,
2466 values[0]->bv_len, sd);
2467 if (!NT_STATUS_IS_OK(status)) {
2468 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2469 nt_errstr(status)));
2470 ret = false;
2474 ldap_value_free_len(values);
2475 return ret;
2479 * in order to support usernames longer than 21 characters we need to
2480 * use both the sAMAccountName and the userPrincipalName attributes
2481 * It seems that not all users have the userPrincipalName attribute set
2483 * @param ads connection to ads server
2484 * @param mem_ctx TALLOC_CTX for allocating sid array
2485 * @param msg Results of search
2486 * @return the username
2488 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2489 LDAPMessage *msg)
2491 #if 0 /* JERRY */
2492 char *ret, *p;
2494 /* lookup_name() only works on the sAMAccountName to
2495 returning the username portion of userPrincipalName
2496 breaks winbindd_getpwnam() */
2498 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2499 if (ret && (p = strchr_m(ret, '@'))) {
2500 *p = 0;
2501 return ret;
2503 #endif
2504 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2509 * find the update serial number - this is the core of the ldap cache
2510 * @param ads connection to ads server
2511 * @param ads connection to ADS server
2512 * @param usn Pointer to retrieved update serial number
2513 * @return status of search
2515 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2517 const char *attrs[] = {"highestCommittedUSN", NULL};
2518 ADS_STATUS status;
2519 LDAPMessage *res;
2521 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2522 if (!ADS_ERR_OK(status))
2523 return status;
2525 if (ads_count_replies(ads, res) != 1) {
2526 ads_msgfree(ads, res);
2527 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2530 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2531 ads_msgfree(ads, res);
2532 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2535 ads_msgfree(ads, res);
2536 return ADS_SUCCESS;
2539 /* parse a ADS timestring - typical string is
2540 '20020917091222.0Z0' which means 09:12.22 17th September
2541 2002, timezone 0 */
2542 static time_t ads_parse_time(const char *str)
2544 struct tm tm;
2546 ZERO_STRUCT(tm);
2548 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2549 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2550 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2551 return 0;
2553 tm.tm_year -= 1900;
2554 tm.tm_mon -= 1;
2556 return timegm(&tm);
2559 /********************************************************************
2560 ********************************************************************/
2562 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2564 const char *attrs[] = {"currentTime", NULL};
2565 ADS_STATUS status;
2566 LDAPMessage *res;
2567 char *timestr;
2568 TALLOC_CTX *ctx;
2569 ADS_STRUCT *ads_s = ads;
2571 if (!(ctx = talloc_init("ads_current_time"))) {
2572 return ADS_ERROR(LDAP_NO_MEMORY);
2575 /* establish a new ldap tcp session if necessary */
2577 if ( !ads->ldap.ld ) {
2578 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2579 ads->server.ldap_server )) == NULL )
2581 goto done;
2583 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2584 status = ads_connect( ads_s );
2585 if ( !ADS_ERR_OK(status))
2586 goto done;
2589 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2590 if (!ADS_ERR_OK(status)) {
2591 goto done;
2594 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2595 if (!timestr) {
2596 ads_msgfree(ads_s, res);
2597 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2598 goto done;
2601 /* but save the time and offset in the original ADS_STRUCT */
2603 ads->config.current_time = ads_parse_time(timestr);
2605 if (ads->config.current_time != 0) {
2606 ads->auth.time_offset = ads->config.current_time - time(NULL);
2607 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2610 ads_msgfree(ads, res);
2612 status = ADS_SUCCESS;
2614 done:
2615 /* free any temporary ads connections */
2616 if ( ads_s != ads ) {
2617 ads_destroy( &ads_s );
2619 talloc_destroy(ctx);
2621 return status;
2624 /********************************************************************
2625 ********************************************************************/
2627 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2629 const char *attrs[] = {"domainFunctionality", NULL};
2630 ADS_STATUS status;
2631 LDAPMessage *res;
2632 ADS_STRUCT *ads_s = ads;
2634 *val = DS_DOMAIN_FUNCTION_2000;
2636 /* establish a new ldap tcp session if necessary */
2638 if ( !ads->ldap.ld ) {
2639 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2640 ads->server.ldap_server )) == NULL )
2642 goto done;
2644 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2645 status = ads_connect( ads_s );
2646 if ( !ADS_ERR_OK(status))
2647 goto done;
2650 /* If the attribute does not exist assume it is a Windows 2000
2651 functional domain */
2653 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2654 if (!ADS_ERR_OK(status)) {
2655 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2656 status = ADS_SUCCESS;
2658 goto done;
2661 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2662 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2664 DEBUG(3,("ads_domain_func_level: %d\n", *val));
2667 ads_msgfree(ads, res);
2669 done:
2670 /* free any temporary ads connections */
2671 if ( ads_s != ads ) {
2672 ads_destroy( &ads_s );
2675 return status;
2679 * find the domain sid for our domain
2680 * @param ads connection to ads server
2681 * @param sid Pointer to domain sid
2682 * @return status of search
2684 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2686 const char *attrs[] = {"objectSid", NULL};
2687 LDAPMessage *res;
2688 ADS_STATUS rc;
2690 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2691 attrs, &res);
2692 if (!ADS_ERR_OK(rc)) return rc;
2693 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2694 ads_msgfree(ads, res);
2695 return ADS_ERROR_SYSTEM(ENOENT);
2697 ads_msgfree(ads, res);
2699 return ADS_SUCCESS;
2703 * find our site name
2704 * @param ads connection to ads server
2705 * @param mem_ctx Pointer to talloc context
2706 * @param site_name Pointer to the sitename
2707 * @return status of search
2709 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2711 ADS_STATUS status;
2712 LDAPMessage *res;
2713 const char *dn, *service_name;
2714 const char *attrs[] = { "dsServiceName", NULL };
2716 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2717 if (!ADS_ERR_OK(status)) {
2718 return status;
2721 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2722 if (service_name == NULL) {
2723 ads_msgfree(ads, res);
2724 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2727 ads_msgfree(ads, res);
2729 /* go up three levels */
2730 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2731 if (dn == NULL) {
2732 return ADS_ERROR(LDAP_NO_MEMORY);
2735 *site_name = talloc_strdup(mem_ctx, dn);
2736 if (*site_name == NULL) {
2737 return ADS_ERROR(LDAP_NO_MEMORY);
2740 return status;
2742 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2747 * find the site dn where a machine resides
2748 * @param ads connection to ads server
2749 * @param mem_ctx Pointer to talloc context
2750 * @param computer_name name of the machine
2751 * @param site_name Pointer to the sitename
2752 * @return status of search
2754 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
2756 ADS_STATUS status;
2757 LDAPMessage *res;
2758 const char *parent, *filter;
2759 char *config_context = NULL;
2760 char *dn;
2762 /* shortcut a query */
2763 if (strequal(computer_name, ads->config.ldap_server_name)) {
2764 return ads_site_dn(ads, mem_ctx, site_dn);
2767 status = ads_config_path(ads, mem_ctx, &config_context);
2768 if (!ADS_ERR_OK(status)) {
2769 return status;
2772 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
2773 if (filter == NULL) {
2774 return ADS_ERROR(LDAP_NO_MEMORY);
2777 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
2778 filter, NULL, &res);
2779 if (!ADS_ERR_OK(status)) {
2780 return status;
2783 if (ads_count_replies(ads, res) != 1) {
2784 ads_msgfree(ads, res);
2785 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2788 dn = ads_get_dn(ads, res);
2789 if (dn == NULL) {
2790 ads_msgfree(ads, res);
2791 return ADS_ERROR(LDAP_NO_MEMORY);
2794 /* go up three levels */
2795 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
2796 if (parent == NULL) {
2797 ads_msgfree(ads, res);
2798 ads_memfree(ads, dn);
2799 return ADS_ERROR(LDAP_NO_MEMORY);
2802 *site_dn = talloc_strdup(mem_ctx, parent);
2803 if (*site_dn == NULL) {
2804 ads_msgfree(ads, res);
2805 ads_memfree(ads, dn);
2806 return ADS_ERROR(LDAP_NO_MEMORY);
2809 ads_memfree(ads, dn);
2810 ads_msgfree(ads, res);
2812 return status;
2816 * get the upn suffixes for a domain
2817 * @param ads connection to ads server
2818 * @param mem_ctx Pointer to talloc context
2819 * @param suffixes Pointer to an array of suffixes
2820 * @param num_suffixes Pointer to the number of suffixes
2821 * @return status of search
2823 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
2825 ADS_STATUS status;
2826 LDAPMessage *res;
2827 const char *base;
2828 char *config_context = NULL;
2829 const char *attrs[] = { "uPNSuffixes", NULL };
2831 status = ads_config_path(ads, mem_ctx, &config_context);
2832 if (!ADS_ERR_OK(status)) {
2833 return status;
2836 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
2837 if (base == NULL) {
2838 return ADS_ERROR(LDAP_NO_MEMORY);
2841 status = ads_search_dn(ads, &res, base, attrs);
2842 if (!ADS_ERR_OK(status)) {
2843 return status;
2846 if (ads_count_replies(ads, res) != 1) {
2847 ads_msgfree(ads, res);
2848 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2851 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
2852 if ((*suffixes) == NULL) {
2853 ads_msgfree(ads, res);
2854 return ADS_ERROR(LDAP_NO_MEMORY);
2857 ads_msgfree(ads, res);
2859 return status;
2863 * get the joinable ous for a domain
2864 * @param ads connection to ads server
2865 * @param mem_ctx Pointer to talloc context
2866 * @param ous Pointer to an array of ous
2867 * @param num_ous Pointer to the number of ous
2868 * @return status of search
2870 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
2871 TALLOC_CTX *mem_ctx,
2872 char ***ous,
2873 size_t *num_ous)
2875 ADS_STATUS status;
2876 LDAPMessage *res = NULL;
2877 LDAPMessage *msg = NULL;
2878 const char *attrs[] = { "dn", NULL };
2879 int count = 0;
2881 status = ads_search(ads, &res,
2882 "(|(objectClass=domain)(objectclass=organizationalUnit))",
2883 attrs);
2884 if (!ADS_ERR_OK(status)) {
2885 return status;
2888 count = ads_count_replies(ads, res);
2889 if (count < 1) {
2890 ads_msgfree(ads, res);
2891 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2894 for (msg = ads_first_entry(ads, res); msg;
2895 msg = ads_next_entry(ads, msg)) {
2897 char *dn = NULL;
2899 dn = ads_get_dn(ads, msg);
2900 if (!dn) {
2901 ads_msgfree(ads, res);
2902 return ADS_ERROR(LDAP_NO_MEMORY);
2905 if (!add_string_to_array(mem_ctx, dn,
2906 (const char ***)ous,
2907 (int *)num_ous)) {
2908 ads_memfree(ads, dn);
2909 ads_msgfree(ads, res);
2910 return ADS_ERROR(LDAP_NO_MEMORY);
2913 ads_memfree(ads, dn);
2916 ads_msgfree(ads, res);
2918 return status;
2923 * pull a DOM_SID from an extended dn string
2924 * @param mem_ctx TALLOC_CTX
2925 * @param extended_dn string
2926 * @param flags string type of extended_dn
2927 * @param sid pointer to a DOM_SID
2928 * @return boolean inidicating success
2930 bool ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
2931 const char *extended_dn,
2932 enum ads_extended_dn_flags flags,
2933 DOM_SID *sid)
2935 char *p, *q, *dn;
2937 if (!extended_dn) {
2938 return False;
2941 /* otherwise extended_dn gets stripped off */
2942 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
2943 return False;
2946 * ADS_EXTENDED_DN_HEX_STRING:
2947 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
2949 * ADS_EXTENDED_DN_STRING (only with w2k3):
2950 <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
2953 p = strchr(dn, ';');
2954 if (!p) {
2955 return False;
2958 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
2959 return False;
2962 p += strlen(";<SID=");
2964 q = strchr(p, '>');
2965 if (!q) {
2966 return False;
2969 *q = '\0';
2971 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
2973 switch (flags) {
2975 case ADS_EXTENDED_DN_STRING:
2976 if (!string_to_sid(sid, p)) {
2977 return False;
2979 break;
2980 case ADS_EXTENDED_DN_HEX_STRING: {
2981 fstring buf;
2982 size_t buf_len;
2984 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
2985 if (buf_len == 0) {
2986 return False;
2989 if (!sid_parse(buf, buf_len, sid)) {
2990 DEBUG(10,("failed to parse sid\n"));
2991 return False;
2993 break;
2995 default:
2996 DEBUG(10,("unknown extended dn format\n"));
2997 return False;
3000 return True;
3004 * pull an array of DOM_SIDs from a ADS result
3005 * @param ads connection to ads server
3006 * @param mem_ctx TALLOC_CTX for allocating sid array
3007 * @param msg Results of search
3008 * @param field Attribute to retrieve
3009 * @param flags string type of extended_dn
3010 * @param sids pointer to sid array to allocate
3011 * @return the count of SIDs pulled
3013 int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads,
3014 TALLOC_CTX *mem_ctx,
3015 LDAPMessage *msg,
3016 const char *field,
3017 enum ads_extended_dn_flags flags,
3018 DOM_SID **sids)
3020 int i;
3021 size_t dn_count;
3022 char **dn_strings;
3024 if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field,
3025 &dn_count)) == NULL) {
3026 return 0;
3029 (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, dn_count + 1);
3030 if (!(*sids)) {
3031 TALLOC_FREE(dn_strings);
3032 return 0;
3035 for (i=0; i<dn_count; i++) {
3037 if (!ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i],
3038 flags, &(*sids)[i])) {
3039 TALLOC_FREE(*sids);
3040 TALLOC_FREE(dn_strings);
3041 return 0;
3045 TALLOC_FREE(dn_strings);
3047 return dn_count;
3050 /********************************************************************
3051 ********************************************************************/
3053 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3055 LDAPMessage *res = NULL;
3056 ADS_STATUS status;
3057 int count = 0;
3058 char *name = NULL;
3060 status = ads_find_machine_acct(ads, &res, global_myname());
3061 if (!ADS_ERR_OK(status)) {
3062 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3063 global_myname()));
3064 goto out;
3067 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3068 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3069 goto out;
3072 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3073 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3076 out:
3077 ads_msgfree(ads, res);
3079 return name;
3082 /********************************************************************
3083 ********************************************************************/
3085 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3087 LDAPMessage *res = NULL;
3088 ADS_STATUS status;
3089 int count = 0;
3090 char *name = NULL;
3092 status = ads_find_machine_acct(ads, &res, machine_name);
3093 if (!ADS_ERR_OK(status)) {
3094 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3095 global_myname()));
3096 goto out;
3099 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3100 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3101 goto out;
3104 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3105 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3108 out:
3109 ads_msgfree(ads, res);
3111 return name;
3114 /********************************************************************
3115 ********************************************************************/
3117 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3119 LDAPMessage *res = NULL;
3120 ADS_STATUS status;
3121 int count = 0;
3122 char *name = NULL;
3124 status = ads_find_machine_acct(ads, &res, global_myname());
3125 if (!ADS_ERR_OK(status)) {
3126 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3127 global_myname()));
3128 goto out;
3131 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3132 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3133 goto out;
3136 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3137 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3140 out:
3141 ads_msgfree(ads, res);
3143 return name;
3146 #if 0
3148 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3151 * Join a machine to a realm
3152 * Creates the machine account and sets the machine password
3153 * @param ads connection to ads server
3154 * @param machine name of host to add
3155 * @param org_unit Organizational unit to place machine in
3156 * @return status of join
3158 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3159 uint32 account_type, const char *org_unit)
3161 ADS_STATUS status;
3162 LDAPMessage *res = NULL;
3163 char *machine;
3165 /* machine name must be lowercase */
3166 machine = SMB_STRDUP(machine_name);
3167 strlower_m(machine);
3170 status = ads_find_machine_acct(ads, (void **)&res, machine);
3171 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3172 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3173 status = ads_leave_realm(ads, machine);
3174 if (!ADS_ERR_OK(status)) {
3175 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3176 machine, ads->config.realm));
3177 return status;
3181 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3182 if (!ADS_ERR_OK(status)) {
3183 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3184 SAFE_FREE(machine);
3185 return status;
3188 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3189 if (!ADS_ERR_OK(status)) {
3190 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3191 SAFE_FREE(machine);
3192 return status;
3195 SAFE_FREE(machine);
3196 ads_msgfree(ads, res);
3198 return status;
3200 #endif
3203 * Delete a machine from the realm
3204 * @param ads connection to ads server
3205 * @param hostname Machine to remove
3206 * @return status of delete
3208 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3210 ADS_STATUS status;
3211 void *msg;
3212 LDAPMessage *res;
3213 char *hostnameDN, *host;
3214 int rc;
3215 LDAPControl ldap_control;
3216 LDAPControl * pldap_control[2] = {NULL, NULL};
3218 pldap_control[0] = &ldap_control;
3219 memset(&ldap_control, 0, sizeof(LDAPControl));
3220 ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
3222 /* hostname must be lowercase */
3223 host = SMB_STRDUP(hostname);
3224 strlower_m(host);
3226 status = ads_find_machine_acct(ads, &res, host);
3227 if (!ADS_ERR_OK(status)) {
3228 DEBUG(0, ("Host account for %s does not exist.\n", host));
3229 SAFE_FREE(host);
3230 return status;
3233 msg = ads_first_entry(ads, res);
3234 if (!msg) {
3235 SAFE_FREE(host);
3236 return ADS_ERROR_SYSTEM(ENOENT);
3239 hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
3241 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3242 if (rc) {
3243 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3244 }else {
3245 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3248 if (rc != LDAP_SUCCESS) {
3249 const char *attrs[] = { "cn", NULL };
3250 LDAPMessage *msg_sub;
3252 /* we only search with scope ONE, we do not expect any further
3253 * objects to be created deeper */
3255 status = ads_do_search_retry(ads, hostnameDN,
3256 LDAP_SCOPE_ONELEVEL,
3257 "(objectclass=*)", attrs, &res);
3259 if (!ADS_ERR_OK(status)) {
3260 SAFE_FREE(host);
3261 ads_memfree(ads, hostnameDN);
3262 return status;
3265 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3266 msg_sub = ads_next_entry(ads, msg_sub)) {
3268 char *dn = NULL;
3270 if ((dn = ads_get_dn(ads, msg_sub)) == NULL) {
3271 SAFE_FREE(host);
3272 ads_memfree(ads, hostnameDN);
3273 return ADS_ERROR(LDAP_NO_MEMORY);
3276 status = ads_del_dn(ads, dn);
3277 if (!ADS_ERR_OK(status)) {
3278 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3279 SAFE_FREE(host);
3280 ads_memfree(ads, dn);
3281 ads_memfree(ads, hostnameDN);
3282 return status;
3285 ads_memfree(ads, dn);
3288 /* there should be no subordinate objects anymore */
3289 status = ads_do_search_retry(ads, hostnameDN,
3290 LDAP_SCOPE_ONELEVEL,
3291 "(objectclass=*)", attrs, &res);
3293 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3294 SAFE_FREE(host);
3295 ads_memfree(ads, hostnameDN);
3296 return status;
3299 /* delete hostnameDN now */
3300 status = ads_del_dn(ads, hostnameDN);
3301 if (!ADS_ERR_OK(status)) {
3302 SAFE_FREE(host);
3303 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3304 ads_memfree(ads, hostnameDN);
3305 return status;
3309 ads_memfree(ads, hostnameDN);
3311 status = ads_find_machine_acct(ads, &res, host);
3312 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3313 DEBUG(3, ("Failed to remove host account.\n"));
3314 SAFE_FREE(host);
3315 return status;
3318 SAFE_FREE(host);
3319 return status;
3323 * pull all token-sids from an LDAP dn
3324 * @param ads connection to ads server
3325 * @param mem_ctx TALLOC_CTX for allocating sid array
3326 * @param dn of LDAP object
3327 * @param user_sid pointer to DOM_SID (objectSid)
3328 * @param primary_group_sid pointer to DOM_SID (self composed)
3329 * @param sids pointer to sid array to allocate
3330 * @param num_sids counter of SIDs pulled
3331 * @return status of token query
3333 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3334 TALLOC_CTX *mem_ctx,
3335 const char *dn,
3336 DOM_SID *user_sid,
3337 DOM_SID *primary_group_sid,
3338 DOM_SID **sids,
3339 size_t *num_sids)
3341 ADS_STATUS status;
3342 LDAPMessage *res = NULL;
3343 int count = 0;
3344 size_t tmp_num_sids;
3345 DOM_SID *tmp_sids;
3346 DOM_SID tmp_user_sid;
3347 DOM_SID tmp_primary_group_sid;
3348 uint32 pgid;
3349 const char *attrs[] = {
3350 "objectSid",
3351 "tokenGroups",
3352 "primaryGroupID",
3353 NULL
3356 status = ads_search_retry_dn(ads, &res, dn, attrs);
3357 if (!ADS_ERR_OK(status)) {
3358 return status;
3361 count = ads_count_replies(ads, res);
3362 if (count != 1) {
3363 ads_msgfree(ads, res);
3364 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3367 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3368 ads_msgfree(ads, res);
3369 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3372 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3373 ads_msgfree(ads, res);
3374 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3378 /* hack to compose the primary group sid without knowing the
3379 * domsid */
3381 DOM_SID domsid;
3382 uint32 dummy_rid;
3384 sid_copy(&domsid, &tmp_user_sid);
3386 if (!sid_split_rid(&domsid, &dummy_rid)) {
3387 ads_msgfree(ads, res);
3388 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3391 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3392 ads_msgfree(ads, res);
3393 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3397 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3399 if (tmp_num_sids == 0 || !tmp_sids) {
3400 ads_msgfree(ads, res);
3401 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3404 if (num_sids) {
3405 *num_sids = tmp_num_sids;
3408 if (sids) {
3409 *sids = tmp_sids;
3412 if (user_sid) {
3413 *user_sid = tmp_user_sid;
3416 if (primary_group_sid) {
3417 *primary_group_sid = tmp_primary_group_sid;
3420 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3422 ads_msgfree(ads, res);
3423 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3427 * Find a sAMAccoutName in LDAP
3428 * @param ads connection to ads server
3429 * @param mem_ctx TALLOC_CTX for allocating sid array
3430 * @param samaccountname to search
3431 * @param uac_ret uint32 pointer userAccountControl attribute value
3432 * @param dn_ret pointer to dn
3433 * @return status of token query
3435 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3436 TALLOC_CTX *mem_ctx,
3437 const char *samaccountname,
3438 uint32 *uac_ret,
3439 const char **dn_ret)
3441 ADS_STATUS status;
3442 const char *attrs[] = { "userAccountControl", NULL };
3443 const char *filter;
3444 LDAPMessage *res = NULL;
3445 char *dn = NULL;
3446 uint32 uac = 0;
3448 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3449 samaccountname);
3450 if (filter == NULL) {
3451 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3452 goto out;
3455 status = ads_do_search_all(ads, ads->config.bind_path,
3456 LDAP_SCOPE_SUBTREE,
3457 filter, attrs, &res);
3459 if (!ADS_ERR_OK(status)) {
3460 goto out;
3463 if (ads_count_replies(ads, res) != 1) {
3464 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3465 goto out;
3468 dn = ads_get_dn(ads, res);
3469 if (dn == NULL) {
3470 status = ADS_ERROR(LDAP_NO_MEMORY);
3471 goto out;
3474 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3475 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3476 goto out;
3479 if (uac_ret) {
3480 *uac_ret = uac;
3483 if (dn_ret) {
3484 *dn_ret = talloc_strdup(mem_ctx, dn);
3485 if (!*dn_ret) {
3486 status = ADS_ERROR(LDAP_NO_MEMORY);
3487 goto out;
3490 out:
3491 ads_memfree(ads, dn);
3492 ads_msgfree(ads, res);
3494 return status;
3498 * find our configuration path
3499 * @param ads connection to ads server
3500 * @param mem_ctx Pointer to talloc context
3501 * @param config_path Pointer to the config path
3502 * @return status of search
3504 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3505 TALLOC_CTX *mem_ctx,
3506 char **config_path)
3508 ADS_STATUS status;
3509 LDAPMessage *res = NULL;
3510 const char *config_context = NULL;
3511 const char *attrs[] = { "configurationNamingContext", NULL };
3513 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3514 "(objectclass=*)", attrs, &res);
3515 if (!ADS_ERR_OK(status)) {
3516 return status;
3519 config_context = ads_pull_string(ads, mem_ctx, res,
3520 "configurationNamingContext");
3521 ads_msgfree(ads, res);
3522 if (!config_context) {
3523 return ADS_ERROR(LDAP_NO_MEMORY);
3526 if (config_path) {
3527 *config_path = talloc_strdup(mem_ctx, config_context);
3528 if (!*config_path) {
3529 return ADS_ERROR(LDAP_NO_MEMORY);
3533 return ADS_ERROR(LDAP_SUCCESS);
3537 * find the displayName of an extended right
3538 * @param ads connection to ads server
3539 * @param config_path The config path
3540 * @param mem_ctx Pointer to talloc context
3541 * @param GUID struct of the rightsGUID
3542 * @return status of search
3544 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3545 const char *config_path,
3546 TALLOC_CTX *mem_ctx,
3547 const struct GUID *rights_guid)
3549 ADS_STATUS rc;
3550 LDAPMessage *res = NULL;
3551 char *expr = NULL;
3552 const char *attrs[] = { "displayName", NULL };
3553 const char *result = NULL;
3554 const char *path;
3556 if (!ads || !mem_ctx || !rights_guid) {
3557 goto done;
3560 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3561 smb_uuid_string(mem_ctx, *rights_guid));
3562 if (!expr) {
3563 goto done;
3566 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3567 if (!path) {
3568 goto done;
3571 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3572 expr, attrs, &res);
3573 if (!ADS_ERR_OK(rc)) {
3574 goto done;
3577 if (ads_count_replies(ads, res) != 1) {
3578 goto done;
3581 result = ads_pull_string(ads, mem_ctx, res, "displayName");
3583 done:
3584 ads_msgfree(ads, res);
3585 return result;
3590 * verify or build and verify an account ou
3591 * @param mem_ctx Pointer to talloc context
3592 * @param ads connection to ads server
3593 * @param account_ou
3594 * @return status of search
3597 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3598 ADS_STRUCT *ads,
3599 const char **account_ou)
3601 struct ldb_dn *name_dn = NULL;
3602 const char *name = NULL;
3603 char *ou_string = NULL;
3605 name_dn = ldb_dn_explode(mem_ctx, *account_ou);
3606 if (name_dn) {
3607 return ADS_SUCCESS;
3610 ou_string = ads_ou_string(ads, *account_ou);
3611 if (!ou_string) {
3612 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3615 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3616 ads->config.bind_path);
3617 SAFE_FREE(ou_string);
3618 if (!name) {
3619 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3622 name_dn = ldb_dn_explode(mem_ctx, name);
3623 if (!name_dn) {
3624 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3627 *account_ou = talloc_strdup(mem_ctx, name);
3628 if (!*account_ou) {
3629 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3632 return ADS_SUCCESS;
3635 #endif