Fix extended DN parse error when AD object does not have a SID.
[Samba.git] / source / libads / ldap.c
blobfa27415b3b1158c61c7b2bc471e17bd572a17de4
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 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2643 goto done;
2645 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2646 status = ads_connect( ads_s );
2647 if ( !ADS_ERR_OK(status))
2648 goto done;
2651 /* If the attribute does not exist assume it is a Windows 2000
2652 functional domain */
2654 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2655 if (!ADS_ERR_OK(status)) {
2656 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2657 status = ADS_SUCCESS;
2659 goto done;
2662 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2663 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2665 DEBUG(3,("ads_domain_func_level: %d\n", *val));
2668 ads_msgfree(ads, res);
2670 done:
2671 /* free any temporary ads connections */
2672 if ( ads_s != ads ) {
2673 ads_destroy( &ads_s );
2676 return status;
2680 * find the domain sid for our domain
2681 * @param ads connection to ads server
2682 * @param sid Pointer to domain sid
2683 * @return status of search
2685 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2687 const char *attrs[] = {"objectSid", NULL};
2688 LDAPMessage *res;
2689 ADS_STATUS rc;
2691 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2692 attrs, &res);
2693 if (!ADS_ERR_OK(rc)) return rc;
2694 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2695 ads_msgfree(ads, res);
2696 return ADS_ERROR_SYSTEM(ENOENT);
2698 ads_msgfree(ads, res);
2700 return ADS_SUCCESS;
2704 * find our site name
2705 * @param ads connection to ads server
2706 * @param mem_ctx Pointer to talloc context
2707 * @param site_name Pointer to the sitename
2708 * @return status of search
2710 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2712 ADS_STATUS status;
2713 LDAPMessage *res;
2714 const char *dn, *service_name;
2715 const char *attrs[] = { "dsServiceName", NULL };
2717 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2718 if (!ADS_ERR_OK(status)) {
2719 return status;
2722 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2723 if (service_name == NULL) {
2724 ads_msgfree(ads, res);
2725 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2728 ads_msgfree(ads, res);
2730 /* go up three levels */
2731 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2732 if (dn == NULL) {
2733 return ADS_ERROR(LDAP_NO_MEMORY);
2736 *site_name = talloc_strdup(mem_ctx, dn);
2737 if (*site_name == NULL) {
2738 return ADS_ERROR(LDAP_NO_MEMORY);
2741 return status;
2743 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2748 * find the site dn where a machine resides
2749 * @param ads connection to ads server
2750 * @param mem_ctx Pointer to talloc context
2751 * @param computer_name name of the machine
2752 * @param site_name Pointer to the sitename
2753 * @return status of search
2755 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
2757 ADS_STATUS status;
2758 LDAPMessage *res;
2759 const char *parent, *filter;
2760 char *config_context = NULL;
2761 char *dn;
2763 /* shortcut a query */
2764 if (strequal(computer_name, ads->config.ldap_server_name)) {
2765 return ads_site_dn(ads, mem_ctx, site_dn);
2768 status = ads_config_path(ads, mem_ctx, &config_context);
2769 if (!ADS_ERR_OK(status)) {
2770 return status;
2773 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
2774 if (filter == NULL) {
2775 return ADS_ERROR(LDAP_NO_MEMORY);
2778 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
2779 filter, NULL, &res);
2780 if (!ADS_ERR_OK(status)) {
2781 return status;
2784 if (ads_count_replies(ads, res) != 1) {
2785 ads_msgfree(ads, res);
2786 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2789 dn = ads_get_dn(ads, res);
2790 if (dn == NULL) {
2791 ads_msgfree(ads, res);
2792 return ADS_ERROR(LDAP_NO_MEMORY);
2795 /* go up three levels */
2796 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
2797 if (parent == NULL) {
2798 ads_msgfree(ads, res);
2799 ads_memfree(ads, dn);
2800 return ADS_ERROR(LDAP_NO_MEMORY);
2803 *site_dn = talloc_strdup(mem_ctx, parent);
2804 if (*site_dn == NULL) {
2805 ads_msgfree(ads, res);
2806 ads_memfree(ads, dn);
2807 return ADS_ERROR(LDAP_NO_MEMORY);
2810 ads_memfree(ads, dn);
2811 ads_msgfree(ads, res);
2813 return status;
2817 * get the upn suffixes for a domain
2818 * @param ads connection to ads server
2819 * @param mem_ctx Pointer to talloc context
2820 * @param suffixes Pointer to an array of suffixes
2821 * @param num_suffixes Pointer to the number of suffixes
2822 * @return status of search
2824 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
2826 ADS_STATUS status;
2827 LDAPMessage *res;
2828 const char *base;
2829 char *config_context = NULL;
2830 const char *attrs[] = { "uPNSuffixes", NULL };
2832 status = ads_config_path(ads, mem_ctx, &config_context);
2833 if (!ADS_ERR_OK(status)) {
2834 return status;
2837 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
2838 if (base == NULL) {
2839 return ADS_ERROR(LDAP_NO_MEMORY);
2842 status = ads_search_dn(ads, &res, base, attrs);
2843 if (!ADS_ERR_OK(status)) {
2844 return status;
2847 if (ads_count_replies(ads, res) != 1) {
2848 ads_msgfree(ads, res);
2849 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2852 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
2853 if ((*suffixes) == NULL) {
2854 ads_msgfree(ads, res);
2855 return ADS_ERROR(LDAP_NO_MEMORY);
2858 ads_msgfree(ads, res);
2860 return status;
2864 * get the joinable ous for a domain
2865 * @param ads connection to ads server
2866 * @param mem_ctx Pointer to talloc context
2867 * @param ous Pointer to an array of ous
2868 * @param num_ous Pointer to the number of ous
2869 * @return status of search
2871 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
2872 TALLOC_CTX *mem_ctx,
2873 char ***ous,
2874 size_t *num_ous)
2876 ADS_STATUS status;
2877 LDAPMessage *res = NULL;
2878 LDAPMessage *msg = NULL;
2879 const char *attrs[] = { "dn", NULL };
2880 int count = 0;
2882 status = ads_search(ads, &res,
2883 "(|(objectClass=domain)(objectclass=organizationalUnit))",
2884 attrs);
2885 if (!ADS_ERR_OK(status)) {
2886 return status;
2889 count = ads_count_replies(ads, res);
2890 if (count < 1) {
2891 ads_msgfree(ads, res);
2892 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2895 for (msg = ads_first_entry(ads, res); msg;
2896 msg = ads_next_entry(ads, msg)) {
2898 char *dn = NULL;
2900 dn = ads_get_dn(ads, msg);
2901 if (!dn) {
2902 ads_msgfree(ads, res);
2903 return ADS_ERROR(LDAP_NO_MEMORY);
2906 if (!add_string_to_array(mem_ctx, dn,
2907 (const char ***)ous,
2908 (int *)num_ous)) {
2909 ads_memfree(ads, dn);
2910 ads_msgfree(ads, res);
2911 return ADS_ERROR(LDAP_NO_MEMORY);
2914 ads_memfree(ads, dn);
2917 ads_msgfree(ads, res);
2919 return status;
2924 * pull a DOM_SID from an extended dn string
2925 * @param mem_ctx TALLOC_CTX
2926 * @param extended_dn string
2927 * @param flags string type of extended_dn
2928 * @param sid pointer to a DOM_SID
2929 * @return NT_STATUS_OK on success,
2930 * NT_INVALID_PARAMETER on error,
2931 * NT_STATUS_NOT_FOUND if no SID present
2933 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
2934 const char *extended_dn,
2935 enum ads_extended_dn_flags flags,
2936 DOM_SID *sid)
2938 char *p, *q, *dn;
2940 if (!extended_dn) {
2941 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
2944 /* otherwise extended_dn gets stripped off */
2945 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
2946 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
2949 * ADS_EXTENDED_DN_HEX_STRING:
2950 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
2952 * ADS_EXTENDED_DN_STRING (only with w2k3):
2953 * <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
2955 * Object with no SID, such as an Exchange Public Folder
2956 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
2959 p = strchr(dn, ';');
2960 if (!p) {
2961 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
2964 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
2965 DEBUG(5,("No SID present in extended dn\n"));
2966 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
2969 p += strlen(";<SID=");
2971 q = strchr(p, '>');
2972 if (!q) {
2973 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
2976 *q = '\0';
2978 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
2980 switch (flags) {
2982 case ADS_EXTENDED_DN_STRING:
2983 if (!string_to_sid(sid, p)) {
2984 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
2986 break;
2987 case ADS_EXTENDED_DN_HEX_STRING: {
2988 fstring buf;
2989 size_t buf_len;
2991 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
2992 if (buf_len == 0) {
2993 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
2996 if (!sid_parse(buf, buf_len, sid)) {
2997 DEBUG(10,("failed to parse sid\n"));
2998 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3000 break;
3002 default:
3003 DEBUG(10,("unknown extended dn format\n"));
3004 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3007 return ADS_ERROR_NT(NT_STATUS_OK);
3011 * pull an array of DOM_SIDs from a ADS result
3012 * @param ads connection to ads server
3013 * @param mem_ctx TALLOC_CTX for allocating sid array
3014 * @param msg Results of search
3015 * @param field Attribute to retrieve
3016 * @param flags string type of extended_dn
3017 * @param sids pointer to sid array to allocate
3018 * @return the count of SIDs pulled
3020 int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads,
3021 TALLOC_CTX *mem_ctx,
3022 LDAPMessage *msg,
3023 const char *field,
3024 enum ads_extended_dn_flags flags,
3025 DOM_SID **sids)
3027 int i;
3028 ADS_STATUS rc;
3029 size_t dn_count, ret_count = 0;
3030 char **dn_strings;
3032 if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field,
3033 &dn_count)) == NULL) {
3034 return 0;
3037 (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, dn_count + 1);
3038 if (!(*sids)) {
3039 TALLOC_FREE(dn_strings);
3040 return 0;
3043 for (i=0; i<dn_count; i++) {
3044 rc = ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i],
3045 flags, &(*sids)[i]);
3046 if (!ADS_ERR_OK(rc)) {
3047 if (NT_STATUS_EQUAL(ads_ntstatus(rc),
3048 NT_STATUS_NOT_FOUND)) {
3049 continue;
3051 else {
3052 TALLOC_FREE(*sids);
3053 TALLOC_FREE(dn_strings);
3054 return 0;
3057 ret_count++;
3060 TALLOC_FREE(dn_strings);
3062 return ret_count;
3065 /********************************************************************
3066 ********************************************************************/
3068 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3070 LDAPMessage *res = NULL;
3071 ADS_STATUS status;
3072 int count = 0;
3073 char *name = NULL;
3075 status = ads_find_machine_acct(ads, &res, global_myname());
3076 if (!ADS_ERR_OK(status)) {
3077 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3078 global_myname()));
3079 goto out;
3082 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3083 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3084 goto out;
3087 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3088 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3091 out:
3092 ads_msgfree(ads, res);
3094 return name;
3097 /********************************************************************
3098 ********************************************************************/
3100 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3102 LDAPMessage *res = NULL;
3103 ADS_STATUS status;
3104 int count = 0;
3105 char *name = NULL;
3107 status = ads_find_machine_acct(ads, &res, machine_name);
3108 if (!ADS_ERR_OK(status)) {
3109 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3110 global_myname()));
3111 goto out;
3114 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3115 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3116 goto out;
3119 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3120 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3123 out:
3124 ads_msgfree(ads, res);
3126 return name;
3129 /********************************************************************
3130 ********************************************************************/
3132 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3134 LDAPMessage *res = NULL;
3135 ADS_STATUS status;
3136 int count = 0;
3137 char *name = NULL;
3139 status = ads_find_machine_acct(ads, &res, global_myname());
3140 if (!ADS_ERR_OK(status)) {
3141 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3142 global_myname()));
3143 goto out;
3146 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3147 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3148 goto out;
3151 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3152 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3155 out:
3156 ads_msgfree(ads, res);
3158 return name;
3161 #if 0
3163 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3166 * Join a machine to a realm
3167 * Creates the machine account and sets the machine password
3168 * @param ads connection to ads server
3169 * @param machine name of host to add
3170 * @param org_unit Organizational unit to place machine in
3171 * @return status of join
3173 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3174 uint32 account_type, const char *org_unit)
3176 ADS_STATUS status;
3177 LDAPMessage *res = NULL;
3178 char *machine;
3180 /* machine name must be lowercase */
3181 machine = SMB_STRDUP(machine_name);
3182 strlower_m(machine);
3185 status = ads_find_machine_acct(ads, (void **)&res, machine);
3186 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3187 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3188 status = ads_leave_realm(ads, machine);
3189 if (!ADS_ERR_OK(status)) {
3190 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3191 machine, ads->config.realm));
3192 return status;
3196 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3197 if (!ADS_ERR_OK(status)) {
3198 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3199 SAFE_FREE(machine);
3200 return status;
3203 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3204 if (!ADS_ERR_OK(status)) {
3205 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3206 SAFE_FREE(machine);
3207 return status;
3210 SAFE_FREE(machine);
3211 ads_msgfree(ads, res);
3213 return status;
3215 #endif
3218 * Delete a machine from the realm
3219 * @param ads connection to ads server
3220 * @param hostname Machine to remove
3221 * @return status of delete
3223 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3225 ADS_STATUS status;
3226 void *msg;
3227 LDAPMessage *res;
3228 char *hostnameDN, *host;
3229 int rc;
3230 LDAPControl ldap_control;
3231 LDAPControl * pldap_control[2] = {NULL, NULL};
3233 pldap_control[0] = &ldap_control;
3234 memset(&ldap_control, 0, sizeof(LDAPControl));
3235 ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
3237 /* hostname must be lowercase */
3238 host = SMB_STRDUP(hostname);
3239 strlower_m(host);
3241 status = ads_find_machine_acct(ads, &res, host);
3242 if (!ADS_ERR_OK(status)) {
3243 DEBUG(0, ("Host account for %s does not exist.\n", host));
3244 SAFE_FREE(host);
3245 return status;
3248 msg = ads_first_entry(ads, res);
3249 if (!msg) {
3250 SAFE_FREE(host);
3251 return ADS_ERROR_SYSTEM(ENOENT);
3254 hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
3256 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3257 if (rc) {
3258 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3259 }else {
3260 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3263 if (rc != LDAP_SUCCESS) {
3264 const char *attrs[] = { "cn", NULL };
3265 LDAPMessage *msg_sub;
3267 /* we only search with scope ONE, we do not expect any further
3268 * objects to be created deeper */
3270 status = ads_do_search_retry(ads, hostnameDN,
3271 LDAP_SCOPE_ONELEVEL,
3272 "(objectclass=*)", attrs, &res);
3274 if (!ADS_ERR_OK(status)) {
3275 SAFE_FREE(host);
3276 ads_memfree(ads, hostnameDN);
3277 return status;
3280 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3281 msg_sub = ads_next_entry(ads, msg_sub)) {
3283 char *dn = NULL;
3285 if ((dn = ads_get_dn(ads, msg_sub)) == NULL) {
3286 SAFE_FREE(host);
3287 ads_memfree(ads, hostnameDN);
3288 return ADS_ERROR(LDAP_NO_MEMORY);
3291 status = ads_del_dn(ads, dn);
3292 if (!ADS_ERR_OK(status)) {
3293 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3294 SAFE_FREE(host);
3295 ads_memfree(ads, dn);
3296 ads_memfree(ads, hostnameDN);
3297 return status;
3300 ads_memfree(ads, dn);
3303 /* there should be no subordinate objects anymore */
3304 status = ads_do_search_retry(ads, hostnameDN,
3305 LDAP_SCOPE_ONELEVEL,
3306 "(objectclass=*)", attrs, &res);
3308 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3309 SAFE_FREE(host);
3310 ads_memfree(ads, hostnameDN);
3311 return status;
3314 /* delete hostnameDN now */
3315 status = ads_del_dn(ads, hostnameDN);
3316 if (!ADS_ERR_OK(status)) {
3317 SAFE_FREE(host);
3318 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3319 ads_memfree(ads, hostnameDN);
3320 return status;
3324 ads_memfree(ads, hostnameDN);
3326 status = ads_find_machine_acct(ads, &res, host);
3327 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3328 DEBUG(3, ("Failed to remove host account.\n"));
3329 SAFE_FREE(host);
3330 return status;
3333 SAFE_FREE(host);
3334 return status;
3338 * pull all token-sids from an LDAP dn
3339 * @param ads connection to ads server
3340 * @param mem_ctx TALLOC_CTX for allocating sid array
3341 * @param dn of LDAP object
3342 * @param user_sid pointer to DOM_SID (objectSid)
3343 * @param primary_group_sid pointer to DOM_SID (self composed)
3344 * @param sids pointer to sid array to allocate
3345 * @param num_sids counter of SIDs pulled
3346 * @return status of token query
3348 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3349 TALLOC_CTX *mem_ctx,
3350 const char *dn,
3351 DOM_SID *user_sid,
3352 DOM_SID *primary_group_sid,
3353 DOM_SID **sids,
3354 size_t *num_sids)
3356 ADS_STATUS status;
3357 LDAPMessage *res = NULL;
3358 int count = 0;
3359 size_t tmp_num_sids;
3360 DOM_SID *tmp_sids;
3361 DOM_SID tmp_user_sid;
3362 DOM_SID tmp_primary_group_sid;
3363 uint32 pgid;
3364 const char *attrs[] = {
3365 "objectSid",
3366 "tokenGroups",
3367 "primaryGroupID",
3368 NULL
3371 status = ads_search_retry_dn(ads, &res, dn, attrs);
3372 if (!ADS_ERR_OK(status)) {
3373 return status;
3376 count = ads_count_replies(ads, res);
3377 if (count != 1) {
3378 ads_msgfree(ads, res);
3379 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3382 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3383 ads_msgfree(ads, res);
3384 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3387 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3388 ads_msgfree(ads, res);
3389 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3393 /* hack to compose the primary group sid without knowing the
3394 * domsid */
3396 DOM_SID domsid;
3397 uint32 dummy_rid;
3399 sid_copy(&domsid, &tmp_user_sid);
3401 if (!sid_split_rid(&domsid, &dummy_rid)) {
3402 ads_msgfree(ads, res);
3403 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3406 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3407 ads_msgfree(ads, res);
3408 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3412 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3414 if (tmp_num_sids == 0 || !tmp_sids) {
3415 ads_msgfree(ads, res);
3416 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3419 if (num_sids) {
3420 *num_sids = tmp_num_sids;
3423 if (sids) {
3424 *sids = tmp_sids;
3427 if (user_sid) {
3428 *user_sid = tmp_user_sid;
3431 if (primary_group_sid) {
3432 *primary_group_sid = tmp_primary_group_sid;
3435 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3437 ads_msgfree(ads, res);
3438 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3442 * Find a sAMAccoutName in LDAP
3443 * @param ads connection to ads server
3444 * @param mem_ctx TALLOC_CTX for allocating sid array
3445 * @param samaccountname to search
3446 * @param uac_ret uint32 pointer userAccountControl attribute value
3447 * @param dn_ret pointer to dn
3448 * @return status of token query
3450 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3451 TALLOC_CTX *mem_ctx,
3452 const char *samaccountname,
3453 uint32 *uac_ret,
3454 const char **dn_ret)
3456 ADS_STATUS status;
3457 const char *attrs[] = { "userAccountControl", NULL };
3458 const char *filter;
3459 LDAPMessage *res = NULL;
3460 char *dn = NULL;
3461 uint32 uac = 0;
3463 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3464 samaccountname);
3465 if (filter == NULL) {
3466 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3467 goto out;
3470 status = ads_do_search_all(ads, ads->config.bind_path,
3471 LDAP_SCOPE_SUBTREE,
3472 filter, attrs, &res);
3474 if (!ADS_ERR_OK(status)) {
3475 goto out;
3478 if (ads_count_replies(ads, res) != 1) {
3479 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3480 goto out;
3483 dn = ads_get_dn(ads, res);
3484 if (dn == NULL) {
3485 status = ADS_ERROR(LDAP_NO_MEMORY);
3486 goto out;
3489 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3490 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3491 goto out;
3494 if (uac_ret) {
3495 *uac_ret = uac;
3498 if (dn_ret) {
3499 *dn_ret = talloc_strdup(mem_ctx, dn);
3500 if (!*dn_ret) {
3501 status = ADS_ERROR(LDAP_NO_MEMORY);
3502 goto out;
3505 out:
3506 ads_memfree(ads, dn);
3507 ads_msgfree(ads, res);
3509 return status;
3513 * find our configuration path
3514 * @param ads connection to ads server
3515 * @param mem_ctx Pointer to talloc context
3516 * @param config_path Pointer to the config path
3517 * @return status of search
3519 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3520 TALLOC_CTX *mem_ctx,
3521 char **config_path)
3523 ADS_STATUS status;
3524 LDAPMessage *res = NULL;
3525 const char *config_context = NULL;
3526 const char *attrs[] = { "configurationNamingContext", NULL };
3528 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3529 "(objectclass=*)", attrs, &res);
3530 if (!ADS_ERR_OK(status)) {
3531 return status;
3534 config_context = ads_pull_string(ads, mem_ctx, res,
3535 "configurationNamingContext");
3536 ads_msgfree(ads, res);
3537 if (!config_context) {
3538 return ADS_ERROR(LDAP_NO_MEMORY);
3541 if (config_path) {
3542 *config_path = talloc_strdup(mem_ctx, config_context);
3543 if (!*config_path) {
3544 return ADS_ERROR(LDAP_NO_MEMORY);
3548 return ADS_ERROR(LDAP_SUCCESS);
3552 * find the displayName of an extended right
3553 * @param ads connection to ads server
3554 * @param config_path The config path
3555 * @param mem_ctx Pointer to talloc context
3556 * @param GUID struct of the rightsGUID
3557 * @return status of search
3559 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3560 const char *config_path,
3561 TALLOC_CTX *mem_ctx,
3562 const struct GUID *rights_guid)
3564 ADS_STATUS rc;
3565 LDAPMessage *res = NULL;
3566 char *expr = NULL;
3567 const char *attrs[] = { "displayName", NULL };
3568 const char *result = NULL;
3569 const char *path;
3571 if (!ads || !mem_ctx || !rights_guid) {
3572 goto done;
3575 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3576 smb_uuid_string(mem_ctx, *rights_guid));
3577 if (!expr) {
3578 goto done;
3581 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3582 if (!path) {
3583 goto done;
3586 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3587 expr, attrs, &res);
3588 if (!ADS_ERR_OK(rc)) {
3589 goto done;
3592 if (ads_count_replies(ads, res) != 1) {
3593 goto done;
3596 result = ads_pull_string(ads, mem_ctx, res, "displayName");
3598 done:
3599 ads_msgfree(ads, res);
3600 return result;
3605 * verify or build and verify an account ou
3606 * @param mem_ctx Pointer to talloc context
3607 * @param ads connection to ads server
3608 * @param account_ou
3609 * @return status of search
3612 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3613 ADS_STRUCT *ads,
3614 const char **account_ou)
3616 struct ldb_dn *name_dn = NULL;
3617 const char *name = NULL;
3618 char *ou_string = NULL;
3620 name_dn = ldb_dn_explode(mem_ctx, *account_ou);
3621 if (name_dn) {
3622 return ADS_SUCCESS;
3625 ou_string = ads_ou_string(ads, *account_ou);
3626 if (!ou_string) {
3627 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3630 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3631 ads->config.bind_path);
3632 SAFE_FREE(ou_string);
3633 if (!name) {
3634 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3637 name_dn = ldb_dn_explode(mem_ctx, name);
3638 if (!name_dn) {
3639 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3642 *account_ou = talloc_strdup(mem_ctx, name);
3643 if (!*account_ou) {
3644 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3647 return ADS_SUCCESS;
3650 #endif