Add ntsvcs_getdevregprop command to rpcclient.
[Samba/gebeck_regimport.git] / source3 / libads / ldap.c
blob00d36b7edcb98040fa3648e77753180f0c71fb5e
1 /*
2 Unix SMB/CIFS implementation.
3 ads (active directory) utility library
4 Copyright (C) Andrew Tridgell 2001
5 Copyright (C) Remus Koos 2001
6 Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002
7 Copyright (C) Guenther Deschner 2005
8 Copyright (C) Gerald Carter 2006
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "includes.h"
26 #ifdef HAVE_LDAP
28 /**
29 * @file ldap.c
30 * @brief basic ldap client-side routines for ads server communications
32 * The routines contained here should do the necessary ldap calls for
33 * ads setups.
35 * Important note: attribute names passed into ads_ routines must
36 * already be in UTF-8 format. We do not convert them because in almost
37 * all cases, they are just ascii (which is represented with the same
38 * codepoints in UTF-8). This may have to change at some point
39 **/
42 #define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"
44 static SIG_ATOMIC_T gotalarm;
46 /***************************************************************
47 Signal function to tell us we timed out.
48 ****************************************************************/
50 static void gotalarm_sig(void)
52 gotalarm = 1;
55 LDAP *ldap_open_with_timeout(const char *server, int port, unsigned int to)
57 LDAP *ldp = NULL;
60 DEBUG(10, ("Opening connection to LDAP server '%s:%d', timeout "
61 "%u seconds\n", server, port, to));
63 /* Setup timeout */
64 gotalarm = 0;
65 CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
66 alarm(to);
67 /* End setup timeout. */
69 ldp = ldap_open(server, port);
71 if (ldp == NULL) {
72 DEBUG(2,("Could not open connection to LDAP server %s:%d: %s\n",
73 server, port, strerror(errno)));
74 } else {
75 DEBUG(10, ("Connected to LDAP server '%s:%d'\n", server, port));
78 /* Teardown timeout. */
79 CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
80 alarm(0);
82 return ldp;
85 static int ldap_search_with_timeout(LDAP *ld,
86 LDAP_CONST char *base,
87 int scope,
88 LDAP_CONST char *filter,
89 char **attrs,
90 int attrsonly,
91 LDAPControl **sctrls,
92 LDAPControl **cctrls,
93 int sizelimit,
94 LDAPMessage **res )
96 struct timeval timeout;
97 int result;
99 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
100 timeout.tv_sec = lp_ldap_timeout();
101 timeout.tv_usec = 0;
103 /* Setup alarm timeout.... Do we need both of these ? JRA. */
104 gotalarm = 0;
105 CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
106 alarm(lp_ldap_timeout());
107 /* End setup timeout. */
109 result = ldap_search_ext_s(ld, base, scope, filter, attrs,
110 attrsonly, sctrls, cctrls, &timeout,
111 sizelimit, res);
113 /* Teardown timeout. */
114 CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
115 alarm(0);
117 if (gotalarm != 0)
118 return LDAP_TIMELIMIT_EXCEEDED;
120 return result;
123 /**********************************************
124 Do client and server sitename match ?
125 **********************************************/
127 bool ads_sitename_match(ADS_STRUCT *ads)
129 if (ads->config.server_site_name == NULL &&
130 ads->config.client_site_name == NULL ) {
131 DEBUG(10,("ads_sitename_match: both null\n"));
132 return True;
134 if (ads->config.server_site_name &&
135 ads->config.client_site_name &&
136 strequal(ads->config.server_site_name,
137 ads->config.client_site_name)) {
138 DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
139 return True;
141 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
142 ads->config.server_site_name ? ads->config.server_site_name : "NULL",
143 ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
144 return False;
147 /**********************************************
148 Is this the closest DC ?
149 **********************************************/
151 bool ads_closest_dc(ADS_STRUCT *ads)
153 if (ads->config.flags & ADS_CLOSEST) {
154 DEBUG(10,("ads_closest_dc: ADS_CLOSEST flag set\n"));
155 return True;
158 /* not sure if this can ever happen */
159 if (ads_sitename_match(ads)) {
160 DEBUG(10,("ads_closest_dc: ADS_CLOSEST flag not set but sites match\n"));
161 return True;
164 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
165 ads->config.ldap_server_name));
167 return False;
172 try a connection to a given ldap server, returning True and setting the servers IP
173 in the ads struct if successful
175 bool ads_try_connect(ADS_STRUCT *ads, const char *server )
177 char *srv;
178 struct cldap_netlogon_reply cldap_reply;
180 if (!server || !*server) {
181 return False;
184 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
185 server, ads->server.realm));
187 /* this copes with inet_ntoa brokenness */
189 srv = SMB_STRDUP(server);
191 ZERO_STRUCT( cldap_reply );
193 if ( !ads_cldap_netlogon( srv, ads->server.realm, &cldap_reply ) ) {
194 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", srv));
195 SAFE_FREE( srv );
196 return False;
199 /* Check the CLDAP reply flags */
201 if ( !(cldap_reply.flags & ADS_LDAP) ) {
202 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
203 srv));
204 SAFE_FREE( srv );
205 return False;
208 /* Fill in the ads->config values */
210 SAFE_FREE(ads->config.realm);
211 SAFE_FREE(ads->config.bind_path);
212 SAFE_FREE(ads->config.ldap_server_name);
213 SAFE_FREE(ads->config.server_site_name);
214 SAFE_FREE(ads->config.client_site_name);
215 SAFE_FREE(ads->server.workgroup);
217 ads->config.flags = cldap_reply.flags;
218 ads->config.ldap_server_name = SMB_STRDUP(cldap_reply.hostname);
219 strupper_m(cldap_reply.domain);
220 ads->config.realm = SMB_STRDUP(cldap_reply.domain);
221 ads->config.bind_path = ads_build_dn(ads->config.realm);
222 if (*cldap_reply.server_site_name) {
223 ads->config.server_site_name =
224 SMB_STRDUP(cldap_reply.server_site_name);
226 if (*cldap_reply.client_site_name) {
227 ads->config.client_site_name =
228 SMB_STRDUP(cldap_reply.client_site_name);
230 ads->server.workgroup = SMB_STRDUP(cldap_reply.netbios_domain);
232 ads->ldap.port = LDAP_PORT;
233 if (!interpret_string_addr(&ads->ldap.ss, srv, 0)) {
234 DEBUG(1,("ads_try_connect: unable to convert %s "
235 "to an address\n",
236 srv));
237 SAFE_FREE( srv );
238 return False;
241 SAFE_FREE(srv);
243 /* Store our site name. */
244 sitename_store( cldap_reply.domain, cldap_reply.client_site_name );
246 return True;
249 /**********************************************************************
250 Try to find an AD dc using our internal name resolution routines
251 Try the realm first and then then workgroup name if netbios is not
252 disabled
253 **********************************************************************/
255 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
257 const char *c_realm;
258 int count, i=0;
259 struct ip_service *ip_list;
260 const char *realm;
261 bool got_realm = False;
262 bool use_own_domain = False;
263 char *sitename;
264 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
266 /* if the realm and workgroup are both empty, assume they are ours */
268 /* realm */
269 c_realm = ads->server.realm;
271 if ( !c_realm || !*c_realm ) {
272 /* special case where no realm and no workgroup means our own */
273 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
274 use_own_domain = True;
275 c_realm = lp_realm();
279 if (c_realm && *c_realm)
280 got_realm = True;
282 /* we need to try once with the realm name and fallback to the
283 netbios domain name if we fail (if netbios has not been disabled */
285 if ( !got_realm && !lp_disable_netbios() ) {
286 c_realm = ads->server.workgroup;
287 if (!c_realm || !*c_realm) {
288 if ( use_own_domain )
289 c_realm = lp_workgroup();
292 if ( !c_realm || !*c_realm ) {
293 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
294 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
298 realm = c_realm;
300 sitename = sitename_fetch(realm);
302 again:
304 DEBUG(6,("ads_find_dc: looking for %s '%s'\n",
305 (got_realm ? "realm" : "domain"), realm));
307 status = get_sorted_dc_list(realm, sitename, &ip_list, &count, got_realm);
308 if (!NT_STATUS_IS_OK(status)) {
309 /* fall back to netbios if we can */
310 if ( got_realm && !lp_disable_netbios() ) {
311 got_realm = False;
312 goto again;
315 SAFE_FREE(sitename);
316 return status;
319 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
320 for ( i=0; i<count; i++ ) {
321 char server[INET6_ADDRSTRLEN];
323 print_sockaddr(server, sizeof(server), &ip_list[i].ss);
325 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
326 continue;
328 if (!got_realm) {
329 /* realm in this case is a workgroup name. We need
330 to ignore any IP addresses in the negative connection
331 cache that match ip addresses returned in the ad realm
332 case. It sucks that I have to reproduce the logic above... */
333 c_realm = ads->server.realm;
334 if ( !c_realm || !*c_realm ) {
335 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
336 c_realm = lp_realm();
339 if (c_realm && *c_realm &&
340 !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm, server))) {
341 /* Ensure we add the workgroup name for this
342 IP address as negative too. */
343 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
344 continue;
348 if ( ads_try_connect(ads, server) ) {
349 SAFE_FREE(ip_list);
350 SAFE_FREE(sitename);
351 return NT_STATUS_OK;
354 /* keep track of failures */
355 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
358 SAFE_FREE(ip_list);
360 /* In case we failed to contact one of our closest DC on our site we
361 * need to try to find another DC, retry with a site-less SRV DNS query
362 * - Guenther */
364 if (sitename) {
365 DEBUG(1,("ads_find_dc: failed to find a valid DC on our site (%s), "
366 "trying to find another DC\n", sitename));
367 SAFE_FREE(sitename);
368 namecache_delete(realm, 0x1C);
369 goto again;
372 return NT_STATUS_NO_LOGON_SERVERS;
377 * Connect to the LDAP server
378 * @param ads Pointer to an existing ADS_STRUCT
379 * @return status of connection
381 ADS_STATUS ads_connect(ADS_STRUCT *ads)
383 int version = LDAP_VERSION3;
384 ADS_STATUS status;
385 NTSTATUS ntstatus;
386 char addr[INET6_ADDRSTRLEN];
388 ZERO_STRUCT(ads->ldap);
389 ads->ldap.last_attempt = time(NULL);
390 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
392 /* try with a user specified server */
394 if (DEBUGLEVEL >= 11) {
395 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
396 DEBUG(11,("ads_connect: entering\n"));
397 DEBUGADD(11,("%s\n", s));
398 TALLOC_FREE(s);
401 if (ads->server.ldap_server &&
402 ads_try_connect(ads, ads->server.ldap_server)) {
403 goto got_connection;
406 ntstatus = ads_find_dc(ads);
407 if (NT_STATUS_IS_OK(ntstatus)) {
408 goto got_connection;
411 status = ADS_ERROR_NT(ntstatus);
412 goto out;
414 got_connection:
416 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
417 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
419 if (!ads->auth.user_name) {
420 /* Must use the userPrincipalName value here or sAMAccountName
421 and not servicePrincipalName; found by Guenther Deschner */
423 asprintf(&ads->auth.user_name, "%s$", global_myname() );
426 if (!ads->auth.realm) {
427 ads->auth.realm = SMB_STRDUP(ads->config.realm);
430 if (!ads->auth.kdc_server) {
431 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
432 ads->auth.kdc_server = SMB_STRDUP(addr);
435 #if KRB5_DNS_HACK
436 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
437 to MIT kerberos to work (tridge) */
439 char *env;
440 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
441 setenv(env, ads->auth.kdc_server, 1);
442 free(env);
444 #endif
446 /* If the caller() requested no LDAP bind, then we are done */
448 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
449 status = ADS_SUCCESS;
450 goto out;
453 ads->ldap.mem_ctx = talloc_init("ads LDAP connection memory");
454 if (!ads->ldap.mem_ctx) {
455 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
456 goto out;
459 /* Otherwise setup the TCP LDAP session */
461 ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name,
462 LDAP_PORT, lp_ldap_timeout());
463 if (ads->ldap.ld == NULL) {
464 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
465 goto out;
467 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
469 /* cache the successful connection for workgroup and realm */
470 if (ads_closest_dc(ads)) {
471 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
472 saf_store( ads->server.workgroup, addr);
473 saf_store( ads->server.realm, addr);
476 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
478 status = ADS_ERROR(smb_ldap_start_tls(ads->ldap.ld, version));
479 if (!ADS_ERR_OK(status)) {
480 goto out;
483 /* fill in the current time and offsets */
485 status = ads_current_time( ads );
486 if ( !ADS_ERR_OK(status) ) {
487 goto out;
490 /* Now do the bind */
492 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
493 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
494 goto out;
497 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
498 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
499 goto out;
502 status = ads_sasl_bind(ads);
504 out:
505 if (DEBUGLEVEL >= 11) {
506 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
507 DEBUG(11,("ads_connect: leaving with: %s\n",
508 ads_errstr(status)));
509 DEBUGADD(11,("%s\n", s));
510 TALLOC_FREE(s);
513 return status;
517 * Disconnect the LDAP server
518 * @param ads Pointer to an existing ADS_STRUCT
520 void ads_disconnect(ADS_STRUCT *ads)
522 if (ads->ldap.ld) {
523 ldap_unbind(ads->ldap.ld);
524 ads->ldap.ld = NULL;
526 if (ads->ldap.wrap_ops && ads->ldap.wrap_ops->disconnect) {
527 ads->ldap.wrap_ops->disconnect(ads);
529 if (ads->ldap.mem_ctx) {
530 talloc_free(ads->ldap.mem_ctx);
532 ZERO_STRUCT(ads->ldap);
536 Duplicate a struct berval into talloc'ed memory
538 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
540 struct berval *value;
542 if (!in_val) return NULL;
544 value = TALLOC_ZERO_P(ctx, struct berval);
545 if (value == NULL)
546 return NULL;
547 if (in_val->bv_len == 0) return value;
549 value->bv_len = in_val->bv_len;
550 value->bv_val = (char *)TALLOC_MEMDUP(ctx, in_val->bv_val,
551 in_val->bv_len);
552 return value;
556 Make a values list out of an array of (struct berval *)
558 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
559 const struct berval **in_vals)
561 struct berval **values;
562 int i;
564 if (!in_vals) return NULL;
565 for (i=0; in_vals[i]; i++)
566 ; /* count values */
567 values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
568 if (!values) return NULL;
570 for (i=0; in_vals[i]; i++) {
571 values[i] = dup_berval(ctx, in_vals[i]);
573 return values;
577 UTF8-encode a values list out of an array of (char *)
579 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
581 char **values;
582 int i;
584 if (!in_vals) return NULL;
585 for (i=0; in_vals[i]; i++)
586 ; /* count values */
587 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
588 if (!values) return NULL;
590 for (i=0; in_vals[i]; i++) {
591 if (push_utf8_talloc(ctx, &values[i], in_vals[i]) == (size_t) -1) {
592 TALLOC_FREE(values);
593 return NULL;
596 return values;
600 Pull a (char *) array out of a UTF8-encoded values list
602 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
604 char **values;
605 int i;
607 if (!in_vals) return NULL;
608 for (i=0; in_vals[i]; i++)
609 ; /* count values */
610 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
611 if (!values) return NULL;
613 for (i=0; in_vals[i]; i++) {
614 pull_utf8_talloc(ctx, &values[i], in_vals[i]);
616 return values;
620 * Do a search with paged results. cookie must be null on the first
621 * call, and then returned on each subsequent call. It will be null
622 * again when the entire search is complete
623 * @param ads connection to ads server
624 * @param bind_path Base dn for the search
625 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
626 * @param expr Search expression - specified in local charset
627 * @param attrs Attributes to retrieve - specified in utf8 or ascii
628 * @param res ** which will contain results - free res* with ads_msgfree()
629 * @param count Number of entries retrieved on this page
630 * @param cookie The paged results cookie to be returned on subsequent calls
631 * @return status of search
633 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
634 const char *bind_path,
635 int scope, const char *expr,
636 const char **attrs, void *args,
637 LDAPMessage **res,
638 int *count, struct berval **cookie)
640 int rc, i, version;
641 char *utf8_expr, *utf8_path, **search_attrs;
642 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
643 BerElement *cookie_be = NULL;
644 struct berval *cookie_bv= NULL;
645 BerElement *ext_be = NULL;
646 struct berval *ext_bv= NULL;
648 TALLOC_CTX *ctx;
649 ads_control *external_control = (ads_control *) args;
651 *res = NULL;
653 if (!(ctx = talloc_init("ads_do_paged_search_args")))
654 return ADS_ERROR(LDAP_NO_MEMORY);
656 /* 0 means the conversion worked but the result was empty
657 so we only fail if it's -1. In any case, it always
658 at least nulls out the dest */
659 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
660 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
661 rc = LDAP_NO_MEMORY;
662 goto done;
665 if (!attrs || !(*attrs))
666 search_attrs = NULL;
667 else {
668 /* This would be the utf8-encoded version...*/
669 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
670 if (!(str_list_copy(talloc_tos(), &search_attrs, attrs))) {
671 rc = LDAP_NO_MEMORY;
672 goto done;
676 /* Paged results only available on ldap v3 or later */
677 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
678 if (version < LDAP_VERSION3) {
679 rc = LDAP_NOT_SUPPORTED;
680 goto done;
683 cookie_be = ber_alloc_t(LBER_USE_DER);
684 if (*cookie) {
685 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
686 ber_bvfree(*cookie); /* don't need it from last time */
687 *cookie = NULL;
688 } else {
689 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
691 ber_flatten(cookie_be, &cookie_bv);
692 PagedResults.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID);
693 PagedResults.ldctl_iscritical = (char) 1;
694 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
695 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
697 NoReferrals.ldctl_oid = CONST_DISCARD(char *, ADS_NO_REFERRALS_OID);
698 NoReferrals.ldctl_iscritical = (char) 0;
699 NoReferrals.ldctl_value.bv_len = 0;
700 NoReferrals.ldctl_value.bv_val = CONST_DISCARD(char *, "");
702 if (external_control &&
703 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
704 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
706 ExternalCtrl.ldctl_oid = CONST_DISCARD(char *, external_control->control);
707 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
709 /* win2k does not accept a ldctl_value beeing passed in */
711 if (external_control->val != 0) {
713 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
714 rc = LDAP_NO_MEMORY;
715 goto done;
718 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
719 rc = LDAP_NO_MEMORY;
720 goto done;
722 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
723 rc = LDAP_NO_MEMORY;
724 goto done;
727 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
728 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
730 } else {
731 ExternalCtrl.ldctl_value.bv_len = 0;
732 ExternalCtrl.ldctl_value.bv_val = NULL;
735 controls[0] = &NoReferrals;
736 controls[1] = &PagedResults;
737 controls[2] = &ExternalCtrl;
738 controls[3] = NULL;
740 } else {
741 controls[0] = &NoReferrals;
742 controls[1] = &PagedResults;
743 controls[2] = NULL;
746 /* we need to disable referrals as the openldap libs don't
747 handle them and paged results at the same time. Using them
748 together results in the result record containing the server
749 page control being removed from the result list (tridge/jmcd)
751 leaving this in despite the control that says don't generate
752 referrals, in case the server doesn't support it (jmcd)
754 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
756 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
757 search_attrs, 0, controls,
758 NULL, LDAP_NO_LIMIT,
759 (LDAPMessage **)res);
761 ber_free(cookie_be, 1);
762 ber_bvfree(cookie_bv);
764 if (rc) {
765 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
766 ldap_err2string(rc)));
767 goto done;
770 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
771 NULL, &rcontrols, 0);
773 if (!rcontrols) {
774 goto done;
777 for (i=0; rcontrols[i]; i++) {
778 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
779 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
780 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
781 &cookie_bv);
782 /* the berval is the cookie, but must be freed when
783 it is all done */
784 if (cookie_bv->bv_len) /* still more to do */
785 *cookie=ber_bvdup(cookie_bv);
786 else
787 *cookie=NULL;
788 ber_bvfree(cookie_bv);
789 ber_free(cookie_be, 1);
790 break;
793 ldap_controls_free(rcontrols);
795 done:
796 talloc_destroy(ctx);
798 if (ext_be) {
799 ber_free(ext_be, 1);
802 if (ext_bv) {
803 ber_bvfree(ext_bv);
806 /* if/when we decide to utf8-encode attrs, take out this next line */
807 TALLOC_FREE(search_attrs);
809 return ADS_ERROR(rc);
812 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
813 int scope, const char *expr,
814 const char **attrs, LDAPMessage **res,
815 int *count, struct berval **cookie)
817 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
822 * Get all results for a search. This uses ads_do_paged_search() to return
823 * all entries in a large search.
824 * @param ads connection to ads server
825 * @param bind_path Base dn for the search
826 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
827 * @param expr Search expression
828 * @param attrs Attributes to retrieve
829 * @param res ** which will contain results - free res* with ads_msgfree()
830 * @return status of search
832 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
833 int scope, const char *expr,
834 const char **attrs, void *args,
835 LDAPMessage **res)
837 struct berval *cookie = NULL;
838 int count = 0;
839 ADS_STATUS status;
841 *res = NULL;
842 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
843 &count, &cookie);
845 if (!ADS_ERR_OK(status))
846 return status;
848 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
849 while (cookie) {
850 LDAPMessage *res2 = NULL;
851 ADS_STATUS status2;
852 LDAPMessage *msg, *next;
854 status2 = ads_do_paged_search_args(ads, bind_path, scope, expr,
855 attrs, args, &res2, &count, &cookie);
857 if (!ADS_ERR_OK(status2)) break;
859 /* this relies on the way that ldap_add_result_entry() works internally. I hope
860 that this works on all ldap libs, but I have only tested with openldap */
861 for (msg = ads_first_entry(ads, res2); msg; msg = next) {
862 next = ads_next_entry(ads, msg);
863 ldap_add_result_entry((LDAPMessage **)res, msg);
865 /* note that we do not free res2, as the memory is now
866 part of the main returned list */
868 #else
869 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
870 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
871 #endif
873 return status;
876 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
877 int scope, const char *expr,
878 const char **attrs, LDAPMessage **res)
880 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
883 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
884 int scope, const char *expr,
885 const char **attrs, uint32 sd_flags,
886 LDAPMessage **res)
888 ads_control args;
890 args.control = ADS_SD_FLAGS_OID;
891 args.val = sd_flags;
892 args.critical = True;
894 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
899 * Run a function on all results for a search. Uses ads_do_paged_search() and
900 * runs the function as each page is returned, using ads_process_results()
901 * @param ads connection to ads server
902 * @param bind_path Base dn for the search
903 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
904 * @param expr Search expression - specified in local charset
905 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
906 * @param fn Function which takes attr name, values list, and data_area
907 * @param data_area Pointer which is passed to function on each call
908 * @return status of search
910 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
911 int scope, const char *expr, const char **attrs,
912 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
913 void *data_area)
915 struct berval *cookie = NULL;
916 int count = 0;
917 ADS_STATUS status;
918 LDAPMessage *res;
920 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
921 &count, &cookie);
923 if (!ADS_ERR_OK(status)) return status;
925 ads_process_results(ads, res, fn, data_area);
926 ads_msgfree(ads, res);
928 while (cookie) {
929 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
930 &res, &count, &cookie);
932 if (!ADS_ERR_OK(status)) break;
934 ads_process_results(ads, res, fn, data_area);
935 ads_msgfree(ads, res);
938 return status;
942 * Do a search with a timeout.
943 * @param ads connection to ads server
944 * @param bind_path Base dn for the search
945 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
946 * @param expr Search expression
947 * @param attrs Attributes to retrieve
948 * @param res ** which will contain results - free res* with ads_msgfree()
949 * @return status of search
951 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
952 const char *expr,
953 const char **attrs, LDAPMessage **res)
955 int rc;
956 char *utf8_expr, *utf8_path, **search_attrs = NULL;
957 TALLOC_CTX *ctx;
959 *res = NULL;
960 if (!(ctx = talloc_init("ads_do_search"))) {
961 DEBUG(1,("ads_do_search: talloc_init() failed!"));
962 return ADS_ERROR(LDAP_NO_MEMORY);
965 /* 0 means the conversion worked but the result was empty
966 so we only fail if it's negative. In any case, it always
967 at least nulls out the dest */
968 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
969 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
970 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
971 rc = LDAP_NO_MEMORY;
972 goto done;
975 if (!attrs || !(*attrs))
976 search_attrs = NULL;
977 else {
978 /* This would be the utf8-encoded version...*/
979 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
980 if (!(str_list_copy(talloc_tos(), &search_attrs, attrs)))
982 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
983 rc = LDAP_NO_MEMORY;
984 goto done;
988 /* see the note in ads_do_paged_search - we *must* disable referrals */
989 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
991 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
992 search_attrs, 0, NULL, NULL,
993 LDAP_NO_LIMIT,
994 (LDAPMessage **)res);
996 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
997 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
998 rc = 0;
1001 done:
1002 talloc_destroy(ctx);
1003 /* if/when we decide to utf8-encode attrs, take out this next line */
1004 TALLOC_FREE(search_attrs);
1005 return ADS_ERROR(rc);
1008 * Do a general ADS search
1009 * @param ads connection to ads server
1010 * @param res ** which will contain results - free res* with ads_msgfree()
1011 * @param expr Search expression
1012 * @param attrs Attributes to retrieve
1013 * @return status of search
1015 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1016 const char *expr, const char **attrs)
1018 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1019 expr, attrs, res);
1023 * Do a search on a specific DistinguishedName
1024 * @param ads connection to ads server
1025 * @param res ** which will contain results - free res* with ads_msgfree()
1026 * @param dn DistinguishName to search
1027 * @param attrs Attributes to retrieve
1028 * @return status of search
1030 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1031 const char *dn, const char **attrs)
1033 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1034 attrs, res);
1038 * Free up memory from a ads_search
1039 * @param ads connection to ads server
1040 * @param msg Search results to free
1042 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1044 if (!msg) return;
1045 ldap_msgfree(msg);
1049 * Free up memory from various ads requests
1050 * @param ads connection to ads server
1051 * @param mem Area to free
1053 void ads_memfree(ADS_STRUCT *ads, void *mem)
1055 SAFE_FREE(mem);
1059 * Get a dn from search results
1060 * @param ads connection to ads server
1061 * @param msg Search result
1062 * @return dn string
1064 char *ads_get_dn(ADS_STRUCT *ads, LDAPMessage *msg)
1066 char *utf8_dn, *unix_dn;
1068 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1070 if (!utf8_dn) {
1071 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1072 return NULL;
1075 if (pull_utf8_allocate(&unix_dn, utf8_dn) == (size_t)-1) {
1076 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1077 utf8_dn ));
1078 return NULL;
1080 ldap_memfree(utf8_dn);
1081 return unix_dn;
1085 * Get the parent from a dn
1086 * @param dn the dn to return the parent from
1087 * @return parent dn string
1089 char *ads_parent_dn(const char *dn)
1091 char *p;
1093 if (dn == NULL) {
1094 return NULL;
1097 p = strchr(dn, ',');
1099 if (p == NULL) {
1100 return NULL;
1103 return p+1;
1107 * Find a machine account given a hostname
1108 * @param ads connection to ads server
1109 * @param res ** which will contain results - free res* with ads_msgfree()
1110 * @param host Hostname to search for
1111 * @return status of search
1113 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1114 const char *machine)
1116 ADS_STATUS status;
1117 char *expr;
1118 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1120 *res = NULL;
1122 /* the easiest way to find a machine account anywhere in the tree
1123 is to look for hostname$ */
1124 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1125 DEBUG(1, ("asprintf failed!\n"));
1126 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1129 status = ads_search(ads, res, expr, attrs);
1130 SAFE_FREE(expr);
1131 return status;
1135 * Initialize a list of mods to be used in a modify request
1136 * @param ctx An initialized TALLOC_CTX
1137 * @return allocated ADS_MODLIST
1139 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1141 #define ADS_MODLIST_ALLOC_SIZE 10
1142 LDAPMod **mods;
1144 if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1145 /* -1 is safety to make sure we don't go over the end.
1146 need to reset it to NULL before doing ldap modify */
1147 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1149 return (ADS_MODLIST)mods;
1154 add an attribute to the list, with values list already constructed
1156 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1157 int mod_op, const char *name,
1158 const void *_invals)
1160 const void **invals = (const void **)_invals;
1161 int curmod;
1162 LDAPMod **modlist = (LDAPMod **) *mods;
1163 struct berval **ber_values = NULL;
1164 char **char_values = NULL;
1166 if (!invals) {
1167 mod_op = LDAP_MOD_DELETE;
1168 } else {
1169 if (mod_op & LDAP_MOD_BVALUES)
1170 ber_values = ads_dup_values(ctx,
1171 (const struct berval **)invals);
1172 else
1173 char_values = ads_push_strvals(ctx,
1174 (const char **) invals);
1177 /* find the first empty slot */
1178 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1179 curmod++);
1180 if (modlist[curmod] == (LDAPMod *) -1) {
1181 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
1182 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1183 return ADS_ERROR(LDAP_NO_MEMORY);
1184 memset(&modlist[curmod], 0,
1185 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1186 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1187 *mods = (ADS_MODLIST)modlist;
1190 if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
1191 return ADS_ERROR(LDAP_NO_MEMORY);
1192 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1193 if (mod_op & LDAP_MOD_BVALUES) {
1194 modlist[curmod]->mod_bvalues = ber_values;
1195 } else if (mod_op & LDAP_MOD_DELETE) {
1196 modlist[curmod]->mod_values = NULL;
1197 } else {
1198 modlist[curmod]->mod_values = char_values;
1201 modlist[curmod]->mod_op = mod_op;
1202 return ADS_ERROR(LDAP_SUCCESS);
1206 * Add a single string value to a mod list
1207 * @param ctx An initialized TALLOC_CTX
1208 * @param mods An initialized ADS_MODLIST
1209 * @param name The attribute name to add
1210 * @param val The value to add - NULL means DELETE
1211 * @return ADS STATUS indicating success of add
1213 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1214 const char *name, const char *val)
1216 const char *values[2];
1218 values[0] = val;
1219 values[1] = NULL;
1221 if (!val)
1222 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1223 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1227 * Add an array of string values to a mod list
1228 * @param ctx An initialized TALLOC_CTX
1229 * @param mods An initialized ADS_MODLIST
1230 * @param name The attribute name to add
1231 * @param vals The array of string values to add - NULL means DELETE
1232 * @return ADS STATUS indicating success of add
1234 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1235 const char *name, const char **vals)
1237 if (!vals)
1238 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1239 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1240 name, (const void **) vals);
1243 #if 0
1245 * Add a single ber-encoded value to a mod list
1246 * @param ctx An initialized TALLOC_CTX
1247 * @param mods An initialized ADS_MODLIST
1248 * @param name The attribute name to add
1249 * @param val The value to add - NULL means DELETE
1250 * @return ADS STATUS indicating success of add
1252 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1253 const char *name, const struct berval *val)
1255 const struct berval *values[2];
1257 values[0] = val;
1258 values[1] = NULL;
1259 if (!val)
1260 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1261 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1262 name, (const void **) values);
1264 #endif
1267 * Perform an ldap modify
1268 * @param ads connection to ads server
1269 * @param mod_dn DistinguishedName to modify
1270 * @param mods list of modifications to perform
1271 * @return status of modify
1273 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1275 int ret,i;
1276 char *utf8_dn = NULL;
1278 this control is needed to modify that contains a currently
1279 non-existent attribute (but allowable for the object) to run
1281 LDAPControl PermitModify = {
1282 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
1283 {0, NULL},
1284 (char) 1};
1285 LDAPControl *controls[2];
1287 controls[0] = &PermitModify;
1288 controls[1] = NULL;
1290 if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) {
1291 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1294 /* find the end of the list, marked by NULL or -1 */
1295 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1296 /* make sure the end of the list is NULL */
1297 mods[i] = NULL;
1298 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1299 (LDAPMod **) mods, controls, NULL);
1300 SAFE_FREE(utf8_dn);
1301 return ADS_ERROR(ret);
1305 * Perform an ldap add
1306 * @param ads connection to ads server
1307 * @param new_dn DistinguishedName to add
1308 * @param mods list of attributes and values for DN
1309 * @return status of add
1311 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1313 int ret, i;
1314 char *utf8_dn = NULL;
1316 if (push_utf8_allocate(&utf8_dn, new_dn) == -1) {
1317 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1318 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1321 /* find the end of the list, marked by NULL or -1 */
1322 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1323 /* make sure the end of the list is NULL */
1324 mods[i] = NULL;
1326 ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1327 SAFE_FREE(utf8_dn);
1328 return ADS_ERROR(ret);
1332 * Delete a DistinguishedName
1333 * @param ads connection to ads server
1334 * @param new_dn DistinguishedName to delete
1335 * @return status of delete
1337 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1339 int ret;
1340 char *utf8_dn = NULL;
1341 if (push_utf8_allocate(&utf8_dn, del_dn) == -1) {
1342 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1343 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1346 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1347 SAFE_FREE(utf8_dn);
1348 return ADS_ERROR(ret);
1352 * Build an org unit string
1353 * if org unit is Computers or blank then assume a container, otherwise
1354 * assume a / separated list of organisational units.
1355 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1356 * @param ads connection to ads server
1357 * @param org_unit Organizational unit
1358 * @return org unit string - caller must free
1360 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1362 char *ret = NULL;
1364 if (!org_unit || !*org_unit) {
1366 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1368 /* samba4 might not yet respond to a wellknownobject-query */
1369 return ret ? ret : SMB_STRDUP("cn=Computers");
1372 if (strequal(org_unit, "Computers")) {
1373 return SMB_STRDUP("cn=Computers");
1376 /* jmcd: removed "\\" from the separation chars, because it is
1377 needed as an escape for chars like '#' which are valid in an
1378 OU name */
1379 return ads_build_path(org_unit, "/", "ou=", 1);
1383 * Get a org unit string for a well-known GUID
1384 * @param ads connection to ads server
1385 * @param wknguid Well known GUID
1386 * @return org unit string - caller must free
1388 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1390 ADS_STATUS status;
1391 LDAPMessage *res = NULL;
1392 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1393 **bind_dn_exp = NULL;
1394 const char *attrs[] = {"distinguishedName", NULL};
1395 int new_ln, wkn_ln, bind_ln, i;
1397 if (wknguid == NULL) {
1398 return NULL;
1401 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1402 DEBUG(1, ("asprintf failed!\n"));
1403 return NULL;
1406 status = ads_search_dn(ads, &res, base, attrs);
1407 if (!ADS_ERR_OK(status)) {
1408 DEBUG(1,("Failed while searching for: %s\n", base));
1409 goto out;
1412 if (ads_count_replies(ads, res) != 1) {
1413 goto out;
1416 /* substitute the bind-path from the well-known-guid-search result */
1417 wkn_dn = ads_get_dn(ads, res);
1418 if (!wkn_dn) {
1419 goto out;
1422 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1423 if (!wkn_dn_exp) {
1424 goto out;
1427 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1428 if (!bind_dn_exp) {
1429 goto out;
1432 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1434 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1437 new_ln = wkn_ln - bind_ln;
1439 ret = SMB_STRDUP(wkn_dn_exp[0]);
1440 if (!ret) {
1441 goto out;
1444 for (i=1; i < new_ln; i++) {
1445 char *s = NULL;
1447 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1448 SAFE_FREE(ret);
1449 goto out;
1452 SAFE_FREE(ret);
1453 ret = SMB_STRDUP(s);
1454 free(s);
1455 if (!ret) {
1456 goto out;
1460 out:
1461 SAFE_FREE(base);
1462 ads_msgfree(ads, res);
1463 ads_memfree(ads, wkn_dn);
1464 if (wkn_dn_exp) {
1465 ldap_value_free(wkn_dn_exp);
1467 if (bind_dn_exp) {
1468 ldap_value_free(bind_dn_exp);
1471 return ret;
1475 * Adds (appends) an item to an attribute array, rather then
1476 * replacing the whole list
1477 * @param ctx An initialized TALLOC_CTX
1478 * @param mods An initialized ADS_MODLIST
1479 * @param name name of the ldap attribute to append to
1480 * @param vals an array of values to add
1481 * @return status of addition
1484 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1485 const char *name, const char **vals)
1487 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1488 (const void *) vals);
1492 * Determines the computer account's current KVNO via an LDAP lookup
1493 * @param ads An initialized ADS_STRUCT
1494 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1495 * @return the kvno for the computer account, or -1 in case of a failure.
1498 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *machine_name)
1500 LDAPMessage *res = NULL;
1501 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1502 char *filter;
1503 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1504 char *dn_string = NULL;
1505 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1507 DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name));
1508 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1509 return kvno;
1511 ret = ads_search(ads, &res, filter, attrs);
1512 SAFE_FREE(filter);
1513 if (!ADS_ERR_OK(ret) && ads_count_replies(ads, res)) {
1514 DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name));
1515 ads_msgfree(ads, res);
1516 return kvno;
1519 dn_string = ads_get_dn(ads, res);
1520 if (!dn_string) {
1521 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1522 ads_msgfree(ads, res);
1523 return kvno;
1525 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1526 ads_memfree(ads, dn_string);
1528 /* ---------------------------------------------------------
1529 * 0 is returned as a default KVNO from this point on...
1530 * This is done because Windows 2000 does not support key
1531 * version numbers. Chances are that a failure in the next
1532 * step is simply due to Windows 2000 being used for a
1533 * domain controller. */
1534 kvno = 0;
1536 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1537 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1538 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1539 ads_msgfree(ads, res);
1540 return kvno;
1543 /* Success */
1544 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1545 ads_msgfree(ads, res);
1546 return kvno;
1550 * This clears out all registered spn's for a given hostname
1551 * @param ads An initilaized ADS_STRUCT
1552 * @param machine_name the NetBIOS name of the computer.
1553 * @return 0 upon success, non-zero otherwise.
1556 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1558 TALLOC_CTX *ctx;
1559 LDAPMessage *res = NULL;
1560 ADS_MODLIST mods;
1561 const char *servicePrincipalName[1] = {NULL};
1562 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1563 char *dn_string = NULL;
1565 ret = ads_find_machine_acct(ads, &res, machine_name);
1566 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1567 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1568 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1569 ads_msgfree(ads, res);
1570 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1573 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1574 ctx = talloc_init("ads_clear_service_principal_names");
1575 if (!ctx) {
1576 ads_msgfree(ads, res);
1577 return ADS_ERROR(LDAP_NO_MEMORY);
1580 if (!(mods = ads_init_mods(ctx))) {
1581 talloc_destroy(ctx);
1582 ads_msgfree(ads, res);
1583 return ADS_ERROR(LDAP_NO_MEMORY);
1585 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1586 if (!ADS_ERR_OK(ret)) {
1587 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1588 ads_msgfree(ads, res);
1589 talloc_destroy(ctx);
1590 return ret;
1592 dn_string = ads_get_dn(ads, res);
1593 if (!dn_string) {
1594 talloc_destroy(ctx);
1595 ads_msgfree(ads, res);
1596 return ADS_ERROR(LDAP_NO_MEMORY);
1598 ret = ads_gen_mod(ads, dn_string, mods);
1599 ads_memfree(ads,dn_string);
1600 if (!ADS_ERR_OK(ret)) {
1601 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1602 machine_name));
1603 ads_msgfree(ads, res);
1604 talloc_destroy(ctx);
1605 return ret;
1608 ads_msgfree(ads, res);
1609 talloc_destroy(ctx);
1610 return ret;
1614 * This adds a service principal name to an existing computer account
1615 * (found by hostname) in AD.
1616 * @param ads An initialized ADS_STRUCT
1617 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1618 * @param my_fqdn The fully qualified DNS name of the machine
1619 * @param spn A string of the service principal to add, i.e. 'host'
1620 * @return 0 upon sucess, or non-zero if a failure occurs
1623 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1624 const char *my_fqdn, const char *spn)
1626 ADS_STATUS ret;
1627 TALLOC_CTX *ctx;
1628 LDAPMessage *res = NULL;
1629 char *psp1, *psp2;
1630 ADS_MODLIST mods;
1631 char *dn_string = NULL;
1632 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1634 ret = ads_find_machine_acct(ads, &res, machine_name);
1635 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1636 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1637 machine_name));
1638 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1639 spn, machine_name, ads->config.realm));
1640 ads_msgfree(ads, res);
1641 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1644 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1645 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1646 ads_msgfree(ads, res);
1647 return ADS_ERROR(LDAP_NO_MEMORY);
1650 /* add short name spn */
1652 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1653 talloc_destroy(ctx);
1654 ads_msgfree(ads, res);
1655 return ADS_ERROR(LDAP_NO_MEMORY);
1657 strupper_m(psp1);
1658 strlower_m(&psp1[strlen(spn)]);
1659 servicePrincipalName[0] = psp1;
1661 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1662 psp1, machine_name));
1665 /* add fully qualified spn */
1667 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1668 ret = ADS_ERROR(LDAP_NO_MEMORY);
1669 goto out;
1671 strupper_m(psp2);
1672 strlower_m(&psp2[strlen(spn)]);
1673 servicePrincipalName[1] = psp2;
1675 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1676 psp2, machine_name));
1678 if ( (mods = ads_init_mods(ctx)) == NULL ) {
1679 ret = ADS_ERROR(LDAP_NO_MEMORY);
1680 goto out;
1683 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1684 if (!ADS_ERR_OK(ret)) {
1685 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1686 goto out;
1689 if ( (dn_string = ads_get_dn(ads, res)) == NULL ) {
1690 ret = ADS_ERROR(LDAP_NO_MEMORY);
1691 goto out;
1694 ret = ads_gen_mod(ads, dn_string, mods);
1695 ads_memfree(ads,dn_string);
1696 if (!ADS_ERR_OK(ret)) {
1697 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1698 goto out;
1701 out:
1702 TALLOC_FREE( ctx );
1703 ads_msgfree(ads, res);
1704 return ret;
1708 * adds a machine account to the ADS server
1709 * @param ads An intialized ADS_STRUCT
1710 * @param machine_name - the NetBIOS machine name of this account.
1711 * @param account_type A number indicating the type of account to create
1712 * @param org_unit The LDAP path in which to place this account
1713 * @return 0 upon success, or non-zero otherwise
1716 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1717 const char *org_unit)
1719 ADS_STATUS ret;
1720 char *samAccountName, *controlstr;
1721 TALLOC_CTX *ctx;
1722 ADS_MODLIST mods;
1723 char *machine_escaped = NULL;
1724 char *new_dn;
1725 const char *objectClass[] = {"top", "person", "organizationalPerson",
1726 "user", "computer", NULL};
1727 LDAPMessage *res = NULL;
1728 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
1729 UF_DONT_EXPIRE_PASSWD |\
1730 UF_ACCOUNTDISABLE );
1732 if (!(ctx = talloc_init("ads_add_machine_acct")))
1733 return ADS_ERROR(LDAP_NO_MEMORY);
1735 ret = ADS_ERROR(LDAP_NO_MEMORY);
1737 machine_escaped = escape_rdn_val_string_alloc(machine_name);
1738 if (!machine_escaped) {
1739 goto done;
1742 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
1743 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
1745 if ( !new_dn || !samAccountName ) {
1746 goto done;
1749 #ifndef ENCTYPE_ARCFOUR_HMAC
1750 acct_control |= UF_USE_DES_KEY_ONLY;
1751 #endif
1753 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
1754 goto done;
1757 if (!(mods = ads_init_mods(ctx))) {
1758 goto done;
1761 ads_mod_str(ctx, &mods, "cn", machine_name);
1762 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1763 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1764 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1766 ret = ads_gen_add(ads, new_dn, mods);
1768 done:
1769 SAFE_FREE(machine_escaped);
1770 ads_msgfree(ads, res);
1771 talloc_destroy(ctx);
1773 return ret;
1777 * move a machine account to another OU on the ADS server
1778 * @param ads - An intialized ADS_STRUCT
1779 * @param machine_name - the NetBIOS machine name of this account.
1780 * @param org_unit - The LDAP path in which to place this account
1781 * @param moved - whether we moved the machine account (optional)
1782 * @return 0 upon success, or non-zero otherwise
1785 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1786 const char *org_unit, bool *moved)
1788 ADS_STATUS rc;
1789 int ldap_status;
1790 LDAPMessage *res = NULL;
1791 char *filter = NULL;
1792 char *computer_dn = NULL;
1793 char *parent_dn;
1794 char *computer_rdn = NULL;
1795 bool need_move = False;
1797 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1798 rc = ADS_ERROR(LDAP_NO_MEMORY);
1799 goto done;
1802 /* Find pre-existing machine */
1803 rc = ads_search(ads, &res, filter, NULL);
1804 if (!ADS_ERR_OK(rc)) {
1805 goto done;
1808 computer_dn = ads_get_dn(ads, res);
1809 if (!computer_dn) {
1810 rc = ADS_ERROR(LDAP_NO_MEMORY);
1811 goto done;
1814 parent_dn = ads_parent_dn(computer_dn);
1815 if (strequal(parent_dn, org_unit)) {
1816 goto done;
1819 need_move = True;
1821 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
1822 rc = ADS_ERROR(LDAP_NO_MEMORY);
1823 goto done;
1826 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
1827 org_unit, 1, NULL, NULL);
1828 rc = ADS_ERROR(ldap_status);
1830 done:
1831 ads_msgfree(ads, res);
1832 SAFE_FREE(filter);
1833 SAFE_FREE(computer_dn);
1834 SAFE_FREE(computer_rdn);
1836 if (!ADS_ERR_OK(rc)) {
1837 need_move = False;
1840 if (moved) {
1841 *moved = need_move;
1844 return rc;
1848 dump a binary result from ldap
1850 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
1852 int i, j;
1853 for (i=0; values[i]; i++) {
1854 printf("%s: ", field);
1855 for (j=0; j<values[i]->bv_len; j++) {
1856 printf("%02X", (unsigned char)values[i]->bv_val[j]);
1858 printf("\n");
1862 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
1864 int i;
1865 for (i=0; values[i]; i++) {
1867 UUID_FLAT guid;
1868 struct GUID tmp;
1870 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1871 smb_uuid_unpack(guid, &tmp);
1872 printf("%s: %s\n", field, smb_uuid_string(talloc_tos(), tmp));
1877 dump a sid result from ldap
1879 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
1881 int i;
1882 for (i=0; values[i]; i++) {
1883 DOM_SID sid;
1884 fstring tmp;
1885 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1886 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
1891 dump ntSecurityDescriptor
1893 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
1895 TALLOC_CTX *frame = talloc_stackframe();
1896 struct security_descriptor *psd;
1897 NTSTATUS status;
1899 status = unmarshall_sec_desc(talloc_tos(), (uint8 *)values[0]->bv_val,
1900 values[0]->bv_len, &psd);
1901 if (!NT_STATUS_IS_OK(status)) {
1902 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
1903 nt_errstr(status)));
1904 TALLOC_FREE(frame);
1905 return;
1908 if (psd) {
1909 ads_disp_sd(ads, talloc_tos(), psd);
1912 TALLOC_FREE(frame);
1916 dump a string result from ldap
1918 static void dump_string(const char *field, char **values)
1920 int i;
1921 for (i=0; values[i]; i++) {
1922 printf("%s: %s\n", field, values[i]);
1927 dump a field from LDAP on stdout
1928 used for debugging
1931 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
1933 const struct {
1934 const char *name;
1935 bool string;
1936 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
1937 } handlers[] = {
1938 {"objectGUID", False, dump_guid},
1939 {"netbootGUID", False, dump_guid},
1940 {"nTSecurityDescriptor", False, dump_sd},
1941 {"dnsRecord", False, dump_binary},
1942 {"objectSid", False, dump_sid},
1943 {"tokenGroups", False, dump_sid},
1944 {"tokenGroupsNoGCAcceptable", False, dump_sid},
1945 {"tokengroupsGlobalandUniversal", False, dump_sid},
1946 {"mS-DS-CreatorSID", False, dump_sid},
1947 {"msExchMailboxGuid", False, dump_guid},
1948 {NULL, True, NULL}
1950 int i;
1952 if (!field) { /* must be end of an entry */
1953 printf("\n");
1954 return False;
1957 for (i=0; handlers[i].name; i++) {
1958 if (StrCaseCmp(handlers[i].name, field) == 0) {
1959 if (!values) /* first time, indicate string or not */
1960 return handlers[i].string;
1961 handlers[i].handler(ads, field, (struct berval **) values);
1962 break;
1965 if (!handlers[i].name) {
1966 if (!values) /* first time, indicate string conversion */
1967 return True;
1968 dump_string(field, (char **)values);
1970 return False;
1974 * Dump a result from LDAP on stdout
1975 * used for debugging
1976 * @param ads connection to ads server
1977 * @param res Results to dump
1980 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
1982 ads_process_results(ads, res, ads_dump_field, NULL);
1986 * Walk through results, calling a function for each entry found.
1987 * The function receives a field name, a berval * array of values,
1988 * and a data area passed through from the start. The function is
1989 * called once with null for field and values at the end of each
1990 * entry.
1991 * @param ads connection to ads server
1992 * @param res Results to process
1993 * @param fn Function for processing each result
1994 * @param data_area user-defined area to pass to function
1996 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
1997 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1998 void *data_area)
2000 LDAPMessage *msg;
2001 TALLOC_CTX *ctx;
2003 if (!(ctx = talloc_init("ads_process_results")))
2004 return;
2006 for (msg = ads_first_entry(ads, res); msg;
2007 msg = ads_next_entry(ads, msg)) {
2008 char *utf8_field;
2009 BerElement *b;
2011 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2012 (LDAPMessage *)msg,&b);
2013 utf8_field;
2014 utf8_field=ldap_next_attribute(ads->ldap.ld,
2015 (LDAPMessage *)msg,b)) {
2016 struct berval **ber_vals;
2017 char **str_vals, **utf8_vals;
2018 char *field;
2019 bool string;
2021 pull_utf8_talloc(ctx, &field, utf8_field);
2022 string = fn(ads, field, NULL, data_area);
2024 if (string) {
2025 utf8_vals = ldap_get_values(ads->ldap.ld,
2026 (LDAPMessage *)msg, field);
2027 str_vals = ads_pull_strvals(ctx,
2028 (const char **) utf8_vals);
2029 fn(ads, field, (void **) str_vals, data_area);
2030 ldap_value_free(utf8_vals);
2031 } else {
2032 ber_vals = ldap_get_values_len(ads->ldap.ld,
2033 (LDAPMessage *)msg, field);
2034 fn(ads, field, (void **) ber_vals, data_area);
2036 ldap_value_free_len(ber_vals);
2038 ldap_memfree(utf8_field);
2040 ber_free(b, 0);
2041 talloc_free_children(ctx);
2042 fn(ads, NULL, NULL, data_area); /* completed an entry */
2045 talloc_destroy(ctx);
2049 * count how many replies are in a LDAPMessage
2050 * @param ads connection to ads server
2051 * @param res Results to count
2052 * @return number of replies
2054 int ads_count_replies(ADS_STRUCT *ads, void *res)
2056 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2060 * pull the first entry from a ADS result
2061 * @param ads connection to ads server
2062 * @param res Results of search
2063 * @return first entry from result
2065 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2067 return ldap_first_entry(ads->ldap.ld, res);
2071 * pull the next entry from a ADS result
2072 * @param ads connection to ads server
2073 * @param res Results of search
2074 * @return next entry from result
2076 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2078 return ldap_next_entry(ads->ldap.ld, res);
2082 * pull a single string from a ADS result
2083 * @param ads connection to ads server
2084 * @param mem_ctx TALLOC_CTX to use for allocating result string
2085 * @param msg Results of search
2086 * @param field Attribute to retrieve
2087 * @return Result string in talloc context
2089 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2090 const char *field)
2092 char **values;
2093 char *ret = NULL;
2094 char *ux_string;
2095 size_t rc;
2097 values = ldap_get_values(ads->ldap.ld, msg, field);
2098 if (!values)
2099 return NULL;
2101 if (values[0]) {
2102 rc = pull_utf8_talloc(mem_ctx, &ux_string,
2103 values[0]);
2104 if (rc != (size_t)-1)
2105 ret = ux_string;
2108 ldap_value_free(values);
2109 return ret;
2113 * pull an array of strings from a ADS result
2114 * @param ads connection to ads server
2115 * @param mem_ctx TALLOC_CTX to use for allocating result string
2116 * @param msg Results of search
2117 * @param field Attribute to retrieve
2118 * @return Result strings in talloc context
2120 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2121 LDAPMessage *msg, const char *field,
2122 size_t *num_values)
2124 char **values;
2125 char **ret = NULL;
2126 int i;
2128 values = ldap_get_values(ads->ldap.ld, msg, field);
2129 if (!values)
2130 return NULL;
2132 *num_values = ldap_count_values(values);
2134 ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
2135 if (!ret) {
2136 ldap_value_free(values);
2137 return NULL;
2140 for (i=0;i<*num_values;i++) {
2141 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
2142 ldap_value_free(values);
2143 return NULL;
2146 ret[i] = NULL;
2148 ldap_value_free(values);
2149 return ret;
2153 * pull an array of strings from a ADS result
2154 * (handle large multivalue attributes with range retrieval)
2155 * @param ads connection to ads server
2156 * @param mem_ctx TALLOC_CTX to use for allocating result string
2157 * @param msg Results of search
2158 * @param field Attribute to retrieve
2159 * @param current_strings strings returned by a previous call to this function
2160 * @param next_attribute The next query should ask for this attribute
2161 * @param num_values How many values did we get this time?
2162 * @param more_values Are there more values to get?
2163 * @return Result strings in talloc context
2165 char **ads_pull_strings_range(ADS_STRUCT *ads,
2166 TALLOC_CTX *mem_ctx,
2167 LDAPMessage *msg, const char *field,
2168 char **current_strings,
2169 const char **next_attribute,
2170 size_t *num_strings,
2171 bool *more_strings)
2173 char *attr;
2174 char *expected_range_attrib, *range_attr;
2175 BerElement *ptr = NULL;
2176 char **strings;
2177 char **new_strings;
2178 size_t num_new_strings;
2179 unsigned long int range_start;
2180 unsigned long int range_end;
2182 /* we might have been given the whole lot anyway */
2183 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2184 *more_strings = False;
2185 return strings;
2188 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2190 /* look for Range result */
2191 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2192 attr;
2193 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2194 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2195 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2196 range_attr = attr;
2197 break;
2199 ldap_memfree(attr);
2201 if (!attr) {
2202 ber_free(ptr, 0);
2203 /* nothing here - this field is just empty */
2204 *more_strings = False;
2205 return NULL;
2208 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2209 &range_start, &range_end) == 2) {
2210 *more_strings = True;
2211 } else {
2212 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2213 &range_start) == 1) {
2214 *more_strings = False;
2215 } else {
2216 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2217 range_attr));
2218 ldap_memfree(range_attr);
2219 *more_strings = False;
2220 return NULL;
2224 if ((*num_strings) != range_start) {
2225 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2226 " - aborting range retreival\n",
2227 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2228 ldap_memfree(range_attr);
2229 *more_strings = False;
2230 return NULL;
2233 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2235 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2236 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2237 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2238 range_attr, (unsigned long int)range_end - range_start + 1,
2239 (unsigned long int)num_new_strings));
2240 ldap_memfree(range_attr);
2241 *more_strings = False;
2242 return NULL;
2245 strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2246 *num_strings + num_new_strings);
2248 if (strings == NULL) {
2249 ldap_memfree(range_attr);
2250 *more_strings = False;
2251 return NULL;
2254 if (new_strings && num_new_strings) {
2255 memcpy(&strings[*num_strings], new_strings,
2256 sizeof(*new_strings) * num_new_strings);
2259 (*num_strings) += num_new_strings;
2261 if (*more_strings) {
2262 *next_attribute = talloc_asprintf(mem_ctx,
2263 "%s;range=%d-*",
2264 field,
2265 (int)*num_strings);
2267 if (!*next_attribute) {
2268 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2269 ldap_memfree(range_attr);
2270 *more_strings = False;
2271 return NULL;
2275 ldap_memfree(range_attr);
2277 return strings;
2281 * pull a single uint32 from a ADS result
2282 * @param ads connection to ads server
2283 * @param msg Results of search
2284 * @param field Attribute to retrieve
2285 * @param v Pointer to int to store result
2286 * @return boolean inidicating success
2288 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2289 uint32 *v)
2291 char **values;
2293 values = ldap_get_values(ads->ldap.ld, msg, field);
2294 if (!values)
2295 return False;
2296 if (!values[0]) {
2297 ldap_value_free(values);
2298 return False;
2301 *v = atoi(values[0]);
2302 ldap_value_free(values);
2303 return True;
2307 * pull a single objectGUID from an ADS result
2308 * @param ads connection to ADS server
2309 * @param msg results of search
2310 * @param guid 37-byte area to receive text guid
2311 * @return boolean indicating success
2313 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2315 char **values;
2316 UUID_FLAT flat_guid;
2318 values = ldap_get_values(ads->ldap.ld, msg, "objectGUID");
2319 if (!values)
2320 return False;
2322 if (values[0]) {
2323 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2324 smb_uuid_unpack(flat_guid, guid);
2325 ldap_value_free(values);
2326 return True;
2328 ldap_value_free(values);
2329 return False;
2335 * pull a single DOM_SID from a ADS result
2336 * @param ads connection to ads server
2337 * @param msg Results of search
2338 * @param field Attribute to retrieve
2339 * @param sid Pointer to sid to store result
2340 * @return boolean inidicating success
2342 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2343 DOM_SID *sid)
2345 struct berval **values;
2346 bool ret = False;
2348 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2350 if (!values)
2351 return False;
2353 if (values[0])
2354 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2356 ldap_value_free_len(values);
2357 return ret;
2361 * pull an array of DOM_SIDs from a ADS result
2362 * @param ads connection to ads server
2363 * @param mem_ctx TALLOC_CTX for allocating sid array
2364 * @param msg Results of search
2365 * @param field Attribute to retrieve
2366 * @param sids pointer to sid array to allocate
2367 * @return the count of SIDs pulled
2369 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2370 LDAPMessage *msg, const char *field, DOM_SID **sids)
2372 struct berval **values;
2373 bool ret;
2374 int count, i;
2376 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2378 if (!values)
2379 return 0;
2381 for (i=0; values[i]; i++)
2382 /* nop */ ;
2384 if (i) {
2385 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2386 if (!(*sids)) {
2387 ldap_value_free_len(values);
2388 return 0;
2390 } else {
2391 (*sids) = NULL;
2394 count = 0;
2395 for (i=0; values[i]; i++) {
2396 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2397 if (ret) {
2398 DEBUG(10, ("pulling SID: %s\n",
2399 sid_string_dbg(&(*sids)[count])));
2400 count++;
2404 ldap_value_free_len(values);
2405 return count;
2409 * pull a SEC_DESC from a ADS result
2410 * @param ads connection to ads server
2411 * @param mem_ctx TALLOC_CTX for allocating sid array
2412 * @param msg Results of search
2413 * @param field Attribute to retrieve
2414 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2415 * @return boolean inidicating success
2417 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2418 LDAPMessage *msg, const char *field, SEC_DESC **sd)
2420 struct berval **values;
2421 bool ret = true;
2423 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2425 if (!values) return false;
2427 if (values[0]) {
2428 NTSTATUS status;
2429 status = unmarshall_sec_desc(mem_ctx,
2430 (uint8 *)values[0]->bv_val,
2431 values[0]->bv_len, sd);
2432 if (!NT_STATUS_IS_OK(status)) {
2433 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2434 nt_errstr(status)));
2435 ret = false;
2439 ldap_value_free_len(values);
2440 return ret;
2444 * in order to support usernames longer than 21 characters we need to
2445 * use both the sAMAccountName and the userPrincipalName attributes
2446 * It seems that not all users have the userPrincipalName attribute set
2448 * @param ads connection to ads server
2449 * @param mem_ctx TALLOC_CTX for allocating sid array
2450 * @param msg Results of search
2451 * @return the username
2453 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2454 LDAPMessage *msg)
2456 #if 0 /* JERRY */
2457 char *ret, *p;
2459 /* lookup_name() only works on the sAMAccountName to
2460 returning the username portion of userPrincipalName
2461 breaks winbindd_getpwnam() */
2463 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2464 if (ret && (p = strchr_m(ret, '@'))) {
2465 *p = 0;
2466 return ret;
2468 #endif
2469 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2474 * find the update serial number - this is the core of the ldap cache
2475 * @param ads connection to ads server
2476 * @param ads connection to ADS server
2477 * @param usn Pointer to retrieved update serial number
2478 * @return status of search
2480 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2482 const char *attrs[] = {"highestCommittedUSN", NULL};
2483 ADS_STATUS status;
2484 LDAPMessage *res;
2486 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2487 if (!ADS_ERR_OK(status))
2488 return status;
2490 if (ads_count_replies(ads, res) != 1) {
2491 ads_msgfree(ads, res);
2492 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2495 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2496 ads_msgfree(ads, res);
2497 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2500 ads_msgfree(ads, res);
2501 return ADS_SUCCESS;
2504 /* parse a ADS timestring - typical string is
2505 '20020917091222.0Z0' which means 09:12.22 17th September
2506 2002, timezone 0 */
2507 static time_t ads_parse_time(const char *str)
2509 struct tm tm;
2511 ZERO_STRUCT(tm);
2513 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2514 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2515 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2516 return 0;
2518 tm.tm_year -= 1900;
2519 tm.tm_mon -= 1;
2521 return timegm(&tm);
2524 /********************************************************************
2525 ********************************************************************/
2527 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2529 const char *attrs[] = {"currentTime", NULL};
2530 ADS_STATUS status;
2531 LDAPMessage *res;
2532 char *timestr;
2533 TALLOC_CTX *ctx;
2534 ADS_STRUCT *ads_s = ads;
2536 if (!(ctx = talloc_init("ads_current_time"))) {
2537 return ADS_ERROR(LDAP_NO_MEMORY);
2540 /* establish a new ldap tcp session if necessary */
2542 if ( !ads->ldap.ld ) {
2543 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2544 ads->server.ldap_server )) == NULL )
2546 goto done;
2548 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2549 status = ads_connect( ads_s );
2550 if ( !ADS_ERR_OK(status))
2551 goto done;
2554 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2555 if (!ADS_ERR_OK(status)) {
2556 goto done;
2559 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2560 if (!timestr) {
2561 ads_msgfree(ads_s, res);
2562 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2563 goto done;
2566 /* but save the time and offset in the original ADS_STRUCT */
2568 ads->config.current_time = ads_parse_time(timestr);
2570 if (ads->config.current_time != 0) {
2571 ads->auth.time_offset = ads->config.current_time - time(NULL);
2572 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2575 ads_msgfree(ads, res);
2577 status = ADS_SUCCESS;
2579 done:
2580 /* free any temporary ads connections */
2581 if ( ads_s != ads ) {
2582 ads_destroy( &ads_s );
2584 talloc_destroy(ctx);
2586 return status;
2589 /********************************************************************
2590 ********************************************************************/
2592 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2594 const char *attrs[] = {"domainFunctionality", NULL};
2595 ADS_STATUS status;
2596 LDAPMessage *res;
2597 ADS_STRUCT *ads_s = ads;
2599 *val = DS_DOMAIN_FUNCTION_2000;
2601 /* establish a new ldap tcp session if necessary */
2603 if ( !ads->ldap.ld ) {
2604 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2605 ads->server.ldap_server )) == NULL )
2607 goto done;
2609 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2610 status = ads_connect( ads_s );
2611 if ( !ADS_ERR_OK(status))
2612 goto done;
2615 /* If the attribute does not exist assume it is a Windows 2000
2616 functional domain */
2618 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2619 if (!ADS_ERR_OK(status)) {
2620 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2621 status = ADS_SUCCESS;
2623 goto done;
2626 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2627 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2629 DEBUG(3,("ads_domain_func_level: %d\n", *val));
2632 ads_msgfree(ads, res);
2634 done:
2635 /* free any temporary ads connections */
2636 if ( ads_s != ads ) {
2637 ads_destroy( &ads_s );
2640 return status;
2644 * find the domain sid for our domain
2645 * @param ads connection to ads server
2646 * @param sid Pointer to domain sid
2647 * @return status of search
2649 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2651 const char *attrs[] = {"objectSid", NULL};
2652 LDAPMessage *res;
2653 ADS_STATUS rc;
2655 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2656 attrs, &res);
2657 if (!ADS_ERR_OK(rc)) return rc;
2658 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2659 ads_msgfree(ads, res);
2660 return ADS_ERROR_SYSTEM(ENOENT);
2662 ads_msgfree(ads, res);
2664 return ADS_SUCCESS;
2668 * find our site name
2669 * @param ads connection to ads server
2670 * @param mem_ctx Pointer to talloc context
2671 * @param site_name Pointer to the sitename
2672 * @return status of search
2674 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2676 ADS_STATUS status;
2677 LDAPMessage *res;
2678 const char *dn, *service_name;
2679 const char *attrs[] = { "dsServiceName", NULL };
2681 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2682 if (!ADS_ERR_OK(status)) {
2683 return status;
2686 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2687 if (service_name == NULL) {
2688 ads_msgfree(ads, res);
2689 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2692 ads_msgfree(ads, res);
2694 /* go up three levels */
2695 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2696 if (dn == NULL) {
2697 return ADS_ERROR(LDAP_NO_MEMORY);
2700 *site_name = talloc_strdup(mem_ctx, dn);
2701 if (*site_name == NULL) {
2702 return ADS_ERROR(LDAP_NO_MEMORY);
2705 return status;
2707 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2712 * find the site dn where a machine resides
2713 * @param ads connection to ads server
2714 * @param mem_ctx Pointer to talloc context
2715 * @param computer_name name of the machine
2716 * @param site_name Pointer to the sitename
2717 * @return status of search
2719 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
2721 ADS_STATUS status;
2722 LDAPMessage *res;
2723 const char *parent, *filter;
2724 char *config_context = NULL;
2725 char *dn;
2727 /* shortcut a query */
2728 if (strequal(computer_name, ads->config.ldap_server_name)) {
2729 return ads_site_dn(ads, mem_ctx, site_dn);
2732 status = ads_config_path(ads, mem_ctx, &config_context);
2733 if (!ADS_ERR_OK(status)) {
2734 return status;
2737 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
2738 if (filter == NULL) {
2739 return ADS_ERROR(LDAP_NO_MEMORY);
2742 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
2743 filter, NULL, &res);
2744 if (!ADS_ERR_OK(status)) {
2745 return status;
2748 if (ads_count_replies(ads, res) != 1) {
2749 ads_msgfree(ads, res);
2750 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2753 dn = ads_get_dn(ads, res);
2754 if (dn == NULL) {
2755 ads_msgfree(ads, res);
2756 return ADS_ERROR(LDAP_NO_MEMORY);
2759 /* go up three levels */
2760 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
2761 if (parent == NULL) {
2762 ads_msgfree(ads, res);
2763 ads_memfree(ads, dn);
2764 return ADS_ERROR(LDAP_NO_MEMORY);
2767 *site_dn = talloc_strdup(mem_ctx, parent);
2768 if (*site_dn == NULL) {
2769 ads_msgfree(ads, res);
2770 ads_memfree(ads, dn);
2771 return ADS_ERROR(LDAP_NO_MEMORY);
2774 ads_memfree(ads, dn);
2775 ads_msgfree(ads, res);
2777 return status;
2781 * get the upn suffixes for a domain
2782 * @param ads connection to ads server
2783 * @param mem_ctx Pointer to talloc context
2784 * @param suffixes Pointer to an array of suffixes
2785 * @param num_suffixes Pointer to the number of suffixes
2786 * @return status of search
2788 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
2790 ADS_STATUS status;
2791 LDAPMessage *res;
2792 const char *base;
2793 char *config_context = NULL;
2794 const char *attrs[] = { "uPNSuffixes", NULL };
2796 status = ads_config_path(ads, mem_ctx, &config_context);
2797 if (!ADS_ERR_OK(status)) {
2798 return status;
2801 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
2802 if (base == NULL) {
2803 return ADS_ERROR(LDAP_NO_MEMORY);
2806 status = ads_search_dn(ads, &res, base, attrs);
2807 if (!ADS_ERR_OK(status)) {
2808 return status;
2811 if (ads_count_replies(ads, res) != 1) {
2812 ads_msgfree(ads, res);
2813 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2816 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
2817 if ((*suffixes) == NULL) {
2818 ads_msgfree(ads, res);
2819 return ADS_ERROR(LDAP_NO_MEMORY);
2822 ads_msgfree(ads, res);
2824 return status;
2828 * get the joinable ous for a domain
2829 * @param ads connection to ads server
2830 * @param mem_ctx Pointer to talloc context
2831 * @param ous Pointer to an array of ous
2832 * @param num_ous Pointer to the number of ous
2833 * @return status of search
2835 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
2836 TALLOC_CTX *mem_ctx,
2837 char ***ous,
2838 size_t *num_ous)
2840 ADS_STATUS status;
2841 LDAPMessage *res = NULL;
2842 LDAPMessage *msg = NULL;
2843 const char *attrs[] = { "dn", NULL };
2844 int count = 0;
2846 status = ads_search(ads, &res,
2847 "(|(objectClass=domain)(objectclass=organizationalUnit))",
2848 attrs);
2849 if (!ADS_ERR_OK(status)) {
2850 return status;
2853 count = ads_count_replies(ads, res);
2854 if (count < 1) {
2855 ads_msgfree(ads, res);
2856 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2859 for (msg = ads_first_entry(ads, res); msg;
2860 msg = ads_next_entry(ads, msg)) {
2862 char *dn = NULL;
2864 dn = ads_get_dn(ads, msg);
2865 if (!dn) {
2866 ads_msgfree(ads, res);
2867 return ADS_ERROR(LDAP_NO_MEMORY);
2870 if (!add_string_to_array(mem_ctx, dn,
2871 (const char ***)ous,
2872 (int *)num_ous)) {
2873 ads_memfree(ads, dn);
2874 ads_msgfree(ads, res);
2875 return ADS_ERROR(LDAP_NO_MEMORY);
2878 ads_memfree(ads, dn);
2881 ads_msgfree(ads, res);
2883 return status;
2888 * pull a DOM_SID from an extended dn string
2889 * @param mem_ctx TALLOC_CTX
2890 * @param extended_dn string
2891 * @param flags string type of extended_dn
2892 * @param sid pointer to a DOM_SID
2893 * @return boolean inidicating success
2895 bool ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
2896 const char *extended_dn,
2897 enum ads_extended_dn_flags flags,
2898 DOM_SID *sid)
2900 char *p, *q, *dn;
2902 if (!extended_dn) {
2903 return False;
2906 /* otherwise extended_dn gets stripped off */
2907 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
2908 return False;
2911 * ADS_EXTENDED_DN_HEX_STRING:
2912 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
2914 * ADS_EXTENDED_DN_STRING (only with w2k3):
2915 <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
2918 p = strchr(dn, ';');
2919 if (!p) {
2920 return False;
2923 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
2924 return False;
2927 p += strlen(";<SID=");
2929 q = strchr(p, '>');
2930 if (!q) {
2931 return False;
2934 *q = '\0';
2936 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
2938 switch (flags) {
2940 case ADS_EXTENDED_DN_STRING:
2941 if (!string_to_sid(sid, p)) {
2942 return False;
2944 break;
2945 case ADS_EXTENDED_DN_HEX_STRING: {
2946 fstring buf;
2947 size_t buf_len;
2949 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
2950 if (buf_len == 0) {
2951 return False;
2954 if (!sid_parse(buf, buf_len, sid)) {
2955 DEBUG(10,("failed to parse sid\n"));
2956 return False;
2958 break;
2960 default:
2961 DEBUG(10,("unknown extended dn format\n"));
2962 return False;
2965 return True;
2969 * pull an array of DOM_SIDs from a ADS result
2970 * @param ads connection to ads server
2971 * @param mem_ctx TALLOC_CTX for allocating sid array
2972 * @param msg Results of search
2973 * @param field Attribute to retrieve
2974 * @param flags string type of extended_dn
2975 * @param sids pointer to sid array to allocate
2976 * @return the count of SIDs pulled
2978 int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads,
2979 TALLOC_CTX *mem_ctx,
2980 LDAPMessage *msg,
2981 const char *field,
2982 enum ads_extended_dn_flags flags,
2983 DOM_SID **sids)
2985 int i;
2986 size_t dn_count;
2987 char **dn_strings;
2989 if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field,
2990 &dn_count)) == NULL) {
2991 return 0;
2994 (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, dn_count + 1);
2995 if (!(*sids)) {
2996 TALLOC_FREE(dn_strings);
2997 return 0;
3000 for (i=0; i<dn_count; i++) {
3002 if (!ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i],
3003 flags, &(*sids)[i])) {
3004 TALLOC_FREE(*sids);
3005 TALLOC_FREE(dn_strings);
3006 return 0;
3010 TALLOC_FREE(dn_strings);
3012 return dn_count;
3015 /********************************************************************
3016 ********************************************************************/
3018 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3020 LDAPMessage *res = NULL;
3021 ADS_STATUS status;
3022 int count = 0;
3023 char *name = NULL;
3025 status = ads_find_machine_acct(ads, &res, global_myname());
3026 if (!ADS_ERR_OK(status)) {
3027 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3028 global_myname()));
3029 goto out;
3032 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3033 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3034 goto out;
3037 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3038 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3041 out:
3042 ads_msgfree(ads, res);
3044 return name;
3047 /********************************************************************
3048 ********************************************************************/
3050 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3052 LDAPMessage *res = NULL;
3053 ADS_STATUS status;
3054 int count = 0;
3055 char *name = NULL;
3057 status = ads_find_machine_acct(ads, &res, machine_name);
3058 if (!ADS_ERR_OK(status)) {
3059 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3060 global_myname()));
3061 goto out;
3064 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3065 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3066 goto out;
3069 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3070 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3073 out:
3074 ads_msgfree(ads, res);
3076 return name;
3079 /********************************************************************
3080 ********************************************************************/
3082 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3084 LDAPMessage *res = NULL;
3085 ADS_STATUS status;
3086 int count = 0;
3087 char *name = NULL;
3089 status = ads_find_machine_acct(ads, &res, global_myname());
3090 if (!ADS_ERR_OK(status)) {
3091 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3092 global_myname()));
3093 goto out;
3096 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3097 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3098 goto out;
3101 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3102 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3105 out:
3106 ads_msgfree(ads, res);
3108 return name;
3111 #if 0
3113 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3116 * Join a machine to a realm
3117 * Creates the machine account and sets the machine password
3118 * @param ads connection to ads server
3119 * @param machine name of host to add
3120 * @param org_unit Organizational unit to place machine in
3121 * @return status of join
3123 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3124 uint32 account_type, const char *org_unit)
3126 ADS_STATUS status;
3127 LDAPMessage *res = NULL;
3128 char *machine;
3130 /* machine name must be lowercase */
3131 machine = SMB_STRDUP(machine_name);
3132 strlower_m(machine);
3135 status = ads_find_machine_acct(ads, (void **)&res, machine);
3136 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3137 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3138 status = ads_leave_realm(ads, machine);
3139 if (!ADS_ERR_OK(status)) {
3140 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3141 machine, ads->config.realm));
3142 return status;
3146 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3147 if (!ADS_ERR_OK(status)) {
3148 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3149 SAFE_FREE(machine);
3150 return status;
3153 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3154 if (!ADS_ERR_OK(status)) {
3155 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3156 SAFE_FREE(machine);
3157 return status;
3160 SAFE_FREE(machine);
3161 ads_msgfree(ads, res);
3163 return status;
3165 #endif
3168 * Delete a machine from the realm
3169 * @param ads connection to ads server
3170 * @param hostname Machine to remove
3171 * @return status of delete
3173 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3175 ADS_STATUS status;
3176 void *msg;
3177 LDAPMessage *res;
3178 char *hostnameDN, *host;
3179 int rc;
3180 LDAPControl ldap_control;
3181 LDAPControl * pldap_control[2] = {NULL, NULL};
3183 pldap_control[0] = &ldap_control;
3184 memset(&ldap_control, 0, sizeof(LDAPControl));
3185 ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
3187 /* hostname must be lowercase */
3188 host = SMB_STRDUP(hostname);
3189 strlower_m(host);
3191 status = ads_find_machine_acct(ads, &res, host);
3192 if (!ADS_ERR_OK(status)) {
3193 DEBUG(0, ("Host account for %s does not exist.\n", host));
3194 SAFE_FREE(host);
3195 return status;
3198 msg = ads_first_entry(ads, res);
3199 if (!msg) {
3200 SAFE_FREE(host);
3201 return ADS_ERROR_SYSTEM(ENOENT);
3204 hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
3206 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3207 if (rc) {
3208 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3209 }else {
3210 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3213 if (rc != LDAP_SUCCESS) {
3214 const char *attrs[] = { "cn", NULL };
3215 LDAPMessage *msg_sub;
3217 /* we only search with scope ONE, we do not expect any further
3218 * objects to be created deeper */
3220 status = ads_do_search_retry(ads, hostnameDN,
3221 LDAP_SCOPE_ONELEVEL,
3222 "(objectclass=*)", attrs, &res);
3224 if (!ADS_ERR_OK(status)) {
3225 SAFE_FREE(host);
3226 ads_memfree(ads, hostnameDN);
3227 return status;
3230 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3231 msg_sub = ads_next_entry(ads, msg_sub)) {
3233 char *dn = NULL;
3235 if ((dn = ads_get_dn(ads, msg_sub)) == NULL) {
3236 SAFE_FREE(host);
3237 ads_memfree(ads, hostnameDN);
3238 return ADS_ERROR(LDAP_NO_MEMORY);
3241 status = ads_del_dn(ads, dn);
3242 if (!ADS_ERR_OK(status)) {
3243 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3244 SAFE_FREE(host);
3245 ads_memfree(ads, dn);
3246 ads_memfree(ads, hostnameDN);
3247 return status;
3250 ads_memfree(ads, dn);
3253 /* there should be no subordinate objects anymore */
3254 status = ads_do_search_retry(ads, hostnameDN,
3255 LDAP_SCOPE_ONELEVEL,
3256 "(objectclass=*)", attrs, &res);
3258 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3259 SAFE_FREE(host);
3260 ads_memfree(ads, hostnameDN);
3261 return status;
3264 /* delete hostnameDN now */
3265 status = ads_del_dn(ads, hostnameDN);
3266 if (!ADS_ERR_OK(status)) {
3267 SAFE_FREE(host);
3268 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3269 ads_memfree(ads, hostnameDN);
3270 return status;
3274 ads_memfree(ads, hostnameDN);
3276 status = ads_find_machine_acct(ads, &res, host);
3277 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3278 DEBUG(3, ("Failed to remove host account.\n"));
3279 SAFE_FREE(host);
3280 return status;
3283 SAFE_FREE(host);
3284 return status;
3288 * pull all token-sids from an LDAP dn
3289 * @param ads connection to ads server
3290 * @param mem_ctx TALLOC_CTX for allocating sid array
3291 * @param dn of LDAP object
3292 * @param user_sid pointer to DOM_SID (objectSid)
3293 * @param primary_group_sid pointer to DOM_SID (self composed)
3294 * @param sids pointer to sid array to allocate
3295 * @param num_sids counter of SIDs pulled
3296 * @return status of token query
3298 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3299 TALLOC_CTX *mem_ctx,
3300 const char *dn,
3301 DOM_SID *user_sid,
3302 DOM_SID *primary_group_sid,
3303 DOM_SID **sids,
3304 size_t *num_sids)
3306 ADS_STATUS status;
3307 LDAPMessage *res = NULL;
3308 int count = 0;
3309 size_t tmp_num_sids;
3310 DOM_SID *tmp_sids;
3311 DOM_SID tmp_user_sid;
3312 DOM_SID tmp_primary_group_sid;
3313 uint32 pgid;
3314 const char *attrs[] = {
3315 "objectSid",
3316 "tokenGroups",
3317 "primaryGroupID",
3318 NULL
3321 status = ads_search_retry_dn(ads, &res, dn, attrs);
3322 if (!ADS_ERR_OK(status)) {
3323 return status;
3326 count = ads_count_replies(ads, res);
3327 if (count != 1) {
3328 ads_msgfree(ads, res);
3329 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3332 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3333 ads_msgfree(ads, res);
3334 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3337 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3338 ads_msgfree(ads, res);
3339 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3343 /* hack to compose the primary group sid without knowing the
3344 * domsid */
3346 DOM_SID domsid;
3347 uint32 dummy_rid;
3349 sid_copy(&domsid, &tmp_user_sid);
3351 if (!sid_split_rid(&domsid, &dummy_rid)) {
3352 ads_msgfree(ads, res);
3353 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3356 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3357 ads_msgfree(ads, res);
3358 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3362 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3364 if (tmp_num_sids == 0 || !tmp_sids) {
3365 ads_msgfree(ads, res);
3366 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3369 if (num_sids) {
3370 *num_sids = tmp_num_sids;
3373 if (sids) {
3374 *sids = tmp_sids;
3377 if (user_sid) {
3378 *user_sid = tmp_user_sid;
3381 if (primary_group_sid) {
3382 *primary_group_sid = tmp_primary_group_sid;
3385 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3387 ads_msgfree(ads, res);
3388 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3392 * Find a sAMAccoutName in LDAP
3393 * @param ads connection to ads server
3394 * @param mem_ctx TALLOC_CTX for allocating sid array
3395 * @param samaccountname to search
3396 * @param uac_ret uint32 pointer userAccountControl attribute value
3397 * @param dn_ret pointer to dn
3398 * @return status of token query
3400 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3401 TALLOC_CTX *mem_ctx,
3402 const char *samaccountname,
3403 uint32 *uac_ret,
3404 const char **dn_ret)
3406 ADS_STATUS status;
3407 const char *attrs[] = { "userAccountControl", NULL };
3408 const char *filter;
3409 LDAPMessage *res = NULL;
3410 char *dn = NULL;
3411 uint32 uac = 0;
3413 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3414 samaccountname);
3415 if (filter == NULL) {
3416 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3417 goto out;
3420 status = ads_do_search_all(ads, ads->config.bind_path,
3421 LDAP_SCOPE_SUBTREE,
3422 filter, attrs, &res);
3424 if (!ADS_ERR_OK(status)) {
3425 goto out;
3428 if (ads_count_replies(ads, res) != 1) {
3429 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3430 goto out;
3433 dn = ads_get_dn(ads, res);
3434 if (dn == NULL) {
3435 status = ADS_ERROR(LDAP_NO_MEMORY);
3436 goto out;
3439 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3440 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3441 goto out;
3444 if (uac_ret) {
3445 *uac_ret = uac;
3448 if (dn_ret) {
3449 *dn_ret = talloc_strdup(mem_ctx, dn);
3450 if (!*dn_ret) {
3451 status = ADS_ERROR(LDAP_NO_MEMORY);
3452 goto out;
3455 out:
3456 ads_memfree(ads, dn);
3457 ads_msgfree(ads, res);
3459 return status;
3463 * find our configuration path
3464 * @param ads connection to ads server
3465 * @param mem_ctx Pointer to talloc context
3466 * @param config_path Pointer to the config path
3467 * @return status of search
3469 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3470 TALLOC_CTX *mem_ctx,
3471 char **config_path)
3473 ADS_STATUS status;
3474 LDAPMessage *res = NULL;
3475 const char *config_context = NULL;
3476 const char *attrs[] = { "configurationNamingContext", NULL };
3478 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3479 "(objectclass=*)", attrs, &res);
3480 if (!ADS_ERR_OK(status)) {
3481 return status;
3484 config_context = ads_pull_string(ads, mem_ctx, res,
3485 "configurationNamingContext");
3486 ads_msgfree(ads, res);
3487 if (!config_context) {
3488 return ADS_ERROR(LDAP_NO_MEMORY);
3491 if (config_path) {
3492 *config_path = talloc_strdup(mem_ctx, config_context);
3493 if (!*config_path) {
3494 return ADS_ERROR(LDAP_NO_MEMORY);
3498 return ADS_ERROR(LDAP_SUCCESS);
3502 * find the displayName of an extended right
3503 * @param ads connection to ads server
3504 * @param config_path The config path
3505 * @param mem_ctx Pointer to talloc context
3506 * @param GUID struct of the rightsGUID
3507 * @return status of search
3509 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3510 const char *config_path,
3511 TALLOC_CTX *mem_ctx,
3512 const struct GUID *rights_guid)
3514 ADS_STATUS rc;
3515 LDAPMessage *res = NULL;
3516 char *expr = NULL;
3517 const char *attrs[] = { "displayName", NULL };
3518 const char *result = NULL;
3519 const char *path;
3521 if (!ads || !mem_ctx || !rights_guid) {
3522 goto done;
3525 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3526 smb_uuid_string(mem_ctx, *rights_guid));
3527 if (!expr) {
3528 goto done;
3531 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3532 if (!path) {
3533 goto done;
3536 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3537 expr, attrs, &res);
3538 if (!ADS_ERR_OK(rc)) {
3539 goto done;
3542 if (ads_count_replies(ads, res) != 1) {
3543 goto done;
3546 result = ads_pull_string(ads, mem_ctx, res, "displayName");
3548 done:
3549 ads_msgfree(ads, res);
3550 return result;
3554 #endif