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 &&
585 ads_try_connect(ads, ads->server.ldap_server, ads->server.gc)) {
589 ntstatus = ads_find_dc(ads);
590 if (NT_STATUS_IS_OK(ntstatus)) {
594 status = ADS_ERROR_NT(ntstatus);
599 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
600 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
602 if (!ads->auth.user_name) {
603 /* Must use the userPrincipalName value here or sAMAccountName
604 and not servicePrincipalName; found by Guenther Deschner */
606 asprintf(&ads->auth.user_name, "%s$", global_myname() );
609 if (!ads->auth.realm) {
610 ads->auth.realm = SMB_STRDUP(ads->config.realm);
613 if (!ads->auth.kdc_server) {
614 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
615 ads->auth.kdc_server = SMB_STRDUP(addr);
619 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
620 to MIT kerberos to work (tridge) */
623 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
624 setenv(env, ads->auth.kdc_server, 1);
629 /* If the caller() requested no LDAP bind, then we are done */
631 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
632 status = ADS_SUCCESS;
636 ads->ldap.mem_ctx = talloc_init("ads LDAP connection memory");
637 if (!ads->ldap.mem_ctx) {
638 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
642 /* Otherwise setup the TCP LDAP session */
644 ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name,
645 ads->ldap.port, lp_ldap_timeout());
646 if (ads->ldap.ld == NULL) {
647 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
650 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
652 /* cache the successful connection for workgroup and realm */
653 if (ads_closest_dc(ads)) {
654 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
655 saf_store( ads->server.workgroup, addr);
656 saf_store( ads->server.realm, addr);
659 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
661 status = ADS_ERROR(smb_ldap_start_tls(ads->ldap.ld, version));
662 if (!ADS_ERR_OK(status)) {
666 /* fill in the current time and offsets */
668 status = ads_current_time( ads );
669 if ( !ADS_ERR_OK(status) ) {
673 /* Now do the bind */
675 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
676 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
680 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
681 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
685 status = ads_sasl_bind(ads);
688 if (DEBUGLEVEL >= 11) {
689 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
690 DEBUG(11,("ads_connect: leaving with: %s\n",
691 ads_errstr(status)));
692 DEBUGADD(11,("%s\n", s));
700 * Connect to the LDAP server using given credentials
701 * @param ads Pointer to an existing ADS_STRUCT
702 * @return status of connection
704 ADS_STATUS ads_connect_user_creds(ADS_STRUCT *ads)
706 ads->auth.flags |= ADS_AUTH_USER_CREDS;
708 return ads_connect(ads);
712 * Disconnect the LDAP server
713 * @param ads Pointer to an existing ADS_STRUCT
715 void ads_disconnect(ADS_STRUCT *ads)
718 ldap_unbind(ads->ldap.ld);
721 if (ads->ldap.wrap_ops && ads->ldap.wrap_ops->disconnect) {
722 ads->ldap.wrap_ops->disconnect(ads);
724 if (ads->ldap.mem_ctx) {
725 talloc_free(ads->ldap.mem_ctx);
727 ZERO_STRUCT(ads->ldap);
731 Duplicate a struct berval into talloc'ed memory
733 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
735 struct berval *value;
737 if (!in_val) return NULL;
739 value = TALLOC_ZERO_P(ctx, struct berval);
742 if (in_val->bv_len == 0) return value;
744 value->bv_len = in_val->bv_len;
745 value->bv_val = (char *)TALLOC_MEMDUP(ctx, in_val->bv_val,
751 Make a values list out of an array of (struct berval *)
753 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
754 const struct berval **in_vals)
756 struct berval **values;
759 if (!in_vals) return NULL;
760 for (i=0; in_vals[i]; i++)
762 values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
763 if (!values) return NULL;
765 for (i=0; in_vals[i]; i++) {
766 values[i] = dup_berval(ctx, in_vals[i]);
772 UTF8-encode a values list out of an array of (char *)
774 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
780 if (!in_vals) return NULL;
781 for (i=0; in_vals[i]; i++)
783 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
784 if (!values) return NULL;
786 for (i=0; in_vals[i]; i++) {
787 if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
796 Pull a (char *) array out of a UTF8-encoded values list
798 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
802 size_t converted_size;
804 if (!in_vals) return NULL;
805 for (i=0; in_vals[i]; i++)
807 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
808 if (!values) return NULL;
810 for (i=0; in_vals[i]; i++) {
811 if (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
813 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
814 "%s", strerror(errno)));
821 * Do a search with paged results. cookie must be null on the first
822 * call, and then returned on each subsequent call. It will be null
823 * again when the entire search is complete
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 - specified in local charset
828 * @param attrs Attributes to retrieve - specified in utf8 or ascii
829 * @param res ** which will contain results - free res* with ads_msgfree()
830 * @param count Number of entries retrieved on this page
831 * @param cookie The paged results cookie to be returned on subsequent calls
832 * @return status of search
834 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
835 const char *bind_path,
836 int scope, const char *expr,
837 const char **attrs, void *args,
839 int *count, struct berval **cookie)
842 char *utf8_expr, *utf8_path, **search_attrs = NULL;
843 size_t converted_size;
844 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
845 BerElement *cookie_be = NULL;
846 struct berval *cookie_bv= NULL;
847 BerElement *ext_be = NULL;
848 struct berval *ext_bv= NULL;
851 ads_control *external_control = (ads_control *) args;
855 if (!(ctx = talloc_init("ads_do_paged_search_args")))
856 return ADS_ERROR(LDAP_NO_MEMORY);
858 /* 0 means the conversion worked but the result was empty
859 so we only fail if it's -1. In any case, it always
860 at least nulls out the dest */
861 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
862 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
868 if (!attrs || !(*attrs))
871 /* This would be the utf8-encoded version...*/
872 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
873 if (!(search_attrs = str_list_copy(talloc_tos(), attrs))) {
879 /* Paged results only available on ldap v3 or later */
880 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
881 if (version < LDAP_VERSION3) {
882 rc = LDAP_NOT_SUPPORTED;
886 cookie_be = ber_alloc_t(LBER_USE_DER);
888 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
889 ber_bvfree(*cookie); /* don't need it from last time */
892 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
894 ber_flatten(cookie_be, &cookie_bv);
895 PagedResults.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID);
896 PagedResults.ldctl_iscritical = (char) 1;
897 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
898 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
900 NoReferrals.ldctl_oid = CONST_DISCARD(char *, ADS_NO_REFERRALS_OID);
901 NoReferrals.ldctl_iscritical = (char) 0;
902 NoReferrals.ldctl_value.bv_len = 0;
903 NoReferrals.ldctl_value.bv_val = CONST_DISCARD(char *, "");
905 if (external_control &&
906 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
907 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
909 ExternalCtrl.ldctl_oid = CONST_DISCARD(char *, external_control->control);
910 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
912 /* win2k does not accept a ldctl_value beeing passed in */
914 if (external_control->val != 0) {
916 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
921 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
925 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
930 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
931 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
934 ExternalCtrl.ldctl_value.bv_len = 0;
935 ExternalCtrl.ldctl_value.bv_val = NULL;
938 controls[0] = &NoReferrals;
939 controls[1] = &PagedResults;
940 controls[2] = &ExternalCtrl;
944 controls[0] = &NoReferrals;
945 controls[1] = &PagedResults;
949 /* we need to disable referrals as the openldap libs don't
950 handle them and paged results at the same time. Using them
951 together results in the result record containing the server
952 page control being removed from the result list (tridge/jmcd)
954 leaving this in despite the control that says don't generate
955 referrals, in case the server doesn't support it (jmcd)
957 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
959 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
960 search_attrs, 0, controls,
962 (LDAPMessage **)res);
964 ber_free(cookie_be, 1);
965 ber_bvfree(cookie_bv);
968 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
969 ldap_err2string(rc)));
973 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
974 NULL, &rcontrols, 0);
980 for (i=0; rcontrols[i]; i++) {
981 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
982 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
983 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
985 /* the berval is the cookie, but must be freed when
987 if (cookie_bv->bv_len) /* still more to do */
988 *cookie=ber_bvdup(cookie_bv);
991 ber_bvfree(cookie_bv);
992 ber_free(cookie_be, 1);
996 ldap_controls_free(rcontrols);
1002 ber_free(ext_be, 1);
1009 /* if/when we decide to utf8-encode attrs, take out this next line */
1010 TALLOC_FREE(search_attrs);
1012 return ADS_ERROR(rc);
1015 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
1016 int scope, const char *expr,
1017 const char **attrs, LDAPMessage **res,
1018 int *count, struct berval **cookie)
1020 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
1025 * Get all results for a search. This uses ads_do_paged_search() to return
1026 * all entries in a large search.
1027 * @param ads connection to ads server
1028 * @param bind_path Base dn for the search
1029 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1030 * @param expr Search expression
1031 * @param attrs Attributes to retrieve
1032 * @param res ** which will contain results - free res* with ads_msgfree()
1033 * @return status of search
1035 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
1036 int scope, const char *expr,
1037 const char **attrs, void *args,
1040 struct berval *cookie = NULL;
1045 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1048 if (!ADS_ERR_OK(status))
1051 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1053 LDAPMessage *res2 = NULL;
1055 LDAPMessage *msg, *next;
1057 status2 = ads_do_paged_search_args(ads, bind_path, scope, expr,
1058 attrs, args, &res2, &count, &cookie);
1060 if (!ADS_ERR_OK(status2)) break;
1062 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1063 that this works on all ldap libs, but I have only tested with openldap */
1064 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1065 next = ads_next_message(ads, msg);
1066 ldap_add_result_entry((LDAPMessage **)res, msg);
1068 /* note that we do not free res2, as the memory is now
1069 part of the main returned list */
1072 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1073 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1079 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1080 int scope, const char *expr,
1081 const char **attrs, LDAPMessage **res)
1083 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1086 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1087 int scope, const char *expr,
1088 const char **attrs, uint32 sd_flags,
1093 args.control = ADS_SD_FLAGS_OID;
1094 args.val = sd_flags;
1095 args.critical = True;
1097 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1102 * Run a function on all results for a search. Uses ads_do_paged_search() and
1103 * runs the function as each page is returned, using ads_process_results()
1104 * @param ads connection to ads server
1105 * @param bind_path Base dn for the search
1106 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1107 * @param expr Search expression - specified in local charset
1108 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1109 * @param fn Function which takes attr name, values list, and data_area
1110 * @param data_area Pointer which is passed to function on each call
1111 * @return status of search
1113 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1114 int scope, const char *expr, const char **attrs,
1115 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1118 struct berval *cookie = NULL;
1123 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1126 if (!ADS_ERR_OK(status)) return status;
1128 ads_process_results(ads, res, fn, data_area);
1129 ads_msgfree(ads, res);
1132 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1133 &res, &count, &cookie);
1135 if (!ADS_ERR_OK(status)) break;
1137 ads_process_results(ads, res, fn, data_area);
1138 ads_msgfree(ads, res);
1145 * Do a search with a timeout.
1146 * @param ads connection to ads server
1147 * @param bind_path Base dn for the search
1148 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1149 * @param expr Search expression
1150 * @param attrs Attributes to retrieve
1151 * @param res ** which will contain results - free res* with ads_msgfree()
1152 * @return status of search
1154 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1156 const char **attrs, LDAPMessage **res)
1159 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1160 size_t converted_size;
1164 if (!(ctx = talloc_init("ads_do_search"))) {
1165 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1166 return ADS_ERROR(LDAP_NO_MEMORY);
1169 /* 0 means the conversion worked but the result was empty
1170 so we only fail if it's negative. In any case, it always
1171 at least nulls out the dest */
1172 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1173 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1175 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1176 rc = LDAP_NO_MEMORY;
1180 if (!attrs || !(*attrs))
1181 search_attrs = NULL;
1183 /* This would be the utf8-encoded version...*/
1184 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1185 if (!(search_attrs = str_list_copy(talloc_tos(), attrs)))
1187 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1188 rc = LDAP_NO_MEMORY;
1193 /* see the note in ads_do_paged_search - we *must* disable referrals */
1194 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1196 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1197 search_attrs, 0, NULL, NULL,
1199 (LDAPMessage **)res);
1201 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1202 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1207 talloc_destroy(ctx);
1208 /* if/when we decide to utf8-encode attrs, take out this next line */
1209 TALLOC_FREE(search_attrs);
1210 return ADS_ERROR(rc);
1213 * Do a general ADS search
1214 * @param ads connection to ads server
1215 * @param res ** which will contain results - free res* with ads_msgfree()
1216 * @param expr Search expression
1217 * @param attrs Attributes to retrieve
1218 * @return status of search
1220 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1221 const char *expr, const char **attrs)
1223 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1228 * Do a search on a specific DistinguishedName
1229 * @param ads connection to ads server
1230 * @param res ** which will contain results - free res* with ads_msgfree()
1231 * @param dn DistinguishName to search
1232 * @param attrs Attributes to retrieve
1233 * @return status of search
1235 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1236 const char *dn, const char **attrs)
1238 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1243 * Free up memory from a ads_search
1244 * @param ads connection to ads server
1245 * @param msg Search results to free
1247 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1254 * Free up memory from various ads requests
1255 * @param ads connection to ads server
1256 * @param mem Area to free
1258 void ads_memfree(ADS_STRUCT *ads, void *mem)
1264 * Get a dn from search results
1265 * @param ads connection to ads server
1266 * @param msg Search result
1269 char *ads_get_dn(ADS_STRUCT *ads, LDAPMessage *msg)
1271 char *utf8_dn, *unix_dn;
1272 size_t converted_size;
1274 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1277 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1281 if (!pull_utf8_allocate(&unix_dn, utf8_dn, &converted_size)) {
1282 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1286 ldap_memfree(utf8_dn);
1291 * Get the parent from a dn
1292 * @param dn the dn to return the parent from
1293 * @return parent dn string
1295 char *ads_parent_dn(const char *dn)
1303 p = strchr(dn, ',');
1313 * Find a machine account given a hostname
1314 * @param ads connection to ads server
1315 * @param res ** which will contain results - free res* with ads_msgfree()
1316 * @param host Hostname to search for
1317 * @return status of search
1319 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1320 const char *machine)
1324 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1328 /* the easiest way to find a machine account anywhere in the tree
1329 is to look for hostname$ */
1330 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1331 DEBUG(1, ("asprintf failed!\n"));
1332 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1335 status = ads_search(ads, res, expr, attrs);
1341 * Initialize a list of mods to be used in a modify request
1342 * @param ctx An initialized TALLOC_CTX
1343 * @return allocated ADS_MODLIST
1345 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1347 #define ADS_MODLIST_ALLOC_SIZE 10
1350 if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1351 /* -1 is safety to make sure we don't go over the end.
1352 need to reset it to NULL before doing ldap modify */
1353 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1355 return (ADS_MODLIST)mods;
1360 add an attribute to the list, with values list already constructed
1362 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1363 int mod_op, const char *name,
1364 const void *_invals)
1366 const void **invals = (const void **)_invals;
1368 LDAPMod **modlist = (LDAPMod **) *mods;
1369 struct berval **ber_values = NULL;
1370 char **char_values = NULL;
1373 mod_op = LDAP_MOD_DELETE;
1375 if (mod_op & LDAP_MOD_BVALUES)
1376 ber_values = ads_dup_values(ctx,
1377 (const struct berval **)invals);
1379 char_values = ads_push_strvals(ctx,
1380 (const char **) invals);
1383 /* find the first empty slot */
1384 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1386 if (modlist[curmod] == (LDAPMod *) -1) {
1387 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
1388 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1389 return ADS_ERROR(LDAP_NO_MEMORY);
1390 memset(&modlist[curmod], 0,
1391 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1392 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1393 *mods = (ADS_MODLIST)modlist;
1396 if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
1397 return ADS_ERROR(LDAP_NO_MEMORY);
1398 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1399 if (mod_op & LDAP_MOD_BVALUES) {
1400 modlist[curmod]->mod_bvalues = ber_values;
1401 } else if (mod_op & LDAP_MOD_DELETE) {
1402 modlist[curmod]->mod_values = NULL;
1404 modlist[curmod]->mod_values = char_values;
1407 modlist[curmod]->mod_op = mod_op;
1408 return ADS_ERROR(LDAP_SUCCESS);
1412 * Add a single string value to a mod list
1413 * @param ctx An initialized TALLOC_CTX
1414 * @param mods An initialized ADS_MODLIST
1415 * @param name The attribute name to add
1416 * @param val The value to add - NULL means DELETE
1417 * @return ADS STATUS indicating success of add
1419 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1420 const char *name, const char *val)
1422 const char *values[2];
1428 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1429 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1433 * Add an array of string values to a mod list
1434 * @param ctx An initialized TALLOC_CTX
1435 * @param mods An initialized ADS_MODLIST
1436 * @param name The attribute name to add
1437 * @param vals The array of string values to add - NULL means DELETE
1438 * @return ADS STATUS indicating success of add
1440 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1441 const char *name, const char **vals)
1444 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1445 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1446 name, (const void **) vals);
1451 * Add a single ber-encoded value to a mod list
1452 * @param ctx An initialized TALLOC_CTX
1453 * @param mods An initialized ADS_MODLIST
1454 * @param name The attribute name to add
1455 * @param val The value to add - NULL means DELETE
1456 * @return ADS STATUS indicating success of add
1458 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1459 const char *name, const struct berval *val)
1461 const struct berval *values[2];
1466 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1467 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1468 name, (const void **) values);
1473 * Perform an ldap modify
1474 * @param ads connection to ads server
1475 * @param mod_dn DistinguishedName to modify
1476 * @param mods list of modifications to perform
1477 * @return status of modify
1479 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1482 char *utf8_dn = NULL;
1483 size_t converted_size;
1485 this control is needed to modify that contains a currently
1486 non-existent attribute (but allowable for the object) to run
1488 LDAPControl PermitModify = {
1489 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
1492 LDAPControl *controls[2];
1494 controls[0] = &PermitModify;
1497 if (!push_utf8_allocate(&utf8_dn, mod_dn, &converted_size)) {
1498 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1501 /* find the end of the list, marked by NULL or -1 */
1502 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1503 /* make sure the end of the list is NULL */
1505 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1506 (LDAPMod **) mods, controls, NULL);
1508 return ADS_ERROR(ret);
1512 * Perform an ldap add
1513 * @param ads connection to ads server
1514 * @param new_dn DistinguishedName to add
1515 * @param mods list of attributes and values for DN
1516 * @return status of add
1518 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1521 char *utf8_dn = NULL;
1522 size_t converted_size;
1524 if (!push_utf8_allocate(&utf8_dn, new_dn, &converted_size)) {
1525 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1526 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1529 /* find the end of the list, marked by NULL or -1 */
1530 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1531 /* make sure the end of the list is NULL */
1534 ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1536 return ADS_ERROR(ret);
1540 * Delete a DistinguishedName
1541 * @param ads connection to ads server
1542 * @param new_dn DistinguishedName to delete
1543 * @return status of delete
1545 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1548 char *utf8_dn = NULL;
1549 size_t converted_size;
1550 if (!push_utf8_allocate(&utf8_dn, del_dn, &converted_size)) {
1551 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1552 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1555 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1557 return ADS_ERROR(ret);
1561 * Build an org unit string
1562 * if org unit is Computers or blank then assume a container, otherwise
1563 * assume a / separated list of organisational units.
1564 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1565 * @param ads connection to ads server
1566 * @param org_unit Organizational unit
1567 * @return org unit string - caller must free
1569 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1573 if (!org_unit || !*org_unit) {
1575 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1577 /* samba4 might not yet respond to a wellknownobject-query */
1578 return ret ? ret : SMB_STRDUP("cn=Computers");
1581 if (strequal(org_unit, "Computers")) {
1582 return SMB_STRDUP("cn=Computers");
1585 /* jmcd: removed "\\" from the separation chars, because it is
1586 needed as an escape for chars like '#' which are valid in an
1588 return ads_build_path(org_unit, "/", "ou=", 1);
1592 * Get a org unit string for a well-known GUID
1593 * @param ads connection to ads server
1594 * @param wknguid Well known GUID
1595 * @return org unit string - caller must free
1597 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1600 LDAPMessage *res = NULL;
1601 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1602 **bind_dn_exp = NULL;
1603 const char *attrs[] = {"distinguishedName", NULL};
1604 int new_ln, wkn_ln, bind_ln, i;
1606 if (wknguid == NULL) {
1610 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1611 DEBUG(1, ("asprintf failed!\n"));
1615 status = ads_search_dn(ads, &res, base, attrs);
1616 if (!ADS_ERR_OK(status)) {
1617 DEBUG(1,("Failed while searching for: %s\n", base));
1621 if (ads_count_replies(ads, res) != 1) {
1625 /* substitute the bind-path from the well-known-guid-search result */
1626 wkn_dn = ads_get_dn(ads, res);
1631 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1636 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1641 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1643 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1646 new_ln = wkn_ln - bind_ln;
1648 ret = SMB_STRDUP(wkn_dn_exp[0]);
1653 for (i=1; i < new_ln; i++) {
1656 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1662 ret = SMB_STRDUP(s);
1671 ads_msgfree(ads, res);
1672 ads_memfree(ads, wkn_dn);
1674 ldap_value_free(wkn_dn_exp);
1677 ldap_value_free(bind_dn_exp);
1684 * Adds (appends) an item to an attribute array, rather then
1685 * replacing the whole list
1686 * @param ctx An initialized TALLOC_CTX
1687 * @param mods An initialized ADS_MODLIST
1688 * @param name name of the ldap attribute to append to
1689 * @param vals an array of values to add
1690 * @return status of addition
1693 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1694 const char *name, const char **vals)
1696 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1697 (const void *) vals);
1701 * Determines the an account's current KVNO via an LDAP lookup
1702 * @param ads An initialized ADS_STRUCT
1703 * @param account_name the NT samaccountname.
1704 * @return the kvno for the account, or -1 in case of a failure.
1707 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
1709 LDAPMessage *res = NULL;
1710 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1712 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1713 char *dn_string = NULL;
1714 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1716 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
1717 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
1720 ret = ads_search(ads, &res, filter, attrs);
1722 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
1723 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
1724 ads_msgfree(ads, res);
1728 dn_string = ads_get_dn(ads, res);
1730 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1731 ads_msgfree(ads, res);
1734 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1735 ads_memfree(ads, dn_string);
1737 /* ---------------------------------------------------------
1738 * 0 is returned as a default KVNO from this point on...
1739 * This is done because Windows 2000 does not support key
1740 * version numbers. Chances are that a failure in the next
1741 * step is simply due to Windows 2000 being used for a
1742 * domain controller. */
1745 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1746 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1747 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1748 ads_msgfree(ads, res);
1753 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1754 ads_msgfree(ads, res);
1759 * Determines the computer account's current KVNO via an LDAP lookup
1760 * @param ads An initialized ADS_STRUCT
1761 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1762 * @return the kvno for the computer account, or -1 in case of a failure.
1765 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
1767 char *computer_account = NULL;
1770 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
1774 kvno = ads_get_kvno(ads, computer_account);
1775 free(computer_account);
1781 * This clears out all registered spn's for a given hostname
1782 * @param ads An initilaized ADS_STRUCT
1783 * @param machine_name the NetBIOS name of the computer.
1784 * @return 0 upon success, non-zero otherwise.
1787 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1790 LDAPMessage *res = NULL;
1792 const char *servicePrincipalName[1] = {NULL};
1793 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1794 char *dn_string = NULL;
1796 ret = ads_find_machine_acct(ads, &res, machine_name);
1797 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1798 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1799 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1800 ads_msgfree(ads, res);
1801 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1804 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1805 ctx = talloc_init("ads_clear_service_principal_names");
1807 ads_msgfree(ads, res);
1808 return ADS_ERROR(LDAP_NO_MEMORY);
1811 if (!(mods = ads_init_mods(ctx))) {
1812 talloc_destroy(ctx);
1813 ads_msgfree(ads, res);
1814 return ADS_ERROR(LDAP_NO_MEMORY);
1816 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1817 if (!ADS_ERR_OK(ret)) {
1818 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1819 ads_msgfree(ads, res);
1820 talloc_destroy(ctx);
1823 dn_string = ads_get_dn(ads, res);
1825 talloc_destroy(ctx);
1826 ads_msgfree(ads, res);
1827 return ADS_ERROR(LDAP_NO_MEMORY);
1829 ret = ads_gen_mod(ads, dn_string, mods);
1830 ads_memfree(ads,dn_string);
1831 if (!ADS_ERR_OK(ret)) {
1832 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1834 ads_msgfree(ads, res);
1835 talloc_destroy(ctx);
1839 ads_msgfree(ads, res);
1840 talloc_destroy(ctx);
1845 * This adds a service principal name to an existing computer account
1846 * (found by hostname) in AD.
1847 * @param ads An initialized ADS_STRUCT
1848 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1849 * @param my_fqdn The fully qualified DNS name of the machine
1850 * @param spn A string of the service principal to add, i.e. 'host'
1851 * @return 0 upon sucess, or non-zero if a failure occurs
1854 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1855 const char *my_fqdn, const char *spn)
1859 LDAPMessage *res = NULL;
1862 char *dn_string = NULL;
1863 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1865 ret = ads_find_machine_acct(ads, &res, machine_name);
1866 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1867 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1869 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1870 spn, machine_name, ads->config.realm));
1871 ads_msgfree(ads, res);
1872 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1875 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1876 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1877 ads_msgfree(ads, res);
1878 return ADS_ERROR(LDAP_NO_MEMORY);
1881 /* add short name spn */
1883 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1884 talloc_destroy(ctx);
1885 ads_msgfree(ads, res);
1886 return ADS_ERROR(LDAP_NO_MEMORY);
1889 strlower_m(&psp1[strlen(spn)]);
1890 servicePrincipalName[0] = psp1;
1892 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1893 psp1, machine_name));
1896 /* add fully qualified spn */
1898 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1899 ret = ADS_ERROR(LDAP_NO_MEMORY);
1903 strlower_m(&psp2[strlen(spn)]);
1904 servicePrincipalName[1] = psp2;
1906 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1907 psp2, machine_name));
1909 if ( (mods = ads_init_mods(ctx)) == NULL ) {
1910 ret = ADS_ERROR(LDAP_NO_MEMORY);
1914 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1915 if (!ADS_ERR_OK(ret)) {
1916 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1920 if ( (dn_string = ads_get_dn(ads, res)) == NULL ) {
1921 ret = ADS_ERROR(LDAP_NO_MEMORY);
1925 ret = ads_gen_mod(ads, dn_string, mods);
1926 ads_memfree(ads,dn_string);
1927 if (!ADS_ERR_OK(ret)) {
1928 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1934 ads_msgfree(ads, res);
1939 * adds a machine account to the ADS server
1940 * @param ads An intialized ADS_STRUCT
1941 * @param machine_name - the NetBIOS machine name of this account.
1942 * @param account_type A number indicating the type of account to create
1943 * @param org_unit The LDAP path in which to place this account
1944 * @return 0 upon success, or non-zero otherwise
1947 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1948 const char *org_unit)
1951 char *samAccountName, *controlstr;
1954 char *machine_escaped = NULL;
1956 const char *objectClass[] = {"top", "person", "organizationalPerson",
1957 "user", "computer", NULL};
1958 LDAPMessage *res = NULL;
1959 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
1960 UF_DONT_EXPIRE_PASSWD |\
1961 UF_ACCOUNTDISABLE );
1963 if (!(ctx = talloc_init("ads_add_machine_acct")))
1964 return ADS_ERROR(LDAP_NO_MEMORY);
1966 ret = ADS_ERROR(LDAP_NO_MEMORY);
1968 machine_escaped = escape_rdn_val_string_alloc(machine_name);
1969 if (!machine_escaped) {
1973 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
1974 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
1976 if ( !new_dn || !samAccountName ) {
1980 #ifndef ENCTYPE_ARCFOUR_HMAC
1981 acct_control |= UF_USE_DES_KEY_ONLY;
1984 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
1988 if (!(mods = ads_init_mods(ctx))) {
1992 ads_mod_str(ctx, &mods, "cn", machine_name);
1993 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1994 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1995 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1997 ret = ads_gen_add(ads, new_dn, mods);
2000 SAFE_FREE(machine_escaped);
2001 ads_msgfree(ads, res);
2002 talloc_destroy(ctx);
2008 * move a machine account to another OU on the ADS server
2009 * @param ads - An intialized ADS_STRUCT
2010 * @param machine_name - the NetBIOS machine name of this account.
2011 * @param org_unit - The LDAP path in which to place this account
2012 * @param moved - whether we moved the machine account (optional)
2013 * @return 0 upon success, or non-zero otherwise
2016 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2017 const char *org_unit, bool *moved)
2021 LDAPMessage *res = NULL;
2022 char *filter = NULL;
2023 char *computer_dn = NULL;
2025 char *computer_rdn = NULL;
2026 bool need_move = False;
2028 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2029 rc = ADS_ERROR(LDAP_NO_MEMORY);
2033 /* Find pre-existing machine */
2034 rc = ads_search(ads, &res, filter, NULL);
2035 if (!ADS_ERR_OK(rc)) {
2039 computer_dn = ads_get_dn(ads, res);
2041 rc = ADS_ERROR(LDAP_NO_MEMORY);
2045 parent_dn = ads_parent_dn(computer_dn);
2046 if (strequal(parent_dn, org_unit)) {
2052 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2053 rc = ADS_ERROR(LDAP_NO_MEMORY);
2057 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2058 org_unit, 1, NULL, NULL);
2059 rc = ADS_ERROR(ldap_status);
2062 ads_msgfree(ads, res);
2064 SAFE_FREE(computer_dn);
2065 SAFE_FREE(computer_rdn);
2067 if (!ADS_ERR_OK(rc)) {
2079 dump a binary result from ldap
2081 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2084 for (i=0; values[i]; i++) {
2085 printf("%s: ", field);
2086 for (j=0; j<values[i]->bv_len; j++) {
2087 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2093 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2096 for (i=0; values[i]; i++) {
2101 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
2102 smb_uuid_unpack(guid, &tmp);
2103 printf("%s: %s\n", field, GUID_string(talloc_tos(), &tmp));
2108 dump a sid result from ldap
2110 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2113 for (i=0; values[i]; i++) {
2116 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
2117 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
2122 dump ntSecurityDescriptor
2124 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2126 TALLOC_CTX *frame = talloc_stackframe();
2127 struct security_descriptor *psd;
2130 status = unmarshall_sec_desc(talloc_tos(), (uint8 *)values[0]->bv_val,
2131 values[0]->bv_len, &psd);
2132 if (!NT_STATUS_IS_OK(status)) {
2133 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2134 nt_errstr(status)));
2140 ads_disp_sd(ads, talloc_tos(), psd);
2147 dump a string result from ldap
2149 static void dump_string(const char *field, char **values)
2152 for (i=0; values[i]; i++) {
2153 printf("%s: %s\n", field, values[i]);
2158 dump a field from LDAP on stdout
2162 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2167 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2169 {"objectGUID", False, dump_guid},
2170 {"netbootGUID", False, dump_guid},
2171 {"nTSecurityDescriptor", False, dump_sd},
2172 {"dnsRecord", False, dump_binary},
2173 {"objectSid", False, dump_sid},
2174 {"tokenGroups", False, dump_sid},
2175 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2176 {"tokengroupsGlobalandUniversal", False, dump_sid},
2177 {"mS-DS-CreatorSID", False, dump_sid},
2178 {"msExchMailboxGuid", False, dump_guid},
2183 if (!field) { /* must be end of an entry */
2188 for (i=0; handlers[i].name; i++) {
2189 if (StrCaseCmp(handlers[i].name, field) == 0) {
2190 if (!values) /* first time, indicate string or not */
2191 return handlers[i].string;
2192 handlers[i].handler(ads, field, (struct berval **) values);
2196 if (!handlers[i].name) {
2197 if (!values) /* first time, indicate string conversion */
2199 dump_string(field, (char **)values);
2205 * Dump a result from LDAP on stdout
2206 * used for debugging
2207 * @param ads connection to ads server
2208 * @param res Results to dump
2211 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2213 ads_process_results(ads, res, ads_dump_field, NULL);
2217 * Walk through results, calling a function for each entry found.
2218 * The function receives a field name, a berval * array of values,
2219 * and a data area passed through from the start. The function is
2220 * called once with null for field and values at the end of each
2222 * @param ads connection to ads server
2223 * @param res Results to process
2224 * @param fn Function for processing each result
2225 * @param data_area user-defined area to pass to function
2227 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2228 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2233 size_t converted_size;
2235 if (!(ctx = talloc_init("ads_process_results")))
2238 for (msg = ads_first_entry(ads, res); msg;
2239 msg = ads_next_entry(ads, msg)) {
2243 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2244 (LDAPMessage *)msg,&b);
2246 utf8_field=ldap_next_attribute(ads->ldap.ld,
2247 (LDAPMessage *)msg,b)) {
2248 struct berval **ber_vals;
2249 char **str_vals, **utf8_vals;
2253 if (!pull_utf8_talloc(ctx, &field, utf8_field,
2256 DEBUG(0,("ads_process_results: "
2257 "pull_utf8_talloc failed: %s",
2261 string = fn(ads, field, NULL, data_area);
2264 utf8_vals = ldap_get_values(ads->ldap.ld,
2265 (LDAPMessage *)msg, field);
2266 str_vals = ads_pull_strvals(ctx,
2267 (const char **) utf8_vals);
2268 fn(ads, field, (void **) str_vals, data_area);
2269 ldap_value_free(utf8_vals);
2271 ber_vals = ldap_get_values_len(ads->ldap.ld,
2272 (LDAPMessage *)msg, field);
2273 fn(ads, field, (void **) ber_vals, data_area);
2275 ldap_value_free_len(ber_vals);
2277 ldap_memfree(utf8_field);
2280 talloc_free_children(ctx);
2281 fn(ads, NULL, NULL, data_area); /* completed an entry */
2284 talloc_destroy(ctx);
2288 * count how many replies are in a LDAPMessage
2289 * @param ads connection to ads server
2290 * @param res Results to count
2291 * @return number of replies
2293 int ads_count_replies(ADS_STRUCT *ads, void *res)
2295 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2299 * pull the first entry from a ADS result
2300 * @param ads connection to ads server
2301 * @param res Results of search
2302 * @return first entry from result
2304 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2306 return ldap_first_entry(ads->ldap.ld, res);
2310 * pull the next entry from a ADS result
2311 * @param ads connection to ads server
2312 * @param res Results of search
2313 * @return next entry from result
2315 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2317 return ldap_next_entry(ads->ldap.ld, res);
2321 * pull the first message from a ADS result
2322 * @param ads connection to ads server
2323 * @param res Results of search
2324 * @return first message from result
2326 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2328 return ldap_first_message(ads->ldap.ld, res);
2332 * pull the next message from a ADS result
2333 * @param ads connection to ads server
2334 * @param res Results of search
2335 * @return next message from result
2337 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2339 return ldap_next_message(ads->ldap.ld, res);
2343 * pull a single string from a ADS result
2344 * @param ads connection to ads server
2345 * @param mem_ctx TALLOC_CTX to use for allocating result string
2346 * @param msg Results of search
2347 * @param field Attribute to retrieve
2348 * @return Result string in talloc context
2350 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2356 size_t converted_size;
2358 values = ldap_get_values(ads->ldap.ld, msg, field);
2362 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2367 ldap_value_free(values);
2372 * pull an array of strings from a ADS result
2373 * @param ads connection to ads server
2374 * @param mem_ctx TALLOC_CTX to use for allocating result string
2375 * @param msg Results of search
2376 * @param field Attribute to retrieve
2377 * @return Result strings in talloc context
2379 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2380 LDAPMessage *msg, const char *field,
2386 size_t converted_size;
2388 values = ldap_get_values(ads->ldap.ld, msg, field);
2392 *num_values = ldap_count_values(values);
2394 ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
2396 ldap_value_free(values);
2400 for (i=0;i<*num_values;i++) {
2401 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2404 ldap_value_free(values);
2410 ldap_value_free(values);
2415 * pull an array of strings from a ADS result
2416 * (handle large multivalue attributes with range retrieval)
2417 * @param ads connection to ads server
2418 * @param mem_ctx TALLOC_CTX to use for allocating result string
2419 * @param msg Results of search
2420 * @param field Attribute to retrieve
2421 * @param current_strings strings returned by a previous call to this function
2422 * @param next_attribute The next query should ask for this attribute
2423 * @param num_values How many values did we get this time?
2424 * @param more_values Are there more values to get?
2425 * @return Result strings in talloc context
2427 char **ads_pull_strings_range(ADS_STRUCT *ads,
2428 TALLOC_CTX *mem_ctx,
2429 LDAPMessage *msg, const char *field,
2430 char **current_strings,
2431 const char **next_attribute,
2432 size_t *num_strings,
2436 char *expected_range_attrib, *range_attr;
2437 BerElement *ptr = NULL;
2440 size_t num_new_strings;
2441 unsigned long int range_start;
2442 unsigned long int range_end;
2444 /* we might have been given the whole lot anyway */
2445 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2446 *more_strings = False;
2450 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2452 /* look for Range result */
2453 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2455 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2456 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2457 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2465 /* nothing here - this field is just empty */
2466 *more_strings = False;
2470 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2471 &range_start, &range_end) == 2) {
2472 *more_strings = True;
2474 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2475 &range_start) == 1) {
2476 *more_strings = False;
2478 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2480 ldap_memfree(range_attr);
2481 *more_strings = False;
2486 if ((*num_strings) != range_start) {
2487 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2488 " - aborting range retreival\n",
2489 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2490 ldap_memfree(range_attr);
2491 *more_strings = False;
2495 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2497 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2498 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2499 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2500 range_attr, (unsigned long int)range_end - range_start + 1,
2501 (unsigned long int)num_new_strings));
2502 ldap_memfree(range_attr);
2503 *more_strings = False;
2507 strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2508 *num_strings + num_new_strings);
2510 if (strings == NULL) {
2511 ldap_memfree(range_attr);
2512 *more_strings = False;
2516 if (new_strings && num_new_strings) {
2517 memcpy(&strings[*num_strings], new_strings,
2518 sizeof(*new_strings) * num_new_strings);
2521 (*num_strings) += num_new_strings;
2523 if (*more_strings) {
2524 *next_attribute = talloc_asprintf(mem_ctx,
2529 if (!*next_attribute) {
2530 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2531 ldap_memfree(range_attr);
2532 *more_strings = False;
2537 ldap_memfree(range_attr);
2543 * pull a single uint32 from a ADS result
2544 * @param ads connection to ads server
2545 * @param msg Results of search
2546 * @param field Attribute to retrieve
2547 * @param v Pointer to int to store result
2548 * @return boolean inidicating success
2550 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2555 values = ldap_get_values(ads->ldap.ld, msg, field);
2559 ldap_value_free(values);
2563 *v = atoi(values[0]);
2564 ldap_value_free(values);
2569 * pull a single objectGUID from an ADS result
2570 * @param ads connection to ADS server
2571 * @param msg results of search
2572 * @param guid 37-byte area to receive text guid
2573 * @return boolean indicating success
2575 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2578 UUID_FLAT flat_guid;
2580 values = ldap_get_values(ads->ldap.ld, msg, "objectGUID");
2585 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2586 smb_uuid_unpack(flat_guid, guid);
2587 ldap_value_free(values);
2590 ldap_value_free(values);
2597 * pull a single DOM_SID from a ADS result
2598 * @param ads connection to ads server
2599 * @param msg Results of search
2600 * @param field Attribute to retrieve
2601 * @param sid Pointer to sid to store result
2602 * @return boolean inidicating success
2604 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2607 struct berval **values;
2610 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2616 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2618 ldap_value_free_len(values);
2623 * pull an array of DOM_SIDs from a ADS result
2624 * @param ads connection to ads server
2625 * @param mem_ctx TALLOC_CTX for allocating sid array
2626 * @param msg Results of search
2627 * @param field Attribute to retrieve
2628 * @param sids pointer to sid array to allocate
2629 * @return the count of SIDs pulled
2631 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2632 LDAPMessage *msg, const char *field, DOM_SID **sids)
2634 struct berval **values;
2638 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2643 for (i=0; values[i]; i++)
2647 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2649 ldap_value_free_len(values);
2657 for (i=0; values[i]; i++) {
2658 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2660 DEBUG(10, ("pulling SID: %s\n",
2661 sid_string_dbg(&(*sids)[count])));
2666 ldap_value_free_len(values);
2671 * pull a SEC_DESC from a ADS result
2672 * @param ads connection to ads server
2673 * @param mem_ctx TALLOC_CTX for allocating sid array
2674 * @param msg Results of search
2675 * @param field Attribute to retrieve
2676 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2677 * @return boolean inidicating success
2679 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2680 LDAPMessage *msg, const char *field, SEC_DESC **sd)
2682 struct berval **values;
2685 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2687 if (!values) return false;
2691 status = unmarshall_sec_desc(mem_ctx,
2692 (uint8 *)values[0]->bv_val,
2693 values[0]->bv_len, sd);
2694 if (!NT_STATUS_IS_OK(status)) {
2695 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2696 nt_errstr(status)));
2701 ldap_value_free_len(values);
2706 * in order to support usernames longer than 21 characters we need to
2707 * use both the sAMAccountName and the userPrincipalName attributes
2708 * It seems that not all users have the userPrincipalName attribute set
2710 * @param ads connection to ads server
2711 * @param mem_ctx TALLOC_CTX for allocating sid array
2712 * @param msg Results of search
2713 * @return the username
2715 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2721 /* lookup_name() only works on the sAMAccountName to
2722 returning the username portion of userPrincipalName
2723 breaks winbindd_getpwnam() */
2725 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2726 if (ret && (p = strchr_m(ret, '@'))) {
2731 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2736 * find the update serial number - this is the core of the ldap cache
2737 * @param ads connection to ads server
2738 * @param ads connection to ADS server
2739 * @param usn Pointer to retrieved update serial number
2740 * @return status of search
2742 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2744 const char *attrs[] = {"highestCommittedUSN", NULL};
2748 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2749 if (!ADS_ERR_OK(status))
2752 if (ads_count_replies(ads, res) != 1) {
2753 ads_msgfree(ads, res);
2754 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2757 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2758 ads_msgfree(ads, res);
2759 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2762 ads_msgfree(ads, res);
2766 /* parse a ADS timestring - typical string is
2767 '20020917091222.0Z0' which means 09:12.22 17th September
2769 static time_t ads_parse_time(const char *str)
2775 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2776 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2777 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2786 /********************************************************************
2787 ********************************************************************/
2789 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2791 const char *attrs[] = {"currentTime", NULL};
2796 ADS_STRUCT *ads_s = ads;
2798 if (!(ctx = talloc_init("ads_current_time"))) {
2799 return ADS_ERROR(LDAP_NO_MEMORY);
2802 /* establish a new ldap tcp session if necessary */
2804 if ( !ads->ldap.ld ) {
2805 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2806 ads->server.ldap_server )) == NULL )
2810 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2811 status = ads_connect( ads_s );
2812 if ( !ADS_ERR_OK(status))
2816 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2817 if (!ADS_ERR_OK(status)) {
2821 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2823 ads_msgfree(ads_s, res);
2824 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2828 /* but save the time and offset in the original ADS_STRUCT */
2830 ads->config.current_time = ads_parse_time(timestr);
2832 if (ads->config.current_time != 0) {
2833 ads->auth.time_offset = ads->config.current_time - time(NULL);
2834 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2837 ads_msgfree(ads, res);
2839 status = ADS_SUCCESS;
2842 /* free any temporary ads connections */
2843 if ( ads_s != ads ) {
2844 ads_destroy( &ads_s );
2846 talloc_destroy(ctx);
2851 /********************************************************************
2852 ********************************************************************/
2854 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2856 const char *attrs[] = {"domainFunctionality", NULL};
2859 ADS_STRUCT *ads_s = ads;
2861 *val = DS_DOMAIN_FUNCTION_2000;
2863 /* establish a new ldap tcp session if necessary */
2865 if ( !ads->ldap.ld ) {
2866 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2867 ads->server.ldap_server )) == NULL )
2869 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2872 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2873 status = ads_connect( ads_s );
2874 if ( !ADS_ERR_OK(status))
2878 /* If the attribute does not exist assume it is a Windows 2000
2879 functional domain */
2881 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2882 if (!ADS_ERR_OK(status)) {
2883 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2884 status = ADS_SUCCESS;
2889 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2890 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2892 DEBUG(3,("ads_domain_func_level: %d\n", *val));
2895 ads_msgfree(ads, res);
2898 /* free any temporary ads connections */
2899 if ( ads_s != ads ) {
2900 ads_destroy( &ads_s );
2907 * find the domain sid for our domain
2908 * @param ads connection to ads server
2909 * @param sid Pointer to domain sid
2910 * @return status of search
2912 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2914 const char *attrs[] = {"objectSid", NULL};
2918 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2920 if (!ADS_ERR_OK(rc)) return rc;
2921 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2922 ads_msgfree(ads, res);
2923 return ADS_ERROR_SYSTEM(ENOENT);
2925 ads_msgfree(ads, res);
2931 * find our site name
2932 * @param ads connection to ads server
2933 * @param mem_ctx Pointer to talloc context
2934 * @param site_name Pointer to the sitename
2935 * @return status of search
2937 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2941 const char *dn, *service_name;
2942 const char *attrs[] = { "dsServiceName", NULL };
2944 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2945 if (!ADS_ERR_OK(status)) {
2949 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2950 if (service_name == NULL) {
2951 ads_msgfree(ads, res);
2952 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2955 ads_msgfree(ads, res);
2957 /* go up three levels */
2958 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2960 return ADS_ERROR(LDAP_NO_MEMORY);
2963 *site_name = talloc_strdup(mem_ctx, dn);
2964 if (*site_name == NULL) {
2965 return ADS_ERROR(LDAP_NO_MEMORY);
2970 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2975 * find the site dn where a machine resides
2976 * @param ads connection to ads server
2977 * @param mem_ctx Pointer to talloc context
2978 * @param computer_name name of the machine
2979 * @param site_name Pointer to the sitename
2980 * @return status of search
2982 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
2986 const char *parent, *filter;
2987 char *config_context = NULL;
2990 /* shortcut a query */
2991 if (strequal(computer_name, ads->config.ldap_server_name)) {
2992 return ads_site_dn(ads, mem_ctx, site_dn);
2995 status = ads_config_path(ads, mem_ctx, &config_context);
2996 if (!ADS_ERR_OK(status)) {
3000 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3001 if (filter == NULL) {
3002 return ADS_ERROR(LDAP_NO_MEMORY);
3005 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3006 filter, NULL, &res);
3007 if (!ADS_ERR_OK(status)) {
3011 if (ads_count_replies(ads, res) != 1) {
3012 ads_msgfree(ads, res);
3013 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3016 dn = ads_get_dn(ads, res);
3018 ads_msgfree(ads, res);
3019 return ADS_ERROR(LDAP_NO_MEMORY);
3022 /* go up three levels */
3023 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3024 if (parent == NULL) {
3025 ads_msgfree(ads, res);
3026 ads_memfree(ads, dn);
3027 return ADS_ERROR(LDAP_NO_MEMORY);
3030 *site_dn = talloc_strdup(mem_ctx, parent);
3031 if (*site_dn == NULL) {
3032 ads_msgfree(ads, res);
3033 ads_memfree(ads, dn);
3034 return ADS_ERROR(LDAP_NO_MEMORY);
3037 ads_memfree(ads, dn);
3038 ads_msgfree(ads, res);
3044 * get the upn suffixes for a domain
3045 * @param ads connection to ads server
3046 * @param mem_ctx Pointer to talloc context
3047 * @param suffixes Pointer to an array of suffixes
3048 * @param num_suffixes Pointer to the number of suffixes
3049 * @return status of search
3051 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3056 char *config_context = NULL;
3057 const char *attrs[] = { "uPNSuffixes", NULL };
3059 status = ads_config_path(ads, mem_ctx, &config_context);
3060 if (!ADS_ERR_OK(status)) {
3064 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3066 return ADS_ERROR(LDAP_NO_MEMORY);
3069 status = ads_search_dn(ads, &res, base, attrs);
3070 if (!ADS_ERR_OK(status)) {
3074 if (ads_count_replies(ads, res) != 1) {
3075 ads_msgfree(ads, res);
3076 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3079 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3080 if ((*suffixes) == NULL) {
3081 ads_msgfree(ads, res);
3082 return ADS_ERROR(LDAP_NO_MEMORY);
3085 ads_msgfree(ads, res);
3091 * get the joinable ous for a domain
3092 * @param ads connection to ads server
3093 * @param mem_ctx Pointer to talloc context
3094 * @param ous Pointer to an array of ous
3095 * @param num_ous Pointer to the number of ous
3096 * @return status of search
3098 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3099 TALLOC_CTX *mem_ctx,
3104 LDAPMessage *res = NULL;
3105 LDAPMessage *msg = NULL;
3106 const char *attrs[] = { "dn", NULL };
3109 status = ads_search(ads, &res,
3110 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3112 if (!ADS_ERR_OK(status)) {
3116 count = ads_count_replies(ads, res);
3118 ads_msgfree(ads, res);
3119 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3122 for (msg = ads_first_entry(ads, res); msg;
3123 msg = ads_next_entry(ads, msg)) {
3127 dn = ads_get_dn(ads, msg);
3129 ads_msgfree(ads, res);
3130 return ADS_ERROR(LDAP_NO_MEMORY);
3133 if (!add_string_to_array(mem_ctx, dn,
3134 (const char ***)ous,
3136 ads_memfree(ads, dn);
3137 ads_msgfree(ads, res);
3138 return ADS_ERROR(LDAP_NO_MEMORY);
3141 ads_memfree(ads, dn);
3144 ads_msgfree(ads, res);
3151 * pull a DOM_SID from an extended dn string
3152 * @param mem_ctx TALLOC_CTX
3153 * @param extended_dn string
3154 * @param flags string type of extended_dn
3155 * @param sid pointer to a DOM_SID
3156 * @return NT_STATUS_OK on success,
3157 * NT_INVALID_PARAMETER on error,
3158 * NT_STATUS_NOT_FOUND if no SID present
3160 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3161 const char *extended_dn,
3162 enum ads_extended_dn_flags flags,
3168 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3171 /* otherwise extended_dn gets stripped off */
3172 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3173 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3176 * ADS_EXTENDED_DN_HEX_STRING:
3177 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3179 * ADS_EXTENDED_DN_STRING (only with w2k3):
3180 * <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
3182 * Object with no SID, such as an Exchange Public Folder
3183 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3186 p = strchr(dn, ';');
3188 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3191 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3192 DEBUG(5,("No SID present in extended dn\n"));
3193 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3196 p += strlen(";<SID=");
3200 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3205 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3209 case ADS_EXTENDED_DN_STRING:
3210 if (!string_to_sid(sid, p)) {
3211 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3214 case ADS_EXTENDED_DN_HEX_STRING: {
3218 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3220 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3223 if (!sid_parse(buf, buf_len, sid)) {
3224 DEBUG(10,("failed to parse sid\n"));
3225 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3230 DEBUG(10,("unknown extended dn format\n"));
3231 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3234 return ADS_ERROR_NT(NT_STATUS_OK);
3238 * pull an array of DOM_SIDs from a ADS result
3239 * @param ads connection to ads server
3240 * @param mem_ctx TALLOC_CTX for allocating sid array
3241 * @param msg Results of search
3242 * @param field Attribute to retrieve
3243 * @param flags string type of extended_dn
3244 * @param sids pointer to sid array to allocate
3245 * @return the count of SIDs pulled
3247 int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads,
3248 TALLOC_CTX *mem_ctx,
3251 enum ads_extended_dn_flags flags,
3256 size_t dn_count, ret_count = 0;
3259 if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field,
3260 &dn_count)) == NULL) {
3264 (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, dn_count + 1);
3266 TALLOC_FREE(dn_strings);
3270 for (i=0; i<dn_count; i++) {
3271 rc = ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i],
3272 flags, &(*sids)[i]);
3273 if (!ADS_ERR_OK(rc)) {
3274 if (NT_STATUS_EQUAL(ads_ntstatus(rc),
3275 NT_STATUS_NOT_FOUND)) {
3280 TALLOC_FREE(dn_strings);
3287 TALLOC_FREE(dn_strings);
3292 /********************************************************************
3293 ********************************************************************/
3295 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3297 LDAPMessage *res = NULL;
3302 status = ads_find_machine_acct(ads, &res, global_myname());
3303 if (!ADS_ERR_OK(status)) {
3304 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3309 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3310 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3314 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3315 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3319 ads_msgfree(ads, res);
3324 /********************************************************************
3325 ********************************************************************/
3327 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3329 LDAPMessage *res = NULL;
3334 status = ads_find_machine_acct(ads, &res, machine_name);
3335 if (!ADS_ERR_OK(status)) {
3336 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3341 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3342 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3346 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3347 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3351 ads_msgfree(ads, res);
3356 /********************************************************************
3357 ********************************************************************/
3359 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3361 LDAPMessage *res = NULL;
3366 status = ads_find_machine_acct(ads, &res, global_myname());
3367 if (!ADS_ERR_OK(status)) {
3368 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3373 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3374 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3378 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3379 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3383 ads_msgfree(ads, res);
3390 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3393 * Join a machine to a realm
3394 * Creates the machine account and sets the machine password
3395 * @param ads connection to ads server
3396 * @param machine name of host to add
3397 * @param org_unit Organizational unit to place machine in
3398 * @return status of join
3400 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3401 uint32 account_type, const char *org_unit)
3404 LDAPMessage *res = NULL;
3407 /* machine name must be lowercase */
3408 machine = SMB_STRDUP(machine_name);
3409 strlower_m(machine);
3412 status = ads_find_machine_acct(ads, (void **)&res, machine);
3413 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3414 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3415 status = ads_leave_realm(ads, machine);
3416 if (!ADS_ERR_OK(status)) {
3417 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3418 machine, ads->config.realm));
3423 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3424 if (!ADS_ERR_OK(status)) {
3425 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3430 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3431 if (!ADS_ERR_OK(status)) {
3432 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3438 ads_msgfree(ads, res);
3445 * Delete a machine from the realm
3446 * @param ads connection to ads server
3447 * @param hostname Machine to remove
3448 * @return status of delete
3450 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3455 char *hostnameDN, *host;
3457 LDAPControl ldap_control;
3458 LDAPControl * pldap_control[2] = {NULL, NULL};
3460 pldap_control[0] = &ldap_control;
3461 memset(&ldap_control, 0, sizeof(LDAPControl));
3462 ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
3464 /* hostname must be lowercase */
3465 host = SMB_STRDUP(hostname);
3468 status = ads_find_machine_acct(ads, &res, host);
3469 if (!ADS_ERR_OK(status)) {
3470 DEBUG(0, ("Host account for %s does not exist.\n", host));
3475 msg = ads_first_entry(ads, res);
3478 return ADS_ERROR_SYSTEM(ENOENT);
3481 hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
3483 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3485 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3487 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3490 if (rc != LDAP_SUCCESS) {
3491 const char *attrs[] = { "cn", NULL };
3492 LDAPMessage *msg_sub;
3494 /* we only search with scope ONE, we do not expect any further
3495 * objects to be created deeper */
3497 status = ads_do_search_retry(ads, hostnameDN,
3498 LDAP_SCOPE_ONELEVEL,
3499 "(objectclass=*)", attrs, &res);
3501 if (!ADS_ERR_OK(status)) {
3503 ads_memfree(ads, hostnameDN);
3507 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3508 msg_sub = ads_next_entry(ads, msg_sub)) {
3512 if ((dn = ads_get_dn(ads, msg_sub)) == NULL) {
3514 ads_memfree(ads, hostnameDN);
3515 return ADS_ERROR(LDAP_NO_MEMORY);
3518 status = ads_del_dn(ads, dn);
3519 if (!ADS_ERR_OK(status)) {
3520 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3522 ads_memfree(ads, dn);
3523 ads_memfree(ads, hostnameDN);
3527 ads_memfree(ads, dn);
3530 /* there should be no subordinate objects anymore */
3531 status = ads_do_search_retry(ads, hostnameDN,
3532 LDAP_SCOPE_ONELEVEL,
3533 "(objectclass=*)", attrs, &res);
3535 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3537 ads_memfree(ads, hostnameDN);
3541 /* delete hostnameDN now */
3542 status = ads_del_dn(ads, hostnameDN);
3543 if (!ADS_ERR_OK(status)) {
3545 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3546 ads_memfree(ads, hostnameDN);
3551 ads_memfree(ads, hostnameDN);
3553 status = ads_find_machine_acct(ads, &res, host);
3554 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3555 DEBUG(3, ("Failed to remove host account.\n"));
3565 * pull all token-sids from an LDAP dn
3566 * @param ads connection to ads server
3567 * @param mem_ctx TALLOC_CTX for allocating sid array
3568 * @param dn of LDAP object
3569 * @param user_sid pointer to DOM_SID (objectSid)
3570 * @param primary_group_sid pointer to DOM_SID (self composed)
3571 * @param sids pointer to sid array to allocate
3572 * @param num_sids counter of SIDs pulled
3573 * @return status of token query
3575 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3576 TALLOC_CTX *mem_ctx,
3579 DOM_SID *primary_group_sid,
3584 LDAPMessage *res = NULL;
3586 size_t tmp_num_sids;
3588 DOM_SID tmp_user_sid;
3589 DOM_SID tmp_primary_group_sid;
3591 const char *attrs[] = {
3598 status = ads_search_retry_dn(ads, &res, dn, attrs);
3599 if (!ADS_ERR_OK(status)) {
3603 count = ads_count_replies(ads, res);
3605 ads_msgfree(ads, res);
3606 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3609 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3610 ads_msgfree(ads, res);
3611 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3614 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3615 ads_msgfree(ads, res);
3616 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3620 /* hack to compose the primary group sid without knowing the
3626 sid_copy(&domsid, &tmp_user_sid);
3628 if (!sid_split_rid(&domsid, &dummy_rid)) {
3629 ads_msgfree(ads, res);
3630 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3633 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3634 ads_msgfree(ads, res);
3635 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3639 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3641 if (tmp_num_sids == 0 || !tmp_sids) {
3642 ads_msgfree(ads, res);
3643 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3647 *num_sids = tmp_num_sids;
3655 *user_sid = tmp_user_sid;
3658 if (primary_group_sid) {
3659 *primary_group_sid = tmp_primary_group_sid;
3662 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3664 ads_msgfree(ads, res);
3665 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3669 * Find a sAMAccoutName in LDAP
3670 * @param ads connection to ads server
3671 * @param mem_ctx TALLOC_CTX for allocating sid array
3672 * @param samaccountname to search
3673 * @param uac_ret uint32 pointer userAccountControl attribute value
3674 * @param dn_ret pointer to dn
3675 * @return status of token query
3677 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3678 TALLOC_CTX *mem_ctx,
3679 const char *samaccountname,
3681 const char **dn_ret)
3684 const char *attrs[] = { "userAccountControl", NULL };
3686 LDAPMessage *res = NULL;
3690 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3692 if (filter == NULL) {
3693 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3697 status = ads_do_search_all(ads, ads->config.bind_path,
3699 filter, attrs, &res);
3701 if (!ADS_ERR_OK(status)) {
3705 if (ads_count_replies(ads, res) != 1) {
3706 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3710 dn = ads_get_dn(ads, res);
3712 status = ADS_ERROR(LDAP_NO_MEMORY);
3716 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3717 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3726 *dn_ret = talloc_strdup(mem_ctx, dn);
3728 status = ADS_ERROR(LDAP_NO_MEMORY);
3733 ads_memfree(ads, dn);
3734 ads_msgfree(ads, res);
3740 * find our configuration path
3741 * @param ads connection to ads server
3742 * @param mem_ctx Pointer to talloc context
3743 * @param config_path Pointer to the config path
3744 * @return status of search
3746 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3747 TALLOC_CTX *mem_ctx,
3751 LDAPMessage *res = NULL;
3752 const char *config_context = NULL;
3753 const char *attrs[] = { "configurationNamingContext", NULL };
3755 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3756 "(objectclass=*)", attrs, &res);
3757 if (!ADS_ERR_OK(status)) {
3761 config_context = ads_pull_string(ads, mem_ctx, res,
3762 "configurationNamingContext");
3763 ads_msgfree(ads, res);
3764 if (!config_context) {
3765 return ADS_ERROR(LDAP_NO_MEMORY);
3769 *config_path = talloc_strdup(mem_ctx, config_context);
3770 if (!*config_path) {
3771 return ADS_ERROR(LDAP_NO_MEMORY);
3775 return ADS_ERROR(LDAP_SUCCESS);
3779 * find the displayName of an extended right
3780 * @param ads connection to ads server
3781 * @param config_path The config path
3782 * @param mem_ctx Pointer to talloc context
3783 * @param GUID struct of the rightsGUID
3784 * @return status of search
3786 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3787 const char *config_path,
3788 TALLOC_CTX *mem_ctx,
3789 const struct GUID *rights_guid)
3792 LDAPMessage *res = NULL;
3794 const char *attrs[] = { "displayName", NULL };
3795 const char *result = NULL;
3798 if (!ads || !mem_ctx || !rights_guid) {
3802 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3803 GUID_string(mem_ctx, rights_guid));
3808 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3813 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3815 if (!ADS_ERR_OK(rc)) {
3819 if (ads_count_replies(ads, res) != 1) {
3823 result = ads_pull_string(ads, mem_ctx, res, "displayName");
3826 ads_msgfree(ads, res);
3832 * verify or build and verify an account ou
3833 * @param mem_ctx Pointer to talloc context
3834 * @param ads connection to ads server
3836 * @return status of search
3839 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3841 const char **account_ou)
3843 struct ldb_dn *name_dn = NULL;
3844 const char *name = NULL;
3845 char *ou_string = NULL;
3847 name_dn = ldb_dn_explode(mem_ctx, *account_ou);
3852 ou_string = ads_ou_string(ads, *account_ou);
3854 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3857 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3858 ads->config.bind_path);
3859 SAFE_FREE(ou_string);
3861 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3864 name_dn = ldb_dn_explode(mem_ctx, name);
3866 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3869 *account_ou = talloc_strdup(mem_ctx, name);
3871 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);