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/>.
25 #include "lib/ldb/include/includes.h"
31 * @brief basic ldap client-side routines for ads server communications
33 * The routines contained here should do the necessary ldap calls for
36 * Important note: attribute names passed into ads_ routines must
37 * already be in UTF-8 format. We do not convert them because in almost
38 * all cases, they are just ascii (which is represented with the same
39 * codepoints in UTF-8). This may have to change at some point
43 #define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"
45 static SIG_ATOMIC_T gotalarm;
47 /***************************************************************
48 Signal function to tell us we timed out.
49 ****************************************************************/
51 static void gotalarm_sig(void)
56 LDAP *ldap_open_with_timeout(const char *server, int port, unsigned int to)
61 DEBUG(10, ("Opening connection to LDAP server '%s:%d', timeout "
62 "%u seconds\n", server, port, to));
66 CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
68 /* End setup timeout. */
70 ldp = ldap_open(server, port);
73 DEBUG(2,("Could not open connection to LDAP server %s:%d: %s\n",
74 server, port, strerror(errno)));
76 DEBUG(10, ("Connected to LDAP server '%s:%d'\n", server, port));
79 /* Teardown timeout. */
80 CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
86 static int ldap_search_with_timeout(LDAP *ld,
87 LDAP_CONST char *base,
89 LDAP_CONST char *filter,
97 struct timeval timeout;
100 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
101 timeout.tv_sec = lp_ldap_timeout();
104 /* Setup alarm timeout.... Do we need both of these ? JRA. */
106 CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
107 alarm(lp_ldap_timeout());
108 /* End setup timeout. */
110 result = ldap_search_ext_s(ld, base, scope, filter, attrs,
111 attrsonly, sctrls, cctrls, &timeout,
114 /* Teardown timeout. */
115 CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
119 return LDAP_TIMELIMIT_EXCEEDED;
124 /**********************************************
125 Do client and server sitename match ?
126 **********************************************/
128 bool ads_sitename_match(ADS_STRUCT *ads)
130 if (ads->config.server_site_name == NULL &&
131 ads->config.client_site_name == NULL ) {
132 DEBUG(10,("ads_sitename_match: both null\n"));
135 if (ads->config.server_site_name &&
136 ads->config.client_site_name &&
137 strequal(ads->config.server_site_name,
138 ads->config.client_site_name)) {
139 DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
142 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
143 ads->config.server_site_name ? ads->config.server_site_name : "NULL",
144 ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
148 /**********************************************
149 Is this the closest DC ?
150 **********************************************/
152 bool ads_closest_dc(ADS_STRUCT *ads)
154 if (ads->config.flags & NBT_SERVER_CLOSEST) {
155 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag set\n"));
159 /* not sure if this can ever happen */
160 if (ads_sitename_match(ads)) {
161 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag not set but sites match\n"));
165 if (ads->config.client_site_name == NULL) {
166 DEBUG(10,("ads_closest_dc: client belongs to no site\n"));
170 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
171 ads->config.ldap_server_name));
178 try a connection to a given ldap server, returning True and setting the servers IP
179 in the ads struct if successful
181 static bool ads_try_connect(ADS_STRUCT *ads, const char *server, bool gc)
184 struct NETLOGON_SAM_LOGON_RESPONSE_EX cldap_reply;
185 TALLOC_CTX *mem_ctx = NULL;
188 if (!server || !*server) {
192 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
193 server, ads->server.realm));
195 mem_ctx = talloc_init("ads_try_connect");
197 DEBUG(0,("out of memory\n"));
201 /* this copes with inet_ntoa brokenness */
203 srv = SMB_STRDUP(server);
205 ZERO_STRUCT( cldap_reply );
207 if ( !ads_cldap_netlogon_5(mem_ctx, srv, ads->server.realm, &cldap_reply ) ) {
208 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", srv));
213 /* Check the CLDAP reply flags */
215 if ( !(cldap_reply.server_type & NBT_SERVER_LDAP) ) {
216 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
222 /* Fill in the ads->config values */
224 SAFE_FREE(ads->config.realm);
225 SAFE_FREE(ads->config.bind_path);
226 SAFE_FREE(ads->config.ldap_server_name);
227 SAFE_FREE(ads->config.server_site_name);
228 SAFE_FREE(ads->config.client_site_name);
229 SAFE_FREE(ads->server.workgroup);
231 ads->config.flags = cldap_reply.server_type;
232 ads->config.ldap_server_name = SMB_STRDUP(cldap_reply.pdc_dns_name);
233 ads->config.realm = SMB_STRDUP(cldap_reply.dns_domain);
234 strupper_m(ads->config.realm);
235 ads->config.bind_path = ads_build_dn(ads->config.realm);
236 if (*cldap_reply.server_site) {
237 ads->config.server_site_name =
238 SMB_STRDUP(cldap_reply.server_site);
240 if (*cldap_reply.client_site) {
241 ads->config.client_site_name =
242 SMB_STRDUP(cldap_reply.client_site);
244 ads->server.workgroup = SMB_STRDUP(cldap_reply.domain);
246 ads->ldap.port = gc ? LDAP_GC_PORT : LDAP_PORT;
247 if (!interpret_string_addr(&ads->ldap.ss, srv, 0)) {
248 DEBUG(1,("ads_try_connect: unable to convert %s "
255 /* Store our site name. */
256 sitename_store( cldap_reply.domain, cldap_reply.client_site);
257 sitename_store( cldap_reply.dns_domain, cldap_reply.client_site);
262 TALLOC_FREE(mem_ctx);
267 /**********************************************************************
268 Try to find an AD dc using our internal name resolution routines
269 Try the realm first and then then workgroup name if netbios is not
271 **********************************************************************/
273 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
275 const char *c_domain;
278 struct ip_service *ip_list;
281 bool got_realm = False;
282 bool use_own_domain = False;
284 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
286 /* if the realm and workgroup are both empty, assume they are ours */
289 c_realm = ads->server.realm;
291 if ( !c_realm || !*c_realm ) {
292 /* special case where no realm and no workgroup means our own */
293 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
294 use_own_domain = True;
295 c_realm = lp_realm();
299 if (c_realm && *c_realm)
302 /* we need to try once with the realm name and fallback to the
303 netbios domain name if we fail (if netbios has not been disabled */
305 if ( !got_realm && !lp_disable_netbios() ) {
306 c_realm = ads->server.workgroup;
307 if (!c_realm || !*c_realm) {
308 if ( use_own_domain )
309 c_realm = lp_workgroup();
313 if ( !c_realm || !*c_realm ) {
314 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
315 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
318 if ( use_own_domain ) {
319 c_domain = lp_workgroup();
321 c_domain = ads->server.workgroup;
328 * In case of LDAP we use get_dc_name() as that
329 * creates the custom krb5.conf file
331 if (!(ads->auth.flags & ADS_AUTH_NO_BIND)) {
333 struct sockaddr_storage ip_out;
335 DEBUG(6,("ads_find_dc: (ldap) looking for %s '%s'\n",
336 (got_realm ? "realm" : "domain"), realm));
338 if (get_dc_name(domain, realm, srv_name, &ip_out)) {
340 * we call ads_try_connect() to fill in the
341 * ads->config details
343 if (ads_try_connect(ads, srv_name, false)) {
348 return NT_STATUS_NO_LOGON_SERVERS;
351 sitename = sitename_fetch(realm);
355 DEBUG(6,("ads_find_dc: (cldap) looking for %s '%s'\n",
356 (got_realm ? "realm" : "domain"), realm));
358 status = get_sorted_dc_list(realm, sitename, &ip_list, &count, got_realm);
359 if (!NT_STATUS_IS_OK(status)) {
360 /* fall back to netbios if we can */
361 if ( got_realm && !lp_disable_netbios() ) {
370 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
371 for ( i=0; i<count; i++ ) {
372 char server[INET6_ADDRSTRLEN];
374 print_sockaddr(server, sizeof(server), &ip_list[i].ss);
376 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
380 /* realm in this case is a workgroup name. We need
381 to ignore any IP addresses in the negative connection
382 cache that match ip addresses returned in the ad realm
383 case. It sucks that I have to reproduce the logic above... */
384 c_realm = ads->server.realm;
385 if ( !c_realm || !*c_realm ) {
386 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
387 c_realm = lp_realm();
390 if (c_realm && *c_realm &&
391 !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm, server))) {
392 /* Ensure we add the workgroup name for this
393 IP address as negative too. */
394 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
399 if ( ads_try_connect(ads, server, false) ) {
405 /* keep track of failures */
406 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
411 /* In case we failed to contact one of our closest DC on our site we
412 * need to try to find another DC, retry with a site-less SRV DNS query
416 DEBUG(1,("ads_find_dc: failed to find a valid DC on our site (%s), "
417 "trying to find another DC\n", sitename));
419 namecache_delete(realm, 0x1C);
423 return NT_STATUS_NO_LOGON_SERVERS;
426 /*********************************************************************
427 *********************************************************************/
429 static NTSTATUS ads_lookup_site(void)
431 ADS_STRUCT *ads = NULL;
432 ADS_STATUS ads_status;
433 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
435 ads = ads_init(lp_realm(), NULL, NULL);
437 return NT_STATUS_NO_MEMORY;
440 /* The NO_BIND here will find a DC and set the client site
441 but not establish the TCP connection */
443 ads->auth.flags = ADS_AUTH_NO_BIND;
444 ads_status = ads_connect(ads);
445 if (!ADS_ERR_OK(ads_status)) {
446 DEBUG(4, ("ads_lookup_site: ads_connect to our realm failed! (%s)\n",
447 ads_errstr(ads_status)));
449 nt_status = ads_ntstatus(ads_status);
458 /*********************************************************************
459 *********************************************************************/
461 static const char* host_dns_domain(const char *fqdn)
463 const char *p = fqdn;
465 /* go to next char following '.' */
467 if ((p = strchr_m(fqdn, '.')) != NULL) {
476 * Connect to the Global Catalog server
477 * @param ads Pointer to an existing ADS_STRUCT
478 * @return status of connection
480 * Simple wrapper around ads_connect() that fills in the
481 * GC ldap server information
484 ADS_STATUS ads_connect_gc(ADS_STRUCT *ads)
486 TALLOC_CTX *frame = talloc_stackframe();
487 struct dns_rr_srv *gcs_list;
489 char *realm = ads->server.realm;
490 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
491 ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
494 char *sitename = NULL;
499 if ((sitename = sitename_fetch(realm)) == NULL) {
501 sitename = sitename_fetch(realm);
505 /* We try once with a sitename and once without
506 (unless we don't have a sitename and then we're
509 if (sitename == NULL)
512 nt_status = ads_dns_query_gcs(frame, realm, sitename,
513 &gcs_list, &num_gcs);
517 if (!NT_STATUS_IS_OK(nt_status)) {
518 ads_status = ADS_ERROR_NT(nt_status);
522 /* Loop until we get a successful connection or have gone
523 through them all. When connecting a GC server, make sure that
524 the realm is the server's DNS name and not the forest root */
526 for (i=0; i<num_gcs; i++) {
527 ads->server.gc = true;
528 ads->server.ldap_server = SMB_STRDUP(gcs_list[i].hostname);
529 ads->server.realm = SMB_STRDUP(host_dns_domain(ads->server.ldap_server));
530 ads_status = ads_connect(ads);
531 if (ADS_ERR_OK(ads_status)) {
532 /* Reset the bind_dn to "". A Global Catalog server
533 may host multiple domain trees in a forest.
534 Windows 2003 GC server will accept "" as the search
535 path to imply search all domain trees in the forest */
537 SAFE_FREE(ads->config.bind_path);
538 ads->config.bind_path = SMB_STRDUP("");
543 SAFE_FREE(ads->server.ldap_server);
544 SAFE_FREE(ads->server.realm);
547 TALLOC_FREE(gcs_list);
553 talloc_destroy(frame);
560 * Connect to the LDAP server
561 * @param ads Pointer to an existing ADS_STRUCT
562 * @return status of connection
564 ADS_STATUS ads_connect(ADS_STRUCT *ads)
566 int version = LDAP_VERSION3;
569 char addr[INET6_ADDRSTRLEN];
571 ZERO_STRUCT(ads->ldap);
572 ads->ldap.last_attempt = time(NULL);
573 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
575 /* try with a user specified server */
577 if (DEBUGLEVEL >= 11) {
578 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
579 DEBUG(11,("ads_connect: entering\n"));
580 DEBUGADD(11,("%s\n", s));
584 if (ads->server.ldap_server)
586 if (ads_try_connect(ads, ads->server.ldap_server, ads->server.gc)) {
590 /* The choice of which GC use is handled one level up in
591 ads_connect_gc(). If we continue on from here with
592 ads_find_dc() we will get GC searches on port 389 which
593 doesn't work. --jerry */
595 if (ads->server.gc == true) {
596 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
600 ntstatus = ads_find_dc(ads);
601 if (NT_STATUS_IS_OK(ntstatus)) {
605 status = ADS_ERROR_NT(ntstatus);
610 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
611 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
613 if (!ads->auth.user_name) {
614 /* Must use the userPrincipalName value here or sAMAccountName
615 and not servicePrincipalName; found by Guenther Deschner */
617 if (asprintf(&ads->auth.user_name, "%s$", global_myname() ) == -1) {
618 DEBUG(0,("ads_connect: asprintf fail.\n"));
619 ads->auth.user_name = NULL;
623 if (!ads->auth.realm) {
624 ads->auth.realm = SMB_STRDUP(ads->config.realm);
627 if (!ads->auth.kdc_server) {
628 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
629 ads->auth.kdc_server = SMB_STRDUP(addr);
633 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
634 to MIT kerberos to work (tridge) */
637 if (asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm) > 0) {
638 setenv(env, ads->auth.kdc_server, 1);
644 /* If the caller() requested no LDAP bind, then we are done */
646 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
647 status = ADS_SUCCESS;
651 ads->ldap.mem_ctx = talloc_init("ads LDAP connection memory");
652 if (!ads->ldap.mem_ctx) {
653 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
657 /* Otherwise setup the TCP LDAP session */
659 ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name,
660 ads->ldap.port, lp_ldap_timeout());
661 if (ads->ldap.ld == NULL) {
662 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
665 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
667 /* cache the successful connection for workgroup and realm */
668 if (ads_closest_dc(ads)) {
669 saf_store( ads->server.workgroup, ads->config.ldap_server_name);
670 saf_store( ads->server.realm, ads->config.ldap_server_name);
673 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
675 if ( lp_ldap_ssl_ads() ) {
676 status = ADS_ERROR(smb_ldap_start_tls(ads->ldap.ld, version));
677 if (!ADS_ERR_OK(status)) {
682 /* fill in the current time and offsets */
684 status = ads_current_time( ads );
685 if ( !ADS_ERR_OK(status) ) {
689 /* Now do the bind */
691 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
692 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
696 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
697 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
701 status = ads_sasl_bind(ads);
704 if (DEBUGLEVEL >= 11) {
705 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
706 DEBUG(11,("ads_connect: leaving with: %s\n",
707 ads_errstr(status)));
708 DEBUGADD(11,("%s\n", s));
716 * Connect to the LDAP server using given credentials
717 * @param ads Pointer to an existing ADS_STRUCT
718 * @return status of connection
720 ADS_STATUS ads_connect_user_creds(ADS_STRUCT *ads)
722 ads->auth.flags |= ADS_AUTH_USER_CREDS;
724 return ads_connect(ads);
728 * Disconnect the LDAP server
729 * @param ads Pointer to an existing ADS_STRUCT
731 void ads_disconnect(ADS_STRUCT *ads)
734 ldap_unbind(ads->ldap.ld);
737 if (ads->ldap.wrap_ops && ads->ldap.wrap_ops->disconnect) {
738 ads->ldap.wrap_ops->disconnect(ads);
740 if (ads->ldap.mem_ctx) {
741 talloc_free(ads->ldap.mem_ctx);
743 ZERO_STRUCT(ads->ldap);
747 Duplicate a struct berval into talloc'ed memory
749 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
751 struct berval *value;
753 if (!in_val) return NULL;
755 value = TALLOC_ZERO_P(ctx, struct berval);
758 if (in_val->bv_len == 0) return value;
760 value->bv_len = in_val->bv_len;
761 value->bv_val = (char *)TALLOC_MEMDUP(ctx, in_val->bv_val,
767 Make a values list out of an array of (struct berval *)
769 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
770 const struct berval **in_vals)
772 struct berval **values;
775 if (!in_vals) return NULL;
776 for (i=0; in_vals[i]; i++)
778 values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
779 if (!values) return NULL;
781 for (i=0; in_vals[i]; i++) {
782 values[i] = dup_berval(ctx, in_vals[i]);
788 UTF8-encode a values list out of an array of (char *)
790 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
796 if (!in_vals) return NULL;
797 for (i=0; in_vals[i]; i++)
799 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
800 if (!values) return NULL;
802 for (i=0; in_vals[i]; i++) {
803 if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
812 Pull a (char *) array out of a UTF8-encoded values list
814 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
818 size_t converted_size;
820 if (!in_vals) return NULL;
821 for (i=0; in_vals[i]; i++)
823 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
824 if (!values) return NULL;
826 for (i=0; in_vals[i]; i++) {
827 if (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
829 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
830 "%s", strerror(errno)));
837 * Do a search with paged results. cookie must be null on the first
838 * call, and then returned on each subsequent call. It will be null
839 * again when the entire search is complete
840 * @param ads connection to ads server
841 * @param bind_path Base dn for the search
842 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
843 * @param expr Search expression - specified in local charset
844 * @param attrs Attributes to retrieve - specified in utf8 or ascii
845 * @param res ** which will contain results - free res* with ads_msgfree()
846 * @param count Number of entries retrieved on this page
847 * @param cookie The paged results cookie to be returned on subsequent calls
848 * @return status of search
850 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
851 const char *bind_path,
852 int scope, const char *expr,
853 const char **attrs, void *args,
855 int *count, struct berval **cookie)
858 char *utf8_expr, *utf8_path, **search_attrs = NULL;
859 size_t converted_size;
860 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
861 BerElement *cookie_be = NULL;
862 struct berval *cookie_bv= NULL;
863 BerElement *ext_be = NULL;
864 struct berval *ext_bv= NULL;
867 ads_control *external_control = (ads_control *) args;
871 if (!(ctx = talloc_init("ads_do_paged_search_args")))
872 return ADS_ERROR(LDAP_NO_MEMORY);
874 /* 0 means the conversion worked but the result was empty
875 so we only fail if it's -1. In any case, it always
876 at least nulls out the dest */
877 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
878 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
884 if (!attrs || !(*attrs))
887 /* This would be the utf8-encoded version...*/
888 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
889 if (!(search_attrs = str_list_copy(talloc_tos(), attrs))) {
895 /* Paged results only available on ldap v3 or later */
896 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
897 if (version < LDAP_VERSION3) {
898 rc = LDAP_NOT_SUPPORTED;
902 cookie_be = ber_alloc_t(LBER_USE_DER);
904 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
905 ber_bvfree(*cookie); /* don't need it from last time */
908 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
910 ber_flatten(cookie_be, &cookie_bv);
911 PagedResults.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID);
912 PagedResults.ldctl_iscritical = (char) 1;
913 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
914 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
916 NoReferrals.ldctl_oid = CONST_DISCARD(char *, ADS_NO_REFERRALS_OID);
917 NoReferrals.ldctl_iscritical = (char) 0;
918 NoReferrals.ldctl_value.bv_len = 0;
919 NoReferrals.ldctl_value.bv_val = CONST_DISCARD(char *, "");
921 if (external_control &&
922 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
923 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
925 ExternalCtrl.ldctl_oid = CONST_DISCARD(char *, external_control->control);
926 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
928 /* win2k does not accept a ldctl_value beeing passed in */
930 if (external_control->val != 0) {
932 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
937 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
941 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
946 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
947 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
950 ExternalCtrl.ldctl_value.bv_len = 0;
951 ExternalCtrl.ldctl_value.bv_val = NULL;
954 controls[0] = &NoReferrals;
955 controls[1] = &PagedResults;
956 controls[2] = &ExternalCtrl;
960 controls[0] = &NoReferrals;
961 controls[1] = &PagedResults;
965 /* we need to disable referrals as the openldap libs don't
966 handle them and paged results at the same time. Using them
967 together results in the result record containing the server
968 page control being removed from the result list (tridge/jmcd)
970 leaving this in despite the control that says don't generate
971 referrals, in case the server doesn't support it (jmcd)
973 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
975 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
976 search_attrs, 0, controls,
978 (LDAPMessage **)res);
980 ber_free(cookie_be, 1);
981 ber_bvfree(cookie_bv);
984 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
985 ldap_err2string(rc)));
989 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
990 NULL, &rcontrols, 0);
996 for (i=0; rcontrols[i]; i++) {
997 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
998 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
999 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
1001 /* the berval is the cookie, but must be freed when
1003 if (cookie_bv->bv_len) /* still more to do */
1004 *cookie=ber_bvdup(cookie_bv);
1007 ber_bvfree(cookie_bv);
1008 ber_free(cookie_be, 1);
1012 ldap_controls_free(rcontrols);
1015 talloc_destroy(ctx);
1018 ber_free(ext_be, 1);
1025 /* if/when we decide to utf8-encode attrs, take out this next line */
1026 TALLOC_FREE(search_attrs);
1028 return ADS_ERROR(rc);
1031 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
1032 int scope, const char *expr,
1033 const char **attrs, LDAPMessage **res,
1034 int *count, struct berval **cookie)
1036 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
1041 * Get all results for a search. This uses ads_do_paged_search() to return
1042 * all entries in a large search.
1043 * @param ads connection to ads server
1044 * @param bind_path Base dn for the search
1045 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1046 * @param expr Search expression
1047 * @param attrs Attributes to retrieve
1048 * @param res ** which will contain results - free res* with ads_msgfree()
1049 * @return status of search
1051 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
1052 int scope, const char *expr,
1053 const char **attrs, void *args,
1056 struct berval *cookie = NULL;
1061 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1064 if (!ADS_ERR_OK(status))
1067 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1069 LDAPMessage *res2 = NULL;
1071 LDAPMessage *msg, *next;
1073 status2 = ads_do_paged_search_args(ads, bind_path, scope, expr,
1074 attrs, args, &res2, &count, &cookie);
1076 if (!ADS_ERR_OK(status2)) break;
1078 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1079 that this works on all ldap libs, but I have only tested with openldap */
1080 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1081 next = ads_next_message(ads, msg);
1082 ldap_add_result_entry((LDAPMessage **)res, msg);
1084 /* note that we do not free res2, as the memory is now
1085 part of the main returned list */
1088 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1089 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1095 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1096 int scope, const char *expr,
1097 const char **attrs, LDAPMessage **res)
1099 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1102 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1103 int scope, const char *expr,
1104 const char **attrs, uint32 sd_flags,
1109 args.control = ADS_SD_FLAGS_OID;
1110 args.val = sd_flags;
1111 args.critical = True;
1113 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1118 * Run a function on all results for a search. Uses ads_do_paged_search() and
1119 * runs the function as each page is returned, using ads_process_results()
1120 * @param ads connection to ads server
1121 * @param bind_path Base dn for the search
1122 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1123 * @param expr Search expression - specified in local charset
1124 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1125 * @param fn Function which takes attr name, values list, and data_area
1126 * @param data_area Pointer which is passed to function on each call
1127 * @return status of search
1129 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1130 int scope, const char *expr, const char **attrs,
1131 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1134 struct berval *cookie = NULL;
1139 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1142 if (!ADS_ERR_OK(status)) return status;
1144 ads_process_results(ads, res, fn, data_area);
1145 ads_msgfree(ads, res);
1148 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1149 &res, &count, &cookie);
1151 if (!ADS_ERR_OK(status)) break;
1153 ads_process_results(ads, res, fn, data_area);
1154 ads_msgfree(ads, res);
1161 * Do a search with a timeout.
1162 * @param ads connection to ads server
1163 * @param bind_path Base dn for the search
1164 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1165 * @param expr Search expression
1166 * @param attrs Attributes to retrieve
1167 * @param res ** which will contain results - free res* with ads_msgfree()
1168 * @return status of search
1170 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1172 const char **attrs, LDAPMessage **res)
1175 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1176 size_t converted_size;
1180 if (!(ctx = talloc_init("ads_do_search"))) {
1181 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1182 return ADS_ERROR(LDAP_NO_MEMORY);
1185 /* 0 means the conversion worked but the result was empty
1186 so we only fail if it's negative. In any case, it always
1187 at least nulls out the dest */
1188 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1189 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1191 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1192 rc = LDAP_NO_MEMORY;
1196 if (!attrs || !(*attrs))
1197 search_attrs = NULL;
1199 /* This would be the utf8-encoded version...*/
1200 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1201 if (!(search_attrs = str_list_copy(talloc_tos(), attrs)))
1203 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1204 rc = LDAP_NO_MEMORY;
1209 /* see the note in ads_do_paged_search - we *must* disable referrals */
1210 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1212 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1213 search_attrs, 0, NULL, NULL,
1215 (LDAPMessage **)res);
1217 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1218 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1223 talloc_destroy(ctx);
1224 /* if/when we decide to utf8-encode attrs, take out this next line */
1225 TALLOC_FREE(search_attrs);
1226 return ADS_ERROR(rc);
1229 * Do a general ADS search
1230 * @param ads connection to ads server
1231 * @param res ** which will contain results - free res* with ads_msgfree()
1232 * @param expr Search expression
1233 * @param attrs Attributes to retrieve
1234 * @return status of search
1236 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1237 const char *expr, const char **attrs)
1239 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1244 * Do a search on a specific DistinguishedName
1245 * @param ads connection to ads server
1246 * @param res ** which will contain results - free res* with ads_msgfree()
1247 * @param dn DistinguishName to search
1248 * @param attrs Attributes to retrieve
1249 * @return status of search
1251 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1252 const char *dn, const char **attrs)
1254 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1259 * Free up memory from a ads_search
1260 * @param ads connection to ads server
1261 * @param msg Search results to free
1263 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1270 * Get a dn from search results
1271 * @param ads connection to ads server
1272 * @param msg Search result
1275 char *ads_get_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg)
1277 char *utf8_dn, *unix_dn;
1278 size_t converted_size;
1280 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1283 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1287 if (!pull_utf8_talloc(mem_ctx, &unix_dn, utf8_dn, &converted_size)) {
1288 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1292 ldap_memfree(utf8_dn);
1297 * Get the parent from a dn
1298 * @param dn the dn to return the parent from
1299 * @return parent dn string
1301 char *ads_parent_dn(const char *dn)
1309 p = strchr(dn, ',');
1319 * Find a machine account given a hostname
1320 * @param ads connection to ads server
1321 * @param res ** which will contain results - free res* with ads_msgfree()
1322 * @param host Hostname to search for
1323 * @return status of search
1325 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1326 const char *machine)
1330 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1334 /* the easiest way to find a machine account anywhere in the tree
1335 is to look for hostname$ */
1336 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1337 DEBUG(1, ("asprintf failed!\n"));
1338 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1341 status = ads_search(ads, res, expr, attrs);
1347 * Initialize a list of mods to be used in a modify request
1348 * @param ctx An initialized TALLOC_CTX
1349 * @return allocated ADS_MODLIST
1351 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1353 #define ADS_MODLIST_ALLOC_SIZE 10
1356 if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1357 /* -1 is safety to make sure we don't go over the end.
1358 need to reset it to NULL before doing ldap modify */
1359 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1361 return (ADS_MODLIST)mods;
1366 add an attribute to the list, with values list already constructed
1368 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1369 int mod_op, const char *name,
1370 const void *_invals)
1372 const void **invals = (const void **)_invals;
1374 LDAPMod **modlist = (LDAPMod **) *mods;
1375 struct berval **ber_values = NULL;
1376 char **char_values = NULL;
1379 mod_op = LDAP_MOD_DELETE;
1381 if (mod_op & LDAP_MOD_BVALUES)
1382 ber_values = ads_dup_values(ctx,
1383 (const struct berval **)invals);
1385 char_values = ads_push_strvals(ctx,
1386 (const char **) invals);
1389 /* find the first empty slot */
1390 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1392 if (modlist[curmod] == (LDAPMod *) -1) {
1393 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
1394 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1395 return ADS_ERROR(LDAP_NO_MEMORY);
1396 memset(&modlist[curmod], 0,
1397 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1398 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1399 *mods = (ADS_MODLIST)modlist;
1402 if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
1403 return ADS_ERROR(LDAP_NO_MEMORY);
1404 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1405 if (mod_op & LDAP_MOD_BVALUES) {
1406 modlist[curmod]->mod_bvalues = ber_values;
1407 } else if (mod_op & LDAP_MOD_DELETE) {
1408 modlist[curmod]->mod_values = NULL;
1410 modlist[curmod]->mod_values = char_values;
1413 modlist[curmod]->mod_op = mod_op;
1414 return ADS_ERROR(LDAP_SUCCESS);
1418 * Add a single string value to a mod list
1419 * @param ctx An initialized TALLOC_CTX
1420 * @param mods An initialized ADS_MODLIST
1421 * @param name The attribute name to add
1422 * @param val The value to add - NULL means DELETE
1423 * @return ADS STATUS indicating success of add
1425 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1426 const char *name, const char *val)
1428 const char *values[2];
1434 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1435 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1439 * Add an array of string values to a mod list
1440 * @param ctx An initialized TALLOC_CTX
1441 * @param mods An initialized ADS_MODLIST
1442 * @param name The attribute name to add
1443 * @param vals The array of string values to add - NULL means DELETE
1444 * @return ADS STATUS indicating success of add
1446 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1447 const char *name, const char **vals)
1450 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1451 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1452 name, (const void **) vals);
1457 * Add a single ber-encoded value to a mod list
1458 * @param ctx An initialized TALLOC_CTX
1459 * @param mods An initialized ADS_MODLIST
1460 * @param name The attribute name to add
1461 * @param val The value to add - NULL means DELETE
1462 * @return ADS STATUS indicating success of add
1464 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1465 const char *name, const struct berval *val)
1467 const struct berval *values[2];
1472 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1473 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1474 name, (const void **) values);
1479 * Perform an ldap modify
1480 * @param ads connection to ads server
1481 * @param mod_dn DistinguishedName to modify
1482 * @param mods list of modifications to perform
1483 * @return status of modify
1485 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1488 char *utf8_dn = NULL;
1489 size_t converted_size;
1491 this control is needed to modify that contains a currently
1492 non-existent attribute (but allowable for the object) to run
1494 LDAPControl PermitModify = {
1495 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
1498 LDAPControl *controls[2];
1500 controls[0] = &PermitModify;
1503 if (!push_utf8_allocate(&utf8_dn, mod_dn, &converted_size)) {
1504 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1507 /* find the end of the list, marked by NULL or -1 */
1508 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1509 /* make sure the end of the list is NULL */
1511 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1512 (LDAPMod **) mods, controls, NULL);
1514 return ADS_ERROR(ret);
1518 * Perform an ldap add
1519 * @param ads connection to ads server
1520 * @param new_dn DistinguishedName to add
1521 * @param mods list of attributes and values for DN
1522 * @return status of add
1524 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1527 char *utf8_dn = NULL;
1528 size_t converted_size;
1530 if (!push_utf8_allocate(&utf8_dn, new_dn, &converted_size)) {
1531 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1532 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1535 /* find the end of the list, marked by NULL or -1 */
1536 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1537 /* make sure the end of the list is NULL */
1540 ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1542 return ADS_ERROR(ret);
1546 * Delete a DistinguishedName
1547 * @param ads connection to ads server
1548 * @param new_dn DistinguishedName to delete
1549 * @return status of delete
1551 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1554 char *utf8_dn = NULL;
1555 size_t converted_size;
1556 if (!push_utf8_allocate(&utf8_dn, del_dn, &converted_size)) {
1557 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1558 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1561 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1563 return ADS_ERROR(ret);
1567 * Build an org unit string
1568 * if org unit is Computers or blank then assume a container, otherwise
1569 * assume a / separated list of organisational units.
1570 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1571 * @param ads connection to ads server
1572 * @param org_unit Organizational unit
1573 * @return org unit string - caller must free
1575 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1579 if (!org_unit || !*org_unit) {
1581 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1583 /* samba4 might not yet respond to a wellknownobject-query */
1584 return ret ? ret : SMB_STRDUP("cn=Computers");
1587 if (strequal(org_unit, "Computers")) {
1588 return SMB_STRDUP("cn=Computers");
1591 /* jmcd: removed "\\" from the separation chars, because it is
1592 needed as an escape for chars like '#' which are valid in an
1594 return ads_build_path(org_unit, "/", "ou=", 1);
1598 * Get a org unit string for a well-known GUID
1599 * @param ads connection to ads server
1600 * @param wknguid Well known GUID
1601 * @return org unit string - caller must free
1603 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1606 LDAPMessage *res = NULL;
1607 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1608 **bind_dn_exp = NULL;
1609 const char *attrs[] = {"distinguishedName", NULL};
1610 int new_ln, wkn_ln, bind_ln, i;
1612 if (wknguid == NULL) {
1616 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1617 DEBUG(1, ("asprintf failed!\n"));
1621 status = ads_search_dn(ads, &res, base, attrs);
1622 if (!ADS_ERR_OK(status)) {
1623 DEBUG(1,("Failed while searching for: %s\n", base));
1627 if (ads_count_replies(ads, res) != 1) {
1631 /* substitute the bind-path from the well-known-guid-search result */
1632 wkn_dn = ads_get_dn(ads, talloc_tos(), res);
1637 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1642 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1647 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1649 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1652 new_ln = wkn_ln - bind_ln;
1654 ret = SMB_STRDUP(wkn_dn_exp[0]);
1659 for (i=1; i < new_ln; i++) {
1662 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1668 ret = SMB_STRDUP(s);
1677 ads_msgfree(ads, res);
1678 TALLOC_FREE(wkn_dn);
1680 ldap_value_free(wkn_dn_exp);
1683 ldap_value_free(bind_dn_exp);
1690 * Adds (appends) an item to an attribute array, rather then
1691 * replacing the whole list
1692 * @param ctx An initialized TALLOC_CTX
1693 * @param mods An initialized ADS_MODLIST
1694 * @param name name of the ldap attribute to append to
1695 * @param vals an array of values to add
1696 * @return status of addition
1699 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1700 const char *name, const char **vals)
1702 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1703 (const void *) vals);
1707 * Determines the an account's current KVNO via an LDAP lookup
1708 * @param ads An initialized ADS_STRUCT
1709 * @param account_name the NT samaccountname.
1710 * @return the kvno for the account, or -1 in case of a failure.
1713 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
1715 LDAPMessage *res = NULL;
1716 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1718 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1719 char *dn_string = NULL;
1720 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1722 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
1723 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
1726 ret = ads_search(ads, &res, filter, attrs);
1728 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
1729 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
1730 ads_msgfree(ads, res);
1734 dn_string = ads_get_dn(ads, talloc_tos(), res);
1736 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1737 ads_msgfree(ads, res);
1740 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1741 TALLOC_FREE(dn_string);
1743 /* ---------------------------------------------------------
1744 * 0 is returned as a default KVNO from this point on...
1745 * This is done because Windows 2000 does not support key
1746 * version numbers. Chances are that a failure in the next
1747 * step is simply due to Windows 2000 being used for a
1748 * domain controller. */
1751 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1752 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1753 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1754 ads_msgfree(ads, res);
1759 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1760 ads_msgfree(ads, res);
1765 * Determines the computer account's current KVNO via an LDAP lookup
1766 * @param ads An initialized ADS_STRUCT
1767 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1768 * @return the kvno for the computer account, or -1 in case of a failure.
1771 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
1773 char *computer_account = NULL;
1776 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
1780 kvno = ads_get_kvno(ads, computer_account);
1781 free(computer_account);
1787 * This clears out all registered spn's for a given hostname
1788 * @param ads An initilaized ADS_STRUCT
1789 * @param machine_name the NetBIOS name of the computer.
1790 * @return 0 upon success, non-zero otherwise.
1793 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1796 LDAPMessage *res = NULL;
1798 const char *servicePrincipalName[1] = {NULL};
1799 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1800 char *dn_string = NULL;
1802 ret = ads_find_machine_acct(ads, &res, machine_name);
1803 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1804 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1805 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1806 ads_msgfree(ads, res);
1807 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1810 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1811 ctx = talloc_init("ads_clear_service_principal_names");
1813 ads_msgfree(ads, res);
1814 return ADS_ERROR(LDAP_NO_MEMORY);
1817 if (!(mods = ads_init_mods(ctx))) {
1818 talloc_destroy(ctx);
1819 ads_msgfree(ads, res);
1820 return ADS_ERROR(LDAP_NO_MEMORY);
1822 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1823 if (!ADS_ERR_OK(ret)) {
1824 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1825 ads_msgfree(ads, res);
1826 talloc_destroy(ctx);
1829 dn_string = ads_get_dn(ads, talloc_tos(), res);
1831 talloc_destroy(ctx);
1832 ads_msgfree(ads, res);
1833 return ADS_ERROR(LDAP_NO_MEMORY);
1835 ret = ads_gen_mod(ads, dn_string, mods);
1836 TALLOC_FREE(dn_string);
1837 if (!ADS_ERR_OK(ret)) {
1838 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1840 ads_msgfree(ads, res);
1841 talloc_destroy(ctx);
1845 ads_msgfree(ads, res);
1846 talloc_destroy(ctx);
1851 * This adds a service principal name to an existing computer account
1852 * (found by hostname) in AD.
1853 * @param ads An initialized ADS_STRUCT
1854 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1855 * @param my_fqdn The fully qualified DNS name of the machine
1856 * @param spn A string of the service principal to add, i.e. 'host'
1857 * @return 0 upon sucess, or non-zero if a failure occurs
1860 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1861 const char *my_fqdn, const char *spn)
1865 LDAPMessage *res = NULL;
1868 char *dn_string = NULL;
1869 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1871 ret = ads_find_machine_acct(ads, &res, machine_name);
1872 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1873 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1875 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1876 spn, machine_name, ads->config.realm));
1877 ads_msgfree(ads, res);
1878 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1881 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1882 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1883 ads_msgfree(ads, res);
1884 return ADS_ERROR(LDAP_NO_MEMORY);
1887 /* add short name spn */
1889 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1890 talloc_destroy(ctx);
1891 ads_msgfree(ads, res);
1892 return ADS_ERROR(LDAP_NO_MEMORY);
1895 strlower_m(&psp1[strlen(spn)]);
1896 servicePrincipalName[0] = psp1;
1898 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1899 psp1, machine_name));
1902 /* add fully qualified spn */
1904 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1905 ret = ADS_ERROR(LDAP_NO_MEMORY);
1909 strlower_m(&psp2[strlen(spn)]);
1910 servicePrincipalName[1] = psp2;
1912 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1913 psp2, machine_name));
1915 if ( (mods = ads_init_mods(ctx)) == NULL ) {
1916 ret = ADS_ERROR(LDAP_NO_MEMORY);
1920 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1921 if (!ADS_ERR_OK(ret)) {
1922 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1926 if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
1927 ret = ADS_ERROR(LDAP_NO_MEMORY);
1931 ret = ads_gen_mod(ads, dn_string, mods);
1932 if (!ADS_ERR_OK(ret)) {
1933 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1939 ads_msgfree(ads, res);
1944 * adds a machine account to the ADS server
1945 * @param ads An intialized ADS_STRUCT
1946 * @param machine_name - the NetBIOS machine name of this account.
1947 * @param account_type A number indicating the type of account to create
1948 * @param org_unit The LDAP path in which to place this account
1949 * @return 0 upon success, or non-zero otherwise
1952 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1953 const char *org_unit)
1956 char *samAccountName, *controlstr;
1959 char *machine_escaped = NULL;
1961 const char *objectClass[] = {"top", "person", "organizationalPerson",
1962 "user", "computer", NULL};
1963 LDAPMessage *res = NULL;
1964 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
1965 UF_DONT_EXPIRE_PASSWD |\
1966 UF_ACCOUNTDISABLE );
1968 if (!(ctx = talloc_init("ads_add_machine_acct")))
1969 return ADS_ERROR(LDAP_NO_MEMORY);
1971 ret = ADS_ERROR(LDAP_NO_MEMORY);
1973 machine_escaped = escape_rdn_val_string_alloc(machine_name);
1974 if (!machine_escaped) {
1978 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
1979 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
1981 if ( !new_dn || !samAccountName ) {
1985 #ifndef ENCTYPE_ARCFOUR_HMAC
1986 acct_control |= UF_USE_DES_KEY_ONLY;
1989 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
1993 if (!(mods = ads_init_mods(ctx))) {
1997 ads_mod_str(ctx, &mods, "cn", machine_name);
1998 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1999 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
2000 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2002 ret = ads_gen_add(ads, new_dn, mods);
2005 SAFE_FREE(machine_escaped);
2006 ads_msgfree(ads, res);
2007 talloc_destroy(ctx);
2013 * move a machine account to another OU on the ADS server
2014 * @param ads - An intialized ADS_STRUCT
2015 * @param machine_name - the NetBIOS machine name of this account.
2016 * @param org_unit - The LDAP path in which to place this account
2017 * @param moved - whether we moved the machine account (optional)
2018 * @return 0 upon success, or non-zero otherwise
2021 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2022 const char *org_unit, bool *moved)
2026 LDAPMessage *res = NULL;
2027 char *filter = NULL;
2028 char *computer_dn = NULL;
2030 char *computer_rdn = NULL;
2031 bool need_move = False;
2033 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2034 rc = ADS_ERROR(LDAP_NO_MEMORY);
2038 /* Find pre-existing machine */
2039 rc = ads_search(ads, &res, filter, NULL);
2040 if (!ADS_ERR_OK(rc)) {
2044 computer_dn = ads_get_dn(ads, talloc_tos(), res);
2046 rc = ADS_ERROR(LDAP_NO_MEMORY);
2050 parent_dn = ads_parent_dn(computer_dn);
2051 if (strequal(parent_dn, org_unit)) {
2057 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2058 rc = ADS_ERROR(LDAP_NO_MEMORY);
2062 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2063 org_unit, 1, NULL, NULL);
2064 rc = ADS_ERROR(ldap_status);
2067 ads_msgfree(ads, res);
2069 SAFE_FREE(computer_dn);
2070 SAFE_FREE(computer_rdn);
2072 if (!ADS_ERR_OK(rc)) {
2084 dump a binary result from ldap
2086 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2089 for (i=0; values[i]; i++) {
2090 printf("%s: ", field);
2091 for (j=0; j<values[i]->bv_len; j++) {
2092 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2098 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2101 for (i=0; values[i]; i++) {
2106 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
2107 smb_uuid_unpack(guid, &tmp);
2108 printf("%s: %s\n", field, GUID_string(talloc_tos(), &tmp));
2113 dump a sid result from ldap
2115 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2118 for (i=0; values[i]; i++) {
2121 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
2122 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
2127 dump ntSecurityDescriptor
2129 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2131 TALLOC_CTX *frame = talloc_stackframe();
2132 struct security_descriptor *psd;
2135 status = unmarshall_sec_desc(talloc_tos(), (uint8 *)values[0]->bv_val,
2136 values[0]->bv_len, &psd);
2137 if (!NT_STATUS_IS_OK(status)) {
2138 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2139 nt_errstr(status)));
2145 ads_disp_sd(ads, talloc_tos(), psd);
2152 dump a string result from ldap
2154 static void dump_string(const char *field, char **values)
2157 for (i=0; values[i]; i++) {
2158 printf("%s: %s\n", field, values[i]);
2163 dump a field from LDAP on stdout
2167 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2172 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2174 {"objectGUID", False, dump_guid},
2175 {"netbootGUID", False, dump_guid},
2176 {"nTSecurityDescriptor", False, dump_sd},
2177 {"dnsRecord", False, dump_binary},
2178 {"objectSid", False, dump_sid},
2179 {"tokenGroups", False, dump_sid},
2180 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2181 {"tokengroupsGlobalandUniversal", False, dump_sid},
2182 {"mS-DS-CreatorSID", False, dump_sid},
2183 {"msExchMailboxGuid", False, dump_guid},
2188 if (!field) { /* must be end of an entry */
2193 for (i=0; handlers[i].name; i++) {
2194 if (StrCaseCmp(handlers[i].name, field) == 0) {
2195 if (!values) /* first time, indicate string or not */
2196 return handlers[i].string;
2197 handlers[i].handler(ads, field, (struct berval **) values);
2201 if (!handlers[i].name) {
2202 if (!values) /* first time, indicate string conversion */
2204 dump_string(field, (char **)values);
2210 * Dump a result from LDAP on stdout
2211 * used for debugging
2212 * @param ads connection to ads server
2213 * @param res Results to dump
2216 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2218 ads_process_results(ads, res, ads_dump_field, NULL);
2222 * Walk through results, calling a function for each entry found.
2223 * The function receives a field name, a berval * array of values,
2224 * and a data area passed through from the start. The function is
2225 * called once with null for field and values at the end of each
2227 * @param ads connection to ads server
2228 * @param res Results to process
2229 * @param fn Function for processing each result
2230 * @param data_area user-defined area to pass to function
2232 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2233 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2238 size_t converted_size;
2240 if (!(ctx = talloc_init("ads_process_results")))
2243 for (msg = ads_first_entry(ads, res); msg;
2244 msg = ads_next_entry(ads, msg)) {
2248 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2249 (LDAPMessage *)msg,&b);
2251 utf8_field=ldap_next_attribute(ads->ldap.ld,
2252 (LDAPMessage *)msg,b)) {
2253 struct berval **ber_vals;
2254 char **str_vals, **utf8_vals;
2258 if (!pull_utf8_talloc(ctx, &field, utf8_field,
2261 DEBUG(0,("ads_process_results: "
2262 "pull_utf8_talloc failed: %s",
2266 string = fn(ads, field, NULL, data_area);
2269 utf8_vals = ldap_get_values(ads->ldap.ld,
2270 (LDAPMessage *)msg, field);
2271 str_vals = ads_pull_strvals(ctx,
2272 (const char **) utf8_vals);
2273 fn(ads, field, (void **) str_vals, data_area);
2274 ldap_value_free(utf8_vals);
2276 ber_vals = ldap_get_values_len(ads->ldap.ld,
2277 (LDAPMessage *)msg, field);
2278 fn(ads, field, (void **) ber_vals, data_area);
2280 ldap_value_free_len(ber_vals);
2282 ldap_memfree(utf8_field);
2285 talloc_free_children(ctx);
2286 fn(ads, NULL, NULL, data_area); /* completed an entry */
2289 talloc_destroy(ctx);
2293 * count how many replies are in a LDAPMessage
2294 * @param ads connection to ads server
2295 * @param res Results to count
2296 * @return number of replies
2298 int ads_count_replies(ADS_STRUCT *ads, void *res)
2300 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2304 * pull the first entry from a ADS result
2305 * @param ads connection to ads server
2306 * @param res Results of search
2307 * @return first entry from result
2309 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2311 return ldap_first_entry(ads->ldap.ld, res);
2315 * pull the next entry from a ADS result
2316 * @param ads connection to ads server
2317 * @param res Results of search
2318 * @return next entry from result
2320 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2322 return ldap_next_entry(ads->ldap.ld, res);
2326 * pull the first message from a ADS result
2327 * @param ads connection to ads server
2328 * @param res Results of search
2329 * @return first message from result
2331 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2333 return ldap_first_message(ads->ldap.ld, res);
2337 * pull the next message from a ADS result
2338 * @param ads connection to ads server
2339 * @param res Results of search
2340 * @return next message from result
2342 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2344 return ldap_next_message(ads->ldap.ld, res);
2348 * pull a single string from a ADS result
2349 * @param ads connection to ads server
2350 * @param mem_ctx TALLOC_CTX to use for allocating result string
2351 * @param msg Results of search
2352 * @param field Attribute to retrieve
2353 * @return Result string in talloc context
2355 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2361 size_t converted_size;
2363 values = ldap_get_values(ads->ldap.ld, msg, field);
2367 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2372 ldap_value_free(values);
2377 * pull an array of strings from a ADS result
2378 * @param ads connection to ads server
2379 * @param mem_ctx TALLOC_CTX to use for allocating result string
2380 * @param msg Results of search
2381 * @param field Attribute to retrieve
2382 * @return Result strings in talloc context
2384 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2385 LDAPMessage *msg, const char *field,
2391 size_t converted_size;
2393 values = ldap_get_values(ads->ldap.ld, msg, field);
2397 *num_values = ldap_count_values(values);
2399 ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
2401 ldap_value_free(values);
2405 for (i=0;i<*num_values;i++) {
2406 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2409 ldap_value_free(values);
2415 ldap_value_free(values);
2420 * pull an array of strings from a ADS result
2421 * (handle large multivalue attributes with range retrieval)
2422 * @param ads connection to ads server
2423 * @param mem_ctx TALLOC_CTX to use for allocating result string
2424 * @param msg Results of search
2425 * @param field Attribute to retrieve
2426 * @param current_strings strings returned by a previous call to this function
2427 * @param next_attribute The next query should ask for this attribute
2428 * @param num_values How many values did we get this time?
2429 * @param more_values Are there more values to get?
2430 * @return Result strings in talloc context
2432 char **ads_pull_strings_range(ADS_STRUCT *ads,
2433 TALLOC_CTX *mem_ctx,
2434 LDAPMessage *msg, const char *field,
2435 char **current_strings,
2436 const char **next_attribute,
2437 size_t *num_strings,
2441 char *expected_range_attrib, *range_attr;
2442 BerElement *ptr = NULL;
2445 size_t num_new_strings;
2446 unsigned long int range_start;
2447 unsigned long int range_end;
2449 /* we might have been given the whole lot anyway */
2450 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2451 *more_strings = False;
2455 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2457 /* look for Range result */
2458 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2460 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2461 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2462 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2470 /* nothing here - this field is just empty */
2471 *more_strings = False;
2475 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2476 &range_start, &range_end) == 2) {
2477 *more_strings = True;
2479 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2480 &range_start) == 1) {
2481 *more_strings = False;
2483 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2485 ldap_memfree(range_attr);
2486 *more_strings = False;
2491 if ((*num_strings) != range_start) {
2492 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2493 " - aborting range retreival\n",
2494 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2495 ldap_memfree(range_attr);
2496 *more_strings = False;
2500 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2502 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2503 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2504 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2505 range_attr, (unsigned long int)range_end - range_start + 1,
2506 (unsigned long int)num_new_strings));
2507 ldap_memfree(range_attr);
2508 *more_strings = False;
2512 strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2513 *num_strings + num_new_strings);
2515 if (strings == NULL) {
2516 ldap_memfree(range_attr);
2517 *more_strings = False;
2521 if (new_strings && num_new_strings) {
2522 memcpy(&strings[*num_strings], new_strings,
2523 sizeof(*new_strings) * num_new_strings);
2526 (*num_strings) += num_new_strings;
2528 if (*more_strings) {
2529 *next_attribute = talloc_asprintf(mem_ctx,
2534 if (!*next_attribute) {
2535 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2536 ldap_memfree(range_attr);
2537 *more_strings = False;
2542 ldap_memfree(range_attr);
2548 * pull a single uint32 from a ADS result
2549 * @param ads connection to ads server
2550 * @param msg Results of search
2551 * @param field Attribute to retrieve
2552 * @param v Pointer to int to store result
2553 * @return boolean inidicating success
2555 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2560 values = ldap_get_values(ads->ldap.ld, msg, field);
2564 ldap_value_free(values);
2568 *v = atoi(values[0]);
2569 ldap_value_free(values);
2574 * pull a single objectGUID from an ADS result
2575 * @param ads connection to ADS server
2576 * @param msg results of search
2577 * @param guid 37-byte area to receive text guid
2578 * @return boolean indicating success
2580 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2583 UUID_FLAT flat_guid;
2585 values = ldap_get_values(ads->ldap.ld, msg, "objectGUID");
2590 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2591 smb_uuid_unpack(flat_guid, guid);
2592 ldap_value_free(values);
2595 ldap_value_free(values);
2602 * pull a single DOM_SID from a ADS result
2603 * @param ads connection to ads server
2604 * @param msg Results of search
2605 * @param field Attribute to retrieve
2606 * @param sid Pointer to sid to store result
2607 * @return boolean inidicating success
2609 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2612 struct berval **values;
2615 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2621 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2623 ldap_value_free_len(values);
2628 * pull an array of DOM_SIDs from a ADS result
2629 * @param ads connection to ads server
2630 * @param mem_ctx TALLOC_CTX for allocating sid array
2631 * @param msg Results of search
2632 * @param field Attribute to retrieve
2633 * @param sids pointer to sid array to allocate
2634 * @return the count of SIDs pulled
2636 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2637 LDAPMessage *msg, const char *field, DOM_SID **sids)
2639 struct berval **values;
2643 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2648 for (i=0; values[i]; i++)
2652 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2654 ldap_value_free_len(values);
2662 for (i=0; values[i]; i++) {
2663 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2665 DEBUG(10, ("pulling SID: %s\n",
2666 sid_string_dbg(&(*sids)[count])));
2671 ldap_value_free_len(values);
2676 * pull a SEC_DESC from a ADS result
2677 * @param ads connection to ads server
2678 * @param mem_ctx TALLOC_CTX for allocating sid array
2679 * @param msg Results of search
2680 * @param field Attribute to retrieve
2681 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2682 * @return boolean inidicating success
2684 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2685 LDAPMessage *msg, const char *field, SEC_DESC **sd)
2687 struct berval **values;
2690 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2692 if (!values) return false;
2696 status = unmarshall_sec_desc(mem_ctx,
2697 (uint8 *)values[0]->bv_val,
2698 values[0]->bv_len, sd);
2699 if (!NT_STATUS_IS_OK(status)) {
2700 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2701 nt_errstr(status)));
2706 ldap_value_free_len(values);
2711 * in order to support usernames longer than 21 characters we need to
2712 * use both the sAMAccountName and the userPrincipalName attributes
2713 * It seems that not all users have the userPrincipalName attribute set
2715 * @param ads connection to ads server
2716 * @param mem_ctx TALLOC_CTX for allocating sid array
2717 * @param msg Results of search
2718 * @return the username
2720 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2726 /* lookup_name() only works on the sAMAccountName to
2727 returning the username portion of userPrincipalName
2728 breaks winbindd_getpwnam() */
2730 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2731 if (ret && (p = strchr_m(ret, '@'))) {
2736 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2741 * find the update serial number - this is the core of the ldap cache
2742 * @param ads connection to ads server
2743 * @param ads connection to ADS server
2744 * @param usn Pointer to retrieved update serial number
2745 * @return status of search
2747 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2749 const char *attrs[] = {"highestCommittedUSN", NULL};
2753 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2754 if (!ADS_ERR_OK(status))
2757 if (ads_count_replies(ads, res) != 1) {
2758 ads_msgfree(ads, res);
2759 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2762 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2763 ads_msgfree(ads, res);
2764 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2767 ads_msgfree(ads, res);
2771 /* parse a ADS timestring - typical string is
2772 '20020917091222.0Z0' which means 09:12.22 17th September
2774 static time_t ads_parse_time(const char *str)
2780 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2781 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2782 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2791 /********************************************************************
2792 ********************************************************************/
2794 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2796 const char *attrs[] = {"currentTime", NULL};
2801 ADS_STRUCT *ads_s = ads;
2803 if (!(ctx = talloc_init("ads_current_time"))) {
2804 return ADS_ERROR(LDAP_NO_MEMORY);
2807 /* establish a new ldap tcp session if necessary */
2809 if ( !ads->ldap.ld ) {
2810 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2811 ads->server.ldap_server )) == NULL )
2815 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2816 status = ads_connect( ads_s );
2817 if ( !ADS_ERR_OK(status))
2821 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2822 if (!ADS_ERR_OK(status)) {
2826 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2828 ads_msgfree(ads_s, res);
2829 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2833 /* but save the time and offset in the original ADS_STRUCT */
2835 ads->config.current_time = ads_parse_time(timestr);
2837 if (ads->config.current_time != 0) {
2838 ads->auth.time_offset = ads->config.current_time - time(NULL);
2839 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2842 ads_msgfree(ads, res);
2844 status = ADS_SUCCESS;
2847 /* free any temporary ads connections */
2848 if ( ads_s != ads ) {
2849 ads_destroy( &ads_s );
2851 talloc_destroy(ctx);
2856 /********************************************************************
2857 ********************************************************************/
2859 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2861 const char *attrs[] = {"domainFunctionality", NULL};
2864 ADS_STRUCT *ads_s = ads;
2866 *val = DS_DOMAIN_FUNCTION_2000;
2868 /* establish a new ldap tcp session if necessary */
2870 if ( !ads->ldap.ld ) {
2871 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2872 ads->server.ldap_server )) == NULL )
2874 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2877 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2878 status = ads_connect( ads_s );
2879 if ( !ADS_ERR_OK(status))
2883 /* If the attribute does not exist assume it is a Windows 2000
2884 functional domain */
2886 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2887 if (!ADS_ERR_OK(status)) {
2888 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2889 status = ADS_SUCCESS;
2894 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2895 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2897 DEBUG(3,("ads_domain_func_level: %d\n", *val));
2900 ads_msgfree(ads, res);
2903 /* free any temporary ads connections */
2904 if ( ads_s != ads ) {
2905 ads_destroy( &ads_s );
2912 * find the domain sid for our domain
2913 * @param ads connection to ads server
2914 * @param sid Pointer to domain sid
2915 * @return status of search
2917 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2919 const char *attrs[] = {"objectSid", NULL};
2923 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2925 if (!ADS_ERR_OK(rc)) return rc;
2926 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2927 ads_msgfree(ads, res);
2928 return ADS_ERROR_SYSTEM(ENOENT);
2930 ads_msgfree(ads, res);
2936 * find our site name
2937 * @param ads connection to ads server
2938 * @param mem_ctx Pointer to talloc context
2939 * @param site_name Pointer to the sitename
2940 * @return status of search
2942 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2946 const char *dn, *service_name;
2947 const char *attrs[] = { "dsServiceName", NULL };
2949 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2950 if (!ADS_ERR_OK(status)) {
2954 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2955 if (service_name == NULL) {
2956 ads_msgfree(ads, res);
2957 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2960 ads_msgfree(ads, res);
2962 /* go up three levels */
2963 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2965 return ADS_ERROR(LDAP_NO_MEMORY);
2968 *site_name = talloc_strdup(mem_ctx, dn);
2969 if (*site_name == NULL) {
2970 return ADS_ERROR(LDAP_NO_MEMORY);
2975 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2980 * find the site dn where a machine resides
2981 * @param ads connection to ads server
2982 * @param mem_ctx Pointer to talloc context
2983 * @param computer_name name of the machine
2984 * @param site_name Pointer to the sitename
2985 * @return status of search
2987 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
2991 const char *parent, *filter;
2992 char *config_context = NULL;
2995 /* shortcut a query */
2996 if (strequal(computer_name, ads->config.ldap_server_name)) {
2997 return ads_site_dn(ads, mem_ctx, site_dn);
3000 status = ads_config_path(ads, mem_ctx, &config_context);
3001 if (!ADS_ERR_OK(status)) {
3005 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3006 if (filter == NULL) {
3007 return ADS_ERROR(LDAP_NO_MEMORY);
3010 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3011 filter, NULL, &res);
3012 if (!ADS_ERR_OK(status)) {
3016 if (ads_count_replies(ads, res) != 1) {
3017 ads_msgfree(ads, res);
3018 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3021 dn = ads_get_dn(ads, mem_ctx, res);
3023 ads_msgfree(ads, res);
3024 return ADS_ERROR(LDAP_NO_MEMORY);
3027 /* go up three levels */
3028 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3029 if (parent == NULL) {
3030 ads_msgfree(ads, res);
3032 return ADS_ERROR(LDAP_NO_MEMORY);
3035 *site_dn = talloc_strdup(mem_ctx, parent);
3036 if (*site_dn == NULL) {
3037 ads_msgfree(ads, res);
3039 return ADS_ERROR(LDAP_NO_MEMORY);
3043 ads_msgfree(ads, res);
3049 * get the upn suffixes for a domain
3050 * @param ads connection to ads server
3051 * @param mem_ctx Pointer to talloc context
3052 * @param suffixes Pointer to an array of suffixes
3053 * @param num_suffixes Pointer to the number of suffixes
3054 * @return status of search
3056 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3061 char *config_context = NULL;
3062 const char *attrs[] = { "uPNSuffixes", NULL };
3064 status = ads_config_path(ads, mem_ctx, &config_context);
3065 if (!ADS_ERR_OK(status)) {
3069 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3071 return ADS_ERROR(LDAP_NO_MEMORY);
3074 status = ads_search_dn(ads, &res, base, attrs);
3075 if (!ADS_ERR_OK(status)) {
3079 if (ads_count_replies(ads, res) != 1) {
3080 ads_msgfree(ads, res);
3081 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3084 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3085 if ((*suffixes) == NULL) {
3086 ads_msgfree(ads, res);
3087 return ADS_ERROR(LDAP_NO_MEMORY);
3090 ads_msgfree(ads, res);
3096 * get the joinable ous for a domain
3097 * @param ads connection to ads server
3098 * @param mem_ctx Pointer to talloc context
3099 * @param ous Pointer to an array of ous
3100 * @param num_ous Pointer to the number of ous
3101 * @return status of search
3103 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3104 TALLOC_CTX *mem_ctx,
3109 LDAPMessage *res = NULL;
3110 LDAPMessage *msg = NULL;
3111 const char *attrs[] = { "dn", NULL };
3114 status = ads_search(ads, &res,
3115 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3117 if (!ADS_ERR_OK(status)) {
3121 count = ads_count_replies(ads, res);
3123 ads_msgfree(ads, res);
3124 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3127 for (msg = ads_first_entry(ads, res); msg;
3128 msg = ads_next_entry(ads, msg)) {
3132 dn = ads_get_dn(ads, talloc_tos(), msg);
3134 ads_msgfree(ads, res);
3135 return ADS_ERROR(LDAP_NO_MEMORY);
3138 if (!add_string_to_array(mem_ctx, dn,
3139 (const char ***)ous,
3142 ads_msgfree(ads, res);
3143 return ADS_ERROR(LDAP_NO_MEMORY);
3149 ads_msgfree(ads, res);
3156 * pull a DOM_SID from an extended dn string
3157 * @param mem_ctx TALLOC_CTX
3158 * @param extended_dn string
3159 * @param flags string type of extended_dn
3160 * @param sid pointer to a DOM_SID
3161 * @return NT_STATUS_OK on success,
3162 * NT_INVALID_PARAMETER on error,
3163 * NT_STATUS_NOT_FOUND if no SID present
3165 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3166 const char *extended_dn,
3167 enum ads_extended_dn_flags flags,
3173 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3176 /* otherwise extended_dn gets stripped off */
3177 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3178 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3181 * ADS_EXTENDED_DN_HEX_STRING:
3182 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3184 * ADS_EXTENDED_DN_STRING (only with w2k3):
3185 * <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
3187 * Object with no SID, such as an Exchange Public Folder
3188 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3191 p = strchr(dn, ';');
3193 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3196 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3197 DEBUG(5,("No SID present in extended dn\n"));
3198 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3201 p += strlen(";<SID=");
3205 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3210 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3214 case ADS_EXTENDED_DN_STRING:
3215 if (!string_to_sid(sid, p)) {
3216 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3219 case ADS_EXTENDED_DN_HEX_STRING: {
3223 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3225 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3228 if (!sid_parse(buf, buf_len, sid)) {
3229 DEBUG(10,("failed to parse sid\n"));
3230 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3235 DEBUG(10,("unknown extended dn format\n"));
3236 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3239 return ADS_ERROR_NT(NT_STATUS_OK);
3243 * pull an array of DOM_SIDs from a ADS result
3244 * @param ads connection to ads server
3245 * @param mem_ctx TALLOC_CTX for allocating sid array
3246 * @param msg Results of search
3247 * @param field Attribute to retrieve
3248 * @param flags string type of extended_dn
3249 * @param sids pointer to sid array to allocate
3250 * @return the count of SIDs pulled
3252 int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads,
3253 TALLOC_CTX *mem_ctx,
3256 enum ads_extended_dn_flags flags,
3261 size_t dn_count, ret_count = 0;
3264 if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field,
3265 &dn_count)) == NULL) {
3269 (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, dn_count + 1);
3271 TALLOC_FREE(dn_strings);
3275 for (i=0; i<dn_count; i++) {
3276 rc = ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i],
3277 flags, &(*sids)[i]);
3278 if (!ADS_ERR_OK(rc)) {
3279 if (NT_STATUS_EQUAL(ads_ntstatus(rc),
3280 NT_STATUS_NOT_FOUND)) {
3285 TALLOC_FREE(dn_strings);
3292 TALLOC_FREE(dn_strings);
3297 /********************************************************************
3298 ********************************************************************/
3300 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3302 LDAPMessage *res = NULL;
3307 status = ads_find_machine_acct(ads, &res, global_myname());
3308 if (!ADS_ERR_OK(status)) {
3309 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3314 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3315 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3319 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3320 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3324 ads_msgfree(ads, res);
3329 /********************************************************************
3330 ********************************************************************/
3332 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3334 LDAPMessage *res = NULL;
3339 status = ads_find_machine_acct(ads, &res, machine_name);
3340 if (!ADS_ERR_OK(status)) {
3341 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3346 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3347 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3351 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3352 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3356 ads_msgfree(ads, res);
3361 /********************************************************************
3362 ********************************************************************/
3364 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3366 LDAPMessage *res = NULL;
3371 status = ads_find_machine_acct(ads, &res, global_myname());
3372 if (!ADS_ERR_OK(status)) {
3373 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3378 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3379 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3383 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3384 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3388 ads_msgfree(ads, res);
3395 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3398 * Join a machine to a realm
3399 * Creates the machine account and sets the machine password
3400 * @param ads connection to ads server
3401 * @param machine name of host to add
3402 * @param org_unit Organizational unit to place machine in
3403 * @return status of join
3405 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3406 uint32 account_type, const char *org_unit)
3409 LDAPMessage *res = NULL;
3412 /* machine name must be lowercase */
3413 machine = SMB_STRDUP(machine_name);
3414 strlower_m(machine);
3417 status = ads_find_machine_acct(ads, (void **)&res, machine);
3418 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3419 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3420 status = ads_leave_realm(ads, machine);
3421 if (!ADS_ERR_OK(status)) {
3422 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3423 machine, ads->config.realm));
3428 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3429 if (!ADS_ERR_OK(status)) {
3430 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3435 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3436 if (!ADS_ERR_OK(status)) {
3437 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3443 ads_msgfree(ads, res);
3450 * Delete a machine from the realm
3451 * @param ads connection to ads server
3452 * @param hostname Machine to remove
3453 * @return status of delete
3455 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3460 char *hostnameDN, *host;
3462 LDAPControl ldap_control;
3463 LDAPControl * pldap_control[2] = {NULL, NULL};
3465 pldap_control[0] = &ldap_control;
3466 memset(&ldap_control, 0, sizeof(LDAPControl));
3467 ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
3469 /* hostname must be lowercase */
3470 host = SMB_STRDUP(hostname);
3473 status = ads_find_machine_acct(ads, &res, host);
3474 if (!ADS_ERR_OK(status)) {
3475 DEBUG(0, ("Host account for %s does not exist.\n", host));
3480 msg = ads_first_entry(ads, res);
3483 return ADS_ERROR_SYSTEM(ENOENT);
3486 hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
3488 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3490 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3492 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3495 if (rc != LDAP_SUCCESS) {
3496 const char *attrs[] = { "cn", NULL };
3497 LDAPMessage *msg_sub;
3499 /* we only search with scope ONE, we do not expect any further
3500 * objects to be created deeper */
3502 status = ads_do_search_retry(ads, hostnameDN,
3503 LDAP_SCOPE_ONELEVEL,
3504 "(objectclass=*)", attrs, &res);
3506 if (!ADS_ERR_OK(status)) {
3508 TALLOC_FREE(hostnameDN);
3512 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3513 msg_sub = ads_next_entry(ads, msg_sub)) {
3517 if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
3519 TALLOC_FREE(hostnameDN);
3520 return ADS_ERROR(LDAP_NO_MEMORY);
3523 status = ads_del_dn(ads, dn);
3524 if (!ADS_ERR_OK(status)) {
3525 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3528 TALLOC_FREE(hostnameDN);
3535 /* there should be no subordinate objects anymore */
3536 status = ads_do_search_retry(ads, hostnameDN,
3537 LDAP_SCOPE_ONELEVEL,
3538 "(objectclass=*)", attrs, &res);
3540 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3542 TALLOC_FREE(hostnameDN);
3546 /* delete hostnameDN now */
3547 status = ads_del_dn(ads, hostnameDN);
3548 if (!ADS_ERR_OK(status)) {
3550 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3551 TALLOC_FREE(hostnameDN);
3556 TALLOC_FREE(hostnameDN);
3558 status = ads_find_machine_acct(ads, &res, host);
3559 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3560 DEBUG(3, ("Failed to remove host account.\n"));
3570 * pull all token-sids from an LDAP dn
3571 * @param ads connection to ads server
3572 * @param mem_ctx TALLOC_CTX for allocating sid array
3573 * @param dn of LDAP object
3574 * @param user_sid pointer to DOM_SID (objectSid)
3575 * @param primary_group_sid pointer to DOM_SID (self composed)
3576 * @param sids pointer to sid array to allocate
3577 * @param num_sids counter of SIDs pulled
3578 * @return status of token query
3580 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3581 TALLOC_CTX *mem_ctx,
3584 DOM_SID *primary_group_sid,
3589 LDAPMessage *res = NULL;
3591 size_t tmp_num_sids;
3593 DOM_SID tmp_user_sid;
3594 DOM_SID tmp_primary_group_sid;
3596 const char *attrs[] = {
3603 status = ads_search_retry_dn(ads, &res, dn, attrs);
3604 if (!ADS_ERR_OK(status)) {
3608 count = ads_count_replies(ads, res);
3610 ads_msgfree(ads, res);
3611 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3614 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3615 ads_msgfree(ads, res);
3616 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3619 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3620 ads_msgfree(ads, res);
3621 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3625 /* hack to compose the primary group sid without knowing the
3631 sid_copy(&domsid, &tmp_user_sid);
3633 if (!sid_split_rid(&domsid, &dummy_rid)) {
3634 ads_msgfree(ads, res);
3635 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3638 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3639 ads_msgfree(ads, res);
3640 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3644 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3646 if (tmp_num_sids == 0 || !tmp_sids) {
3647 ads_msgfree(ads, res);
3648 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3652 *num_sids = tmp_num_sids;
3660 *user_sid = tmp_user_sid;
3663 if (primary_group_sid) {
3664 *primary_group_sid = tmp_primary_group_sid;
3667 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3669 ads_msgfree(ads, res);
3670 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3674 * Find a sAMAccoutName in LDAP
3675 * @param ads connection to ads server
3676 * @param mem_ctx TALLOC_CTX for allocating sid array
3677 * @param samaccountname to search
3678 * @param uac_ret uint32 pointer userAccountControl attribute value
3679 * @param dn_ret pointer to dn
3680 * @return status of token query
3682 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3683 TALLOC_CTX *mem_ctx,
3684 const char *samaccountname,
3686 const char **dn_ret)
3689 const char *attrs[] = { "userAccountControl", NULL };
3691 LDAPMessage *res = NULL;
3695 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3697 if (filter == NULL) {
3698 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3702 status = ads_do_search_all(ads, ads->config.bind_path,
3704 filter, attrs, &res);
3706 if (!ADS_ERR_OK(status)) {
3710 if (ads_count_replies(ads, res) != 1) {
3711 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3715 dn = ads_get_dn(ads, talloc_tos(), res);
3717 status = ADS_ERROR(LDAP_NO_MEMORY);
3721 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3722 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3731 *dn_ret = talloc_strdup(mem_ctx, dn);
3733 status = ADS_ERROR(LDAP_NO_MEMORY);
3739 ads_msgfree(ads, res);
3745 * find our configuration path
3746 * @param ads connection to ads server
3747 * @param mem_ctx Pointer to talloc context
3748 * @param config_path Pointer to the config path
3749 * @return status of search
3751 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3752 TALLOC_CTX *mem_ctx,
3756 LDAPMessage *res = NULL;
3757 const char *config_context = NULL;
3758 const char *attrs[] = { "configurationNamingContext", NULL };
3760 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3761 "(objectclass=*)", attrs, &res);
3762 if (!ADS_ERR_OK(status)) {
3766 config_context = ads_pull_string(ads, mem_ctx, res,
3767 "configurationNamingContext");
3768 ads_msgfree(ads, res);
3769 if (!config_context) {
3770 return ADS_ERROR(LDAP_NO_MEMORY);
3774 *config_path = talloc_strdup(mem_ctx, config_context);
3775 if (!*config_path) {
3776 return ADS_ERROR(LDAP_NO_MEMORY);
3780 return ADS_ERROR(LDAP_SUCCESS);
3784 * find the displayName of an extended right
3785 * @param ads connection to ads server
3786 * @param config_path The config path
3787 * @param mem_ctx Pointer to talloc context
3788 * @param GUID struct of the rightsGUID
3789 * @return status of search
3791 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3792 const char *config_path,
3793 TALLOC_CTX *mem_ctx,
3794 const struct GUID *rights_guid)
3797 LDAPMessage *res = NULL;
3799 const char *attrs[] = { "displayName", NULL };
3800 const char *result = NULL;
3803 if (!ads || !mem_ctx || !rights_guid) {
3807 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3808 GUID_string(mem_ctx, rights_guid));
3813 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3818 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3820 if (!ADS_ERR_OK(rc)) {
3824 if (ads_count_replies(ads, res) != 1) {
3828 result = ads_pull_string(ads, mem_ctx, res, "displayName");
3831 ads_msgfree(ads, res);
3837 * verify or build and verify an account ou
3838 * @param mem_ctx Pointer to talloc context
3839 * @param ads connection to ads server
3841 * @return status of search
3844 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3846 const char **account_ou)
3848 struct ldb_dn *name_dn = NULL;
3849 const char *name = NULL;
3850 char *ou_string = NULL;
3852 name_dn = ldb_dn_explode(mem_ctx, *account_ou);
3857 ou_string = ads_ou_string(ads, *account_ou);
3859 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3862 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3863 ads->config.bind_path);
3864 SAFE_FREE(ou_string);
3866 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3869 name_dn = ldb_dn_explode(mem_ctx, name);
3871 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3874 *account_ou = talloc_strdup(mem_ctx, name);
3876 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);