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/>.
26 #include "libads/sitename_cache.h"
27 #include "libads/cldap.h"
28 #include "../lib/addns/dnsquery.h"
29 #include "../libds/common/flags.h"
31 #include "../libcli/security/security.h"
32 #include "lib/param/loadparm.h"
38 * @brief basic ldap client-side routines for ads server communications
40 * The routines contained here should do the necessary ldap calls for
43 * Important note: attribute names passed into ads_ routines must
44 * already be in UTF-8 format. We do not convert them because in almost
45 * all cases, they are just ascii (which is represented with the same
46 * codepoints in UTF-8). This may have to change at some point
50 #define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"
52 static SIG_ATOMIC_T gotalarm;
54 /***************************************************************
55 Signal function to tell us we timed out.
56 ****************************************************************/
58 static void gotalarm_sig(int signum)
63 LDAP *ldap_open_with_timeout(const char *server,
64 struct sockaddr_storage *ss,
65 int port, unsigned int to)
71 DEBUG(10, ("Opening connection to LDAP server '%s:%d', timeout "
72 "%u seconds\n", server, port, to));
74 #if defined(HAVE_LDAP_INIT_FD) && defined(SOCKET_WRAPPER)
75 /* Only use this private LDAP function if we are in make test,
76 * as this is the best way to get the emulated TCP socket into
78 if (socket_wrapper_dir() != NULL) {
83 status = open_socket_out(ss, port, to, &fd);
85 if (!NT_STATUS_IS_OK(status)) {
89 #ifndef LDAP_PROTO_TCP
90 #define LDAP_PROTO_TCP 1
92 uri = talloc_asprintf(talloc_tos(), "ldap://%s:%u", server, port);
96 ldap_err = ldap_init_fd(fd, LDAP_PROTO_TCP, uri, &ldp);
99 if (ldap_err != LDAP_SUCCESS) {
109 CatchSignal(SIGALRM, gotalarm_sig);
111 /* End setup timeout. */
114 uri = talloc_asprintf(talloc_tos(), "ldap://%s:%u", server, port);
119 #ifdef HAVE_LDAP_INITIALIZE
120 ldap_err = ldap_initialize(&ldp, uri);
122 ldp = ldap_open(server, port);
124 ldap_err = LDAP_SUCCESS;
126 ldap_err = LDAP_OTHER;
129 if (ldap_err != LDAP_SUCCESS) {
130 DEBUG(2,("Could not initialize connection for LDAP server '%s': %s\n",
131 uri, ldap_err2string(ldap_err)));
133 DEBUG(10, ("Initialized connection for LDAP server '%s'\n", uri));
137 /* Teardown timeout. */
139 CatchSignal(SIGALRM, SIG_IGN);
145 static int ldap_search_with_timeout(LDAP *ld,
146 LDAP_CONST char *base,
148 LDAP_CONST char *filter,
151 LDAPControl **sctrls,
152 LDAPControl **cctrls,
156 int to = lp_ldap_timeout();
157 struct timeval timeout;
158 struct timeval *timeout_ptr = NULL;
161 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
167 timeout_ptr = &timeout;
169 /* Setup alarm timeout. */
170 CatchSignal(SIGALRM, gotalarm_sig);
171 /* Make the alarm time one second beyond
172 the timout we're setting for the
173 remote search timeout, to allow that
174 to fire in preference. */
176 /* End setup timeout. */
180 result = ldap_search_ext_s(ld, base, scope, filter, attrs,
181 attrsonly, sctrls, cctrls, timeout_ptr,
185 /* Teardown alarm timeout. */
186 CatchSignal(SIGALRM, SIG_IGN);
191 return LDAP_TIMELIMIT_EXCEEDED;
194 * A bug in OpenLDAP means ldap_search_ext_s can return
195 * LDAP_SUCCESS but with a NULL res pointer. Cope with
196 * this. See bug #6279 for details. JRA.
200 return LDAP_TIMELIMIT_EXCEEDED;
206 /**********************************************
207 Do client and server sitename match ?
208 **********************************************/
210 bool ads_sitename_match(ADS_STRUCT *ads)
212 if (ads->config.server_site_name == NULL &&
213 ads->config.client_site_name == NULL ) {
214 DEBUG(10,("ads_sitename_match: both null\n"));
217 if (ads->config.server_site_name &&
218 ads->config.client_site_name &&
219 strequal(ads->config.server_site_name,
220 ads->config.client_site_name)) {
221 DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
224 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
225 ads->config.server_site_name ? ads->config.server_site_name : "NULL",
226 ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
230 /**********************************************
231 Is this the closest DC ?
232 **********************************************/
234 bool ads_closest_dc(ADS_STRUCT *ads)
236 if (ads->config.flags & NBT_SERVER_CLOSEST) {
237 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag set\n"));
241 /* not sure if this can ever happen */
242 if (ads_sitename_match(ads)) {
243 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag not set but sites match\n"));
247 if (ads->config.client_site_name == NULL) {
248 DEBUG(10,("ads_closest_dc: client belongs to no site\n"));
252 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
253 ads->config.ldap_server_name));
260 try a connection to a given ldap server, returning True and setting the servers IP
261 in the ads struct if successful
263 static bool ads_try_connect(ADS_STRUCT *ads, const char *server, bool gc)
265 struct NETLOGON_SAM_LOGON_RESPONSE_EX cldap_reply;
266 TALLOC_CTX *frame = talloc_stackframe();
268 struct sockaddr_storage ss;
269 char addr[INET6_ADDRSTRLEN];
271 if (!server || !*server) {
276 if (!resolve_name(server, &ss, 0x20, true)) {
277 DEBUG(5,("ads_try_connect: unable to resolve name %s\n",
282 print_sockaddr(addr, sizeof(addr), &ss);
284 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
285 addr, ads->server.realm));
287 ZERO_STRUCT( cldap_reply );
289 if ( !ads_cldap_netlogon_5(frame, &ss, ads->server.realm, &cldap_reply ) ) {
290 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", addr));
295 /* Check the CLDAP reply flags */
297 if ( !(cldap_reply.server_type & NBT_SERVER_LDAP) ) {
298 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
304 /* Fill in the ads->config values */
306 SAFE_FREE(ads->config.realm);
307 SAFE_FREE(ads->config.bind_path);
308 SAFE_FREE(ads->config.ldap_server_name);
309 SAFE_FREE(ads->config.server_site_name);
310 SAFE_FREE(ads->config.client_site_name);
311 SAFE_FREE(ads->server.workgroup);
313 ads->config.flags = cldap_reply.server_type;
314 ads->config.ldap_server_name = SMB_STRDUP(cldap_reply.pdc_dns_name);
315 ads->config.realm = SMB_STRDUP(cldap_reply.dns_domain);
316 if (!strupper_m(ads->config.realm)) {
321 ads->config.bind_path = ads_build_dn(ads->config.realm);
322 if (*cldap_reply.server_site) {
323 ads->config.server_site_name =
324 SMB_STRDUP(cldap_reply.server_site);
326 if (*cldap_reply.client_site) {
327 ads->config.client_site_name =
328 SMB_STRDUP(cldap_reply.client_site);
330 ads->server.workgroup = SMB_STRDUP(cldap_reply.domain_name);
332 ads->ldap.port = gc ? LDAP_GC_PORT : LDAP_PORT;
335 /* Store our site name. */
336 sitename_store( cldap_reply.domain_name, cldap_reply.client_site);
337 sitename_store( cldap_reply.dns_domain, cldap_reply.client_site);
347 /**********************************************************************
348 Try to find an AD dc using our internal name resolution routines
349 Try the realm first and then then workgroup name if netbios is not
351 **********************************************************************/
353 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
355 const char *c_domain;
358 struct ip_service *ip_list;
361 bool got_realm = False;
362 bool use_own_domain = False;
364 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
366 /* if the realm and workgroup are both empty, assume they are ours */
369 c_realm = ads->server.realm;
371 if ( !c_realm || !*c_realm ) {
372 /* special case where no realm and no workgroup means our own */
373 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
374 use_own_domain = True;
375 c_realm = lp_realm();
379 if (c_realm && *c_realm)
382 /* we need to try once with the realm name and fallback to the
383 netbios domain name if we fail (if netbios has not been disabled */
385 if ( !got_realm && !lp_disable_netbios() ) {
386 c_realm = ads->server.workgroup;
387 if (!c_realm || !*c_realm) {
388 if ( use_own_domain )
389 c_realm = lp_workgroup();
393 if ( !c_realm || !*c_realm ) {
394 DEBUG(1, ("ads_find_dc: no realm or workgroup! Don't know "
396 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
399 if ( use_own_domain ) {
400 c_domain = lp_workgroup();
402 c_domain = ads->server.workgroup;
409 * In case of LDAP we use get_dc_name() as that
410 * creates the custom krb5.conf file
412 if (!(ads->auth.flags & ADS_AUTH_NO_BIND)) {
414 struct sockaddr_storage ip_out;
416 DEBUG(6,("ads_find_dc: (ldap) looking for %s '%s'\n",
417 (got_realm ? "realm" : "domain"), realm));
419 if (get_dc_name(domain, realm, srv_name, &ip_out)) {
421 * we call ads_try_connect() to fill in the
422 * ads->config details
424 if (ads_try_connect(ads, srv_name, false)) {
429 return NT_STATUS_NO_LOGON_SERVERS;
432 sitename = sitename_fetch(talloc_tos(), realm);
436 DEBUG(6,("ads_find_dc: (cldap) looking for %s '%s'\n",
437 (got_realm ? "realm" : "domain"), realm));
439 status = get_sorted_dc_list(realm, sitename, &ip_list, &count, got_realm);
440 if (!NT_STATUS_IS_OK(status)) {
441 /* fall back to netbios if we can */
442 if ( got_realm && !lp_disable_netbios() ) {
447 TALLOC_FREE(sitename);
451 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
452 for ( i=0; i<count; i++ ) {
453 char server[INET6_ADDRSTRLEN];
455 print_sockaddr(server, sizeof(server), &ip_list[i].ss);
457 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
461 /* realm in this case is a workgroup name. We need
462 to ignore any IP addresses in the negative connection
463 cache that match ip addresses returned in the ad realm
464 case. It sucks that I have to reproduce the logic above... */
465 c_realm = ads->server.realm;
466 if ( !c_realm || !*c_realm ) {
467 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
468 c_realm = lp_realm();
471 if (c_realm && *c_realm &&
472 !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm, server))) {
473 /* Ensure we add the workgroup name for this
474 IP address as negative too. */
475 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
480 if ( ads_try_connect(ads, server, false) ) {
482 TALLOC_FREE(sitename);
486 /* keep track of failures */
487 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
492 /* In case we failed to contact one of our closest DC on our site we
493 * need to try to find another DC, retry with a site-less SRV DNS query
497 DEBUG(1,("ads_find_dc: failed to find a valid DC on our site (%s), "
498 "trying to find another DC\n", sitename));
499 TALLOC_FREE(sitename);
500 namecache_delete(realm, 0x1C);
504 return NT_STATUS_NO_LOGON_SERVERS;
507 /*********************************************************************
508 *********************************************************************/
510 static NTSTATUS ads_lookup_site(void)
512 ADS_STRUCT *ads = NULL;
513 ADS_STATUS ads_status;
514 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
516 ads = ads_init(lp_realm(), NULL, NULL);
518 return NT_STATUS_NO_MEMORY;
521 /* The NO_BIND here will find a DC and set the client site
522 but not establish the TCP connection */
524 ads->auth.flags = ADS_AUTH_NO_BIND;
525 ads_status = ads_connect(ads);
526 if (!ADS_ERR_OK(ads_status)) {
527 DEBUG(4, ("ads_lookup_site: ads_connect to our realm failed! (%s)\n",
528 ads_errstr(ads_status)));
530 nt_status = ads_ntstatus(ads_status);
539 /*********************************************************************
540 *********************************************************************/
542 static const char* host_dns_domain(const char *fqdn)
544 const char *p = fqdn;
546 /* go to next char following '.' */
548 if ((p = strchr_m(fqdn, '.')) != NULL) {
557 * Connect to the Global Catalog server
558 * @param ads Pointer to an existing ADS_STRUCT
559 * @return status of connection
561 * Simple wrapper around ads_connect() that fills in the
562 * GC ldap server information
565 ADS_STATUS ads_connect_gc(ADS_STRUCT *ads)
567 TALLOC_CTX *frame = talloc_stackframe();
568 struct dns_rr_srv *gcs_list;
570 const char *realm = ads->server.realm;
571 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
572 ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
575 char *sitename = NULL;
576 const char *dns_hosts_file;
581 if ((sitename = sitename_fetch(frame, realm)) == NULL) {
583 sitename = sitename_fetch(frame, realm);
586 dns_hosts_file = lp_parm_const_string(-1, "resolv", "host file", NULL);
588 /* We try once with a sitename and once without
589 (unless we don't have a sitename and then we're
592 if (sitename == NULL)
595 nt_status = ads_dns_query_gcs(frame, dns_hosts_file,
597 &gcs_list, &num_gcs);
599 if (!NT_STATUS_IS_OK(nt_status)) {
600 ads_status = ADS_ERROR_NT(nt_status);
604 /* Loop until we get a successful connection or have gone
605 through them all. When connecting a GC server, make sure that
606 the realm is the server's DNS name and not the forest root */
608 for (i=0; i<num_gcs; i++) {
609 ads->server.gc = true;
610 ads->server.ldap_server = SMB_STRDUP(gcs_list[i].hostname);
611 ads->server.realm = SMB_STRDUP(host_dns_domain(ads->server.ldap_server));
612 ads_status = ads_connect(ads);
613 if (ADS_ERR_OK(ads_status)) {
614 /* Reset the bind_dn to "". A Global Catalog server
615 may host multiple domain trees in a forest.
616 Windows 2003 GC server will accept "" as the search
617 path to imply search all domain trees in the forest */
619 SAFE_FREE(ads->config.bind_path);
620 ads->config.bind_path = SMB_STRDUP("");
625 SAFE_FREE(ads->server.ldap_server);
626 SAFE_FREE(ads->server.realm);
629 TALLOC_FREE(gcs_list);
634 talloc_destroy(frame);
641 * Connect to the LDAP server
642 * @param ads Pointer to an existing ADS_STRUCT
643 * @return status of connection
645 ADS_STATUS ads_connect(ADS_STRUCT *ads)
647 int version = LDAP_VERSION3;
650 char addr[INET6_ADDRSTRLEN];
652 ZERO_STRUCT(ads->ldap);
653 ads->ldap.last_attempt = time_mono(NULL);
654 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
656 /* try with a user specified server */
658 if (DEBUGLEVEL >= 11) {
659 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
660 DEBUG(11,("ads_connect: entering\n"));
661 DEBUGADD(11,("%s\n", s));
665 if (ads->server.ldap_server)
667 if (ads_try_connect(ads, ads->server.ldap_server, ads->server.gc)) {
671 /* The choice of which GC use is handled one level up in
672 ads_connect_gc(). If we continue on from here with
673 ads_find_dc() we will get GC searches on port 389 which
674 doesn't work. --jerry */
676 if (ads->server.gc == true) {
677 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
681 ntstatus = ads_find_dc(ads);
682 if (NT_STATUS_IS_OK(ntstatus)) {
686 status = ADS_ERROR_NT(ntstatus);
691 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
692 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
694 if (!ads->auth.user_name) {
695 /* Must use the userPrincipalName value here or sAMAccountName
696 and not servicePrincipalName; found by Guenther Deschner */
698 if (asprintf(&ads->auth.user_name, "%s$", lp_netbios_name() ) == -1) {
699 DEBUG(0,("ads_connect: asprintf fail.\n"));
700 ads->auth.user_name = NULL;
704 if (!ads->auth.realm) {
705 ads->auth.realm = SMB_STRDUP(ads->config.realm);
708 if (!ads->auth.kdc_server) {
709 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
710 ads->auth.kdc_server = SMB_STRDUP(addr);
713 /* If the caller() requested no LDAP bind, then we are done */
715 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
716 status = ADS_SUCCESS;
720 ads->ldap.mem_ctx = talloc_init("ads LDAP connection memory");
721 if (!ads->ldap.mem_ctx) {
722 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
726 /* Otherwise setup the TCP LDAP session */
728 ads->ldap.ld = ldap_open_with_timeout(addr,
730 ads->ldap.port, lp_ldap_timeout());
731 if (ads->ldap.ld == NULL) {
732 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
735 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
737 /* cache the successful connection for workgroup and realm */
738 if (ads_closest_dc(ads)) {
739 saf_store( ads->server.workgroup, ads->config.ldap_server_name);
740 saf_store( ads->server.realm, ads->config.ldap_server_name);
743 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
745 if ( lp_ldap_ssl_ads() ) {
746 status = ADS_ERROR(smbldap_start_tls(ads->ldap.ld, version));
747 if (!ADS_ERR_OK(status)) {
752 /* fill in the current time and offsets */
754 status = ads_current_time( ads );
755 if ( !ADS_ERR_OK(status) ) {
759 /* Now do the bind */
761 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
762 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
766 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
767 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
771 status = ads_sasl_bind(ads);
774 if (DEBUGLEVEL >= 11) {
775 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
776 DEBUG(11,("ads_connect: leaving with: %s\n",
777 ads_errstr(status)));
778 DEBUGADD(11,("%s\n", s));
786 * Connect to the LDAP server using given credentials
787 * @param ads Pointer to an existing ADS_STRUCT
788 * @return status of connection
790 ADS_STATUS ads_connect_user_creds(ADS_STRUCT *ads)
792 ads->auth.flags |= ADS_AUTH_USER_CREDS;
794 return ads_connect(ads);
798 * Disconnect the LDAP server
799 * @param ads Pointer to an existing ADS_STRUCT
801 void ads_disconnect(ADS_STRUCT *ads)
804 ldap_unbind(ads->ldap.ld);
807 if (ads->ldap.wrap_ops && ads->ldap.wrap_ops->disconnect) {
808 ads->ldap.wrap_ops->disconnect(ads);
810 if (ads->ldap.mem_ctx) {
811 talloc_free(ads->ldap.mem_ctx);
813 ZERO_STRUCT(ads->ldap);
817 Duplicate a struct berval into talloc'ed memory
819 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
821 struct berval *value;
823 if (!in_val) return NULL;
825 value = talloc_zero(ctx, struct berval);
828 if (in_val->bv_len == 0) return value;
830 value->bv_len = in_val->bv_len;
831 value->bv_val = (char *)talloc_memdup(ctx, in_val->bv_val,
837 Make a values list out of an array of (struct berval *)
839 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
840 const struct berval **in_vals)
842 struct berval **values;
845 if (!in_vals) return NULL;
846 for (i=0; in_vals[i]; i++)
848 values = talloc_zero_array(ctx, struct berval *, i+1);
849 if (!values) return NULL;
851 for (i=0; in_vals[i]; i++) {
852 values[i] = dup_berval(ctx, in_vals[i]);
858 UTF8-encode a values list out of an array of (char *)
860 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
866 if (!in_vals) return NULL;
867 for (i=0; in_vals[i]; i++)
869 values = talloc_zero_array(ctx, char *, i+1);
870 if (!values) return NULL;
872 for (i=0; in_vals[i]; i++) {
873 if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
882 Pull a (char *) array out of a UTF8-encoded values list
884 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
888 size_t converted_size;
890 if (!in_vals) return NULL;
891 for (i=0; in_vals[i]; i++)
893 values = talloc_zero_array(ctx, char *, i+1);
894 if (!values) return NULL;
896 for (i=0; in_vals[i]; i++) {
897 if (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
899 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
900 "%s", strerror(errno)));
907 * Do a search with paged results. cookie must be null on the first
908 * call, and then returned on each subsequent call. It will be null
909 * again when the entire search is complete
910 * @param ads connection to ads server
911 * @param bind_path Base dn for the search
912 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
913 * @param expr Search expression - specified in local charset
914 * @param attrs Attributes to retrieve - specified in utf8 or ascii
915 * @param res ** which will contain results - free res* with ads_msgfree()
916 * @param count Number of entries retrieved on this page
917 * @param cookie The paged results cookie to be returned on subsequent calls
918 * @return status of search
920 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
921 const char *bind_path,
922 int scope, const char *expr,
923 const char **attrs, void *args,
925 int *count, struct berval **cookie)
928 char *utf8_expr, *utf8_path, **search_attrs = NULL;
929 size_t converted_size;
930 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
931 BerElement *cookie_be = NULL;
932 struct berval *cookie_bv= NULL;
933 BerElement *ext_be = NULL;
934 struct berval *ext_bv= NULL;
937 ads_control *external_control = (ads_control *) args;
941 if (!(ctx = talloc_init("ads_do_paged_search_args")))
942 return ADS_ERROR(LDAP_NO_MEMORY);
944 /* 0 means the conversion worked but the result was empty
945 so we only fail if it's -1. In any case, it always
946 at least nulls out the dest */
947 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
948 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
954 if (!attrs || !(*attrs))
957 /* This would be the utf8-encoded version...*/
958 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
959 if (!(search_attrs = str_list_copy(talloc_tos(), attrs))) {
965 /* Paged results only available on ldap v3 or later */
966 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
967 if (version < LDAP_VERSION3) {
968 rc = LDAP_NOT_SUPPORTED;
972 cookie_be = ber_alloc_t(LBER_USE_DER);
974 ber_printf(cookie_be, "{iO}", (ber_int_t) ads->config.ldap_page_size, *cookie);
975 ber_bvfree(*cookie); /* don't need it from last time */
978 ber_printf(cookie_be, "{io}", (ber_int_t) ads->config.ldap_page_size, "", 0);
980 ber_flatten(cookie_be, &cookie_bv);
981 PagedResults.ldctl_oid = discard_const_p(char, ADS_PAGE_CTL_OID);
982 PagedResults.ldctl_iscritical = (char) 1;
983 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
984 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
986 NoReferrals.ldctl_oid = discard_const_p(char, ADS_NO_REFERRALS_OID);
987 NoReferrals.ldctl_iscritical = (char) 0;
988 NoReferrals.ldctl_value.bv_len = 0;
989 NoReferrals.ldctl_value.bv_val = discard_const_p(char, "");
991 if (external_control &&
992 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
993 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
995 ExternalCtrl.ldctl_oid = discard_const_p(char, external_control->control);
996 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
998 /* win2k does not accept a ldctl_value beeing passed in */
1000 if (external_control->val != 0) {
1002 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
1003 rc = LDAP_NO_MEMORY;
1007 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
1008 rc = LDAP_NO_MEMORY;
1011 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
1012 rc = LDAP_NO_MEMORY;
1016 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
1017 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
1020 ExternalCtrl.ldctl_value.bv_len = 0;
1021 ExternalCtrl.ldctl_value.bv_val = NULL;
1024 controls[0] = &NoReferrals;
1025 controls[1] = &PagedResults;
1026 controls[2] = &ExternalCtrl;
1030 controls[0] = &NoReferrals;
1031 controls[1] = &PagedResults;
1035 /* we need to disable referrals as the openldap libs don't
1036 handle them and paged results at the same time. Using them
1037 together results in the result record containing the server
1038 page control being removed from the result list (tridge/jmcd)
1040 leaving this in despite the control that says don't generate
1041 referrals, in case the server doesn't support it (jmcd)
1043 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1045 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1046 search_attrs, 0, controls,
1047 NULL, LDAP_NO_LIMIT,
1048 (LDAPMessage **)res);
1050 ber_free(cookie_be, 1);
1051 ber_bvfree(cookie_bv);
1054 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
1055 ldap_err2string(rc)));
1056 if (rc == LDAP_OTHER) {
1060 ret = ldap_parse_result(ads->ldap.ld,
1068 if (ret == LDAP_SUCCESS) {
1069 DEBUG(3, ("ldap_search_with_timeout(%s) "
1070 "error: %s\n", expr, ldap_errmsg));
1071 ldap_memfree(ldap_errmsg);
1077 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
1078 NULL, &rcontrols, 0);
1084 for (i=0; rcontrols[i]; i++) {
1085 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
1086 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
1087 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
1089 /* the berval is the cookie, but must be freed when
1091 if (cookie_bv->bv_len) /* still more to do */
1092 *cookie=ber_bvdup(cookie_bv);
1095 ber_bvfree(cookie_bv);
1096 ber_free(cookie_be, 1);
1100 ldap_controls_free(rcontrols);
1103 talloc_destroy(ctx);
1106 ber_free(ext_be, 1);
1113 /* if/when we decide to utf8-encode attrs, take out this next line */
1114 TALLOC_FREE(search_attrs);
1116 return ADS_ERROR(rc);
1119 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
1120 int scope, const char *expr,
1121 const char **attrs, LDAPMessage **res,
1122 int *count, struct berval **cookie)
1124 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
1129 * Get all results for a search. This uses ads_do_paged_search() to return
1130 * all entries in a large search.
1131 * @param ads connection to ads server
1132 * @param bind_path Base dn for the search
1133 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1134 * @param expr Search expression
1135 * @param attrs Attributes to retrieve
1136 * @param res ** which will contain results - free res* with ads_msgfree()
1137 * @return status of search
1139 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
1140 int scope, const char *expr,
1141 const char **attrs, void *args,
1144 struct berval *cookie = NULL;
1149 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1152 if (!ADS_ERR_OK(status))
1155 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1157 LDAPMessage *res2 = NULL;
1158 LDAPMessage *msg, *next;
1160 status = ads_do_paged_search_args(ads, bind_path, scope, expr,
1161 attrs, args, &res2, &count, &cookie);
1162 if (!ADS_ERR_OK(status)) {
1163 /* Ensure we free all collected results */
1164 ads_msgfree(ads, *res);
1169 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1170 that this works on all ldap libs, but I have only tested with openldap */
1171 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1172 next = ads_next_message(ads, msg);
1173 ldap_add_result_entry((LDAPMessage **)res, msg);
1175 /* note that we do not free res2, as the memory is now
1176 part of the main returned list */
1179 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1180 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1186 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1187 int scope, const char *expr,
1188 const char **attrs, LDAPMessage **res)
1190 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1193 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1194 int scope, const char *expr,
1195 const char **attrs, uint32 sd_flags,
1200 args.control = ADS_SD_FLAGS_OID;
1201 args.val = sd_flags;
1202 args.critical = True;
1204 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1209 * Run a function on all results for a search. Uses ads_do_paged_search() and
1210 * runs the function as each page is returned, using ads_process_results()
1211 * @param ads connection to ads server
1212 * @param bind_path Base dn for the search
1213 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1214 * @param expr Search expression - specified in local charset
1215 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1216 * @param fn Function which takes attr name, values list, and data_area
1217 * @param data_area Pointer which is passed to function on each call
1218 * @return status of search
1220 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1221 int scope, const char *expr, const char **attrs,
1222 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1225 struct berval *cookie = NULL;
1230 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1233 if (!ADS_ERR_OK(status)) return status;
1235 ads_process_results(ads, res, fn, data_area);
1236 ads_msgfree(ads, res);
1239 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1240 &res, &count, &cookie);
1242 if (!ADS_ERR_OK(status)) break;
1244 ads_process_results(ads, res, fn, data_area);
1245 ads_msgfree(ads, res);
1252 * Do a search with a timeout.
1253 * @param ads connection to ads server
1254 * @param bind_path Base dn for the search
1255 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1256 * @param expr Search expression
1257 * @param attrs Attributes to retrieve
1258 * @param res ** which will contain results - free res* with ads_msgfree()
1259 * @return status of search
1261 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1263 const char **attrs, LDAPMessage **res)
1266 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1267 size_t converted_size;
1271 if (!(ctx = talloc_init("ads_do_search"))) {
1272 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1273 return ADS_ERROR(LDAP_NO_MEMORY);
1276 /* 0 means the conversion worked but the result was empty
1277 so we only fail if it's negative. In any case, it always
1278 at least nulls out the dest */
1279 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1280 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1282 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1283 rc = LDAP_NO_MEMORY;
1287 if (!attrs || !(*attrs))
1288 search_attrs = NULL;
1290 /* This would be the utf8-encoded version...*/
1291 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1292 if (!(search_attrs = str_list_copy(talloc_tos(), attrs)))
1294 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1295 rc = LDAP_NO_MEMORY;
1300 /* see the note in ads_do_paged_search - we *must* disable referrals */
1301 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1303 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1304 search_attrs, 0, NULL, NULL,
1306 (LDAPMessage **)res);
1308 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1309 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1314 talloc_destroy(ctx);
1315 /* if/when we decide to utf8-encode attrs, take out this next line */
1316 TALLOC_FREE(search_attrs);
1317 return ADS_ERROR(rc);
1320 * Do a general ADS search
1321 * @param ads connection to ads server
1322 * @param res ** which will contain results - free res* with ads_msgfree()
1323 * @param expr Search expression
1324 * @param attrs Attributes to retrieve
1325 * @return status of search
1327 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1328 const char *expr, const char **attrs)
1330 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1335 * Do a search on a specific DistinguishedName
1336 * @param ads connection to ads server
1337 * @param res ** which will contain results - free res* with ads_msgfree()
1338 * @param dn DistinguishName to search
1339 * @param attrs Attributes to retrieve
1340 * @return status of search
1342 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1343 const char *dn, const char **attrs)
1345 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1350 * Free up memory from a ads_search
1351 * @param ads connection to ads server
1352 * @param msg Search results to free
1354 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1361 * Get a dn from search results
1362 * @param ads connection to ads server
1363 * @param msg Search result
1366 char *ads_get_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg)
1368 char *utf8_dn, *unix_dn;
1369 size_t converted_size;
1371 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1374 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1378 if (!pull_utf8_talloc(mem_ctx, &unix_dn, utf8_dn, &converted_size)) {
1379 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1383 ldap_memfree(utf8_dn);
1388 * Get the parent from a dn
1389 * @param dn the dn to return the parent from
1390 * @return parent dn string
1392 char *ads_parent_dn(const char *dn)
1400 p = strchr(dn, ',');
1410 * Find a machine account given a hostname
1411 * @param ads connection to ads server
1412 * @param res ** which will contain results - free res* with ads_msgfree()
1413 * @param host Hostname to search for
1414 * @return status of search
1416 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1417 const char *machine)
1421 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1425 /* the easiest way to find a machine account anywhere in the tree
1426 is to look for hostname$ */
1427 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1428 DEBUG(1, ("asprintf failed!\n"));
1429 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1432 status = ads_search(ads, res, expr, attrs);
1438 * Initialize a list of mods to be used in a modify request
1439 * @param ctx An initialized TALLOC_CTX
1440 * @return allocated ADS_MODLIST
1442 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1444 #define ADS_MODLIST_ALLOC_SIZE 10
1447 if ((mods = talloc_zero_array(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1448 /* -1 is safety to make sure we don't go over the end.
1449 need to reset it to NULL before doing ldap modify */
1450 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1452 return (ADS_MODLIST)mods;
1457 add an attribute to the list, with values list already constructed
1459 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1460 int mod_op, const char *name,
1461 const void *_invals)
1463 const void **invals = (const void **)_invals;
1465 LDAPMod **modlist = (LDAPMod **) *mods;
1466 struct berval **ber_values = NULL;
1467 char **char_values = NULL;
1470 mod_op = LDAP_MOD_DELETE;
1472 if (mod_op & LDAP_MOD_BVALUES)
1473 ber_values = ads_dup_values(ctx,
1474 (const struct berval **)invals);
1476 char_values = ads_push_strvals(ctx,
1477 (const char **) invals);
1480 /* find the first empty slot */
1481 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1483 if (modlist[curmod] == (LDAPMod *) -1) {
1484 if (!(modlist = talloc_realloc(ctx, modlist, LDAPMod *,
1485 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1486 return ADS_ERROR(LDAP_NO_MEMORY);
1487 memset(&modlist[curmod], 0,
1488 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1489 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1490 *mods = (ADS_MODLIST)modlist;
1493 if (!(modlist[curmod] = talloc_zero(ctx, LDAPMod)))
1494 return ADS_ERROR(LDAP_NO_MEMORY);
1495 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1496 if (mod_op & LDAP_MOD_BVALUES) {
1497 modlist[curmod]->mod_bvalues = ber_values;
1498 } else if (mod_op & LDAP_MOD_DELETE) {
1499 modlist[curmod]->mod_values = NULL;
1501 modlist[curmod]->mod_values = char_values;
1504 modlist[curmod]->mod_op = mod_op;
1505 return ADS_ERROR(LDAP_SUCCESS);
1509 * Add a single string value to a mod list
1510 * @param ctx An initialized TALLOC_CTX
1511 * @param mods An initialized ADS_MODLIST
1512 * @param name The attribute name to add
1513 * @param val The value to add - NULL means DELETE
1514 * @return ADS STATUS indicating success of add
1516 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1517 const char *name, const char *val)
1519 const char *values[2];
1525 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1526 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1530 * Add an array of string values to a mod list
1531 * @param ctx An initialized TALLOC_CTX
1532 * @param mods An initialized ADS_MODLIST
1533 * @param name The attribute name to add
1534 * @param vals The array of string values to add - NULL means DELETE
1535 * @return ADS STATUS indicating success of add
1537 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1538 const char *name, const char **vals)
1541 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1542 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1543 name, (const void **) vals);
1548 * Add a single ber-encoded value to a mod list
1549 * @param ctx An initialized TALLOC_CTX
1550 * @param mods An initialized ADS_MODLIST
1551 * @param name The attribute name to add
1552 * @param val The value to add - NULL means DELETE
1553 * @return ADS STATUS indicating success of add
1555 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1556 const char *name, const struct berval *val)
1558 const struct berval *values[2];
1563 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1564 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1565 name, (const void **) values);
1570 * Perform an ldap modify
1571 * @param ads connection to ads server
1572 * @param mod_dn DistinguishedName to modify
1573 * @param mods list of modifications to perform
1574 * @return status of modify
1576 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1579 char *utf8_dn = NULL;
1580 size_t converted_size;
1582 this control is needed to modify that contains a currently
1583 non-existent attribute (but allowable for the object) to run
1585 LDAPControl PermitModify = {
1586 discard_const_p(char, ADS_PERMIT_MODIFY_OID),
1589 LDAPControl *controls[2];
1591 controls[0] = &PermitModify;
1594 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, mod_dn, &converted_size)) {
1595 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1598 /* find the end of the list, marked by NULL or -1 */
1599 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1600 /* make sure the end of the list is NULL */
1602 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1603 (LDAPMod **) mods, controls, NULL);
1604 TALLOC_FREE(utf8_dn);
1605 return ADS_ERROR(ret);
1609 * Perform an ldap add
1610 * @param ads connection to ads server
1611 * @param new_dn DistinguishedName to add
1612 * @param mods list of attributes and values for DN
1613 * @return status of add
1615 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1618 char *utf8_dn = NULL;
1619 size_t converted_size;
1621 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, new_dn, &converted_size)) {
1622 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!"));
1623 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1626 /* find the end of the list, marked by NULL or -1 */
1627 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1628 /* make sure the end of the list is NULL */
1631 ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1632 TALLOC_FREE(utf8_dn);
1633 return ADS_ERROR(ret);
1637 * Delete a DistinguishedName
1638 * @param ads connection to ads server
1639 * @param new_dn DistinguishedName to delete
1640 * @return status of delete
1642 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1645 char *utf8_dn = NULL;
1646 size_t converted_size;
1647 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, del_dn, &converted_size)) {
1648 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!"));
1649 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1652 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1653 TALLOC_FREE(utf8_dn);
1654 return ADS_ERROR(ret);
1658 * Build an org unit string
1659 * if org unit is Computers or blank then assume a container, otherwise
1660 * assume a / separated list of organisational units.
1661 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1662 * @param ads connection to ads server
1663 * @param org_unit Organizational unit
1664 * @return org unit string - caller must free
1666 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1670 if (!org_unit || !*org_unit) {
1672 ret = ads_default_ou_string(ads, DS_GUID_COMPUTERS_CONTAINER);
1674 /* samba4 might not yet respond to a wellknownobject-query */
1675 return ret ? ret : SMB_STRDUP("cn=Computers");
1678 if (strequal(org_unit, "Computers")) {
1679 return SMB_STRDUP("cn=Computers");
1682 /* jmcd: removed "\\" from the separation chars, because it is
1683 needed as an escape for chars like '#' which are valid in an
1685 return ads_build_path(org_unit, "/", "ou=", 1);
1689 * Get a org unit string for a well-known GUID
1690 * @param ads connection to ads server
1691 * @param wknguid Well known GUID
1692 * @return org unit string - caller must free
1694 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1697 LDAPMessage *res = NULL;
1698 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1699 **bind_dn_exp = NULL;
1700 const char *attrs[] = {"distinguishedName", NULL};
1701 int new_ln, wkn_ln, bind_ln, i;
1703 if (wknguid == NULL) {
1707 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1708 DEBUG(1, ("asprintf failed!\n"));
1712 status = ads_search_dn(ads, &res, base, attrs);
1713 if (!ADS_ERR_OK(status)) {
1714 DEBUG(1,("Failed while searching for: %s\n", base));
1718 if (ads_count_replies(ads, res) != 1) {
1722 /* substitute the bind-path from the well-known-guid-search result */
1723 wkn_dn = ads_get_dn(ads, talloc_tos(), res);
1728 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1733 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1738 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1740 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1743 new_ln = wkn_ln - bind_ln;
1745 ret = SMB_STRDUP(wkn_dn_exp[0]);
1750 for (i=1; i < new_ln; i++) {
1753 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1759 ret = SMB_STRDUP(s);
1768 ads_msgfree(ads, res);
1769 TALLOC_FREE(wkn_dn);
1771 ldap_value_free(wkn_dn_exp);
1774 ldap_value_free(bind_dn_exp);
1781 * Adds (appends) an item to an attribute array, rather then
1782 * replacing the whole list
1783 * @param ctx An initialized TALLOC_CTX
1784 * @param mods An initialized ADS_MODLIST
1785 * @param name name of the ldap attribute to append to
1786 * @param vals an array of values to add
1787 * @return status of addition
1790 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1791 const char *name, const char **vals)
1793 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1794 (const void *) vals);
1798 * Determines the an account's current KVNO via an LDAP lookup
1799 * @param ads An initialized ADS_STRUCT
1800 * @param account_name the NT samaccountname.
1801 * @return the kvno for the account, or -1 in case of a failure.
1804 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
1806 LDAPMessage *res = NULL;
1807 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1809 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1810 char *dn_string = NULL;
1811 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1813 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
1814 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
1817 ret = ads_search(ads, &res, filter, attrs);
1819 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
1820 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
1821 ads_msgfree(ads, res);
1825 dn_string = ads_get_dn(ads, talloc_tos(), res);
1827 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1828 ads_msgfree(ads, res);
1831 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1832 TALLOC_FREE(dn_string);
1834 /* ---------------------------------------------------------
1835 * 0 is returned as a default KVNO from this point on...
1836 * This is done because Windows 2000 does not support key
1837 * version numbers. Chances are that a failure in the next
1838 * step is simply due to Windows 2000 being used for a
1839 * domain controller. */
1842 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1843 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1844 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1845 ads_msgfree(ads, res);
1850 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1851 ads_msgfree(ads, res);
1856 * Determines the computer account's current KVNO via an LDAP lookup
1857 * @param ads An initialized ADS_STRUCT
1858 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1859 * @return the kvno for the computer account, or -1 in case of a failure.
1862 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
1864 char *computer_account = NULL;
1867 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
1871 kvno = ads_get_kvno(ads, computer_account);
1872 free(computer_account);
1878 * This clears out all registered spn's for a given hostname
1879 * @param ads An initilaized ADS_STRUCT
1880 * @param machine_name the NetBIOS name of the computer.
1881 * @return 0 upon success, non-zero otherwise.
1884 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1887 LDAPMessage *res = NULL;
1889 const char *servicePrincipalName[1] = {NULL};
1890 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1891 char *dn_string = NULL;
1893 ret = ads_find_machine_acct(ads, &res, machine_name);
1894 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1895 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1896 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1897 ads_msgfree(ads, res);
1898 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1901 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1902 ctx = talloc_init("ads_clear_service_principal_names");
1904 ads_msgfree(ads, res);
1905 return ADS_ERROR(LDAP_NO_MEMORY);
1908 if (!(mods = ads_init_mods(ctx))) {
1909 talloc_destroy(ctx);
1910 ads_msgfree(ads, res);
1911 return ADS_ERROR(LDAP_NO_MEMORY);
1913 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1914 if (!ADS_ERR_OK(ret)) {
1915 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1916 ads_msgfree(ads, res);
1917 talloc_destroy(ctx);
1920 dn_string = ads_get_dn(ads, talloc_tos(), res);
1922 talloc_destroy(ctx);
1923 ads_msgfree(ads, res);
1924 return ADS_ERROR(LDAP_NO_MEMORY);
1926 ret = ads_gen_mod(ads, dn_string, mods);
1927 TALLOC_FREE(dn_string);
1928 if (!ADS_ERR_OK(ret)) {
1929 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1931 ads_msgfree(ads, res);
1932 talloc_destroy(ctx);
1936 ads_msgfree(ads, res);
1937 talloc_destroy(ctx);
1942 * This adds a service principal name to an existing computer account
1943 * (found by hostname) in AD.
1944 * @param ads An initialized ADS_STRUCT
1945 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1946 * @param my_fqdn The fully qualified DNS name of the machine
1947 * @param spn A string of the service principal to add, i.e. 'host'
1948 * @return 0 upon sucess, or non-zero if a failure occurs
1951 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1952 const char *my_fqdn, const char *spn)
1956 LDAPMessage *res = NULL;
1959 char *dn_string = NULL;
1960 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1962 ret = ads_find_machine_acct(ads, &res, machine_name);
1963 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1964 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1966 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1967 spn, machine_name, ads->config.realm));
1968 ads_msgfree(ads, res);
1969 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1972 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1973 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1974 ads_msgfree(ads, res);
1975 return ADS_ERROR(LDAP_NO_MEMORY);
1978 /* add short name spn */
1980 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1981 talloc_destroy(ctx);
1982 ads_msgfree(ads, res);
1983 return ADS_ERROR(LDAP_NO_MEMORY);
1985 if (!strlower_m(&psp1[strlen(spn) + 1])) {
1986 ret = ADS_ERROR(LDAP_NO_MEMORY);
1989 servicePrincipalName[0] = psp1;
1991 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1992 psp1, machine_name));
1995 /* add fully qualified spn */
1997 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1998 ret = ADS_ERROR(LDAP_NO_MEMORY);
2001 if (!strlower_m(&psp2[strlen(spn) + 1])) {
2002 ret = ADS_ERROR(LDAP_NO_MEMORY);
2005 servicePrincipalName[1] = psp2;
2007 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
2008 psp2, machine_name));
2010 if ( (mods = ads_init_mods(ctx)) == NULL ) {
2011 ret = ADS_ERROR(LDAP_NO_MEMORY);
2015 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
2016 if (!ADS_ERR_OK(ret)) {
2017 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2021 if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
2022 ret = ADS_ERROR(LDAP_NO_MEMORY);
2026 ret = ads_gen_mod(ads, dn_string, mods);
2027 if (!ADS_ERR_OK(ret)) {
2028 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2034 ads_msgfree(ads, res);
2039 * adds a machine account to the ADS server
2040 * @param ads An intialized ADS_STRUCT
2041 * @param machine_name - the NetBIOS machine name of this account.
2042 * @param account_type A number indicating the type of account to create
2043 * @param org_unit The LDAP path in which to place this account
2044 * @return 0 upon success, or non-zero otherwise
2047 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2048 const char *org_unit)
2051 char *samAccountName, *controlstr;
2054 char *machine_escaped = NULL;
2056 const char *objectClass[] = {"top", "person", "organizationalPerson",
2057 "user", "computer", NULL};
2058 LDAPMessage *res = NULL;
2059 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
2060 UF_DONT_EXPIRE_PASSWD |\
2061 UF_ACCOUNTDISABLE );
2063 if (!(ctx = talloc_init("ads_add_machine_acct")))
2064 return ADS_ERROR(LDAP_NO_MEMORY);
2066 ret = ADS_ERROR(LDAP_NO_MEMORY);
2068 machine_escaped = escape_rdn_val_string_alloc(machine_name);
2069 if (!machine_escaped) {
2073 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
2074 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
2076 if ( !new_dn || !samAccountName ) {
2080 #ifndef ENCTYPE_ARCFOUR_HMAC
2081 acct_control |= UF_USE_DES_KEY_ONLY;
2084 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
2088 if (!(mods = ads_init_mods(ctx))) {
2092 ads_mod_str(ctx, &mods, "cn", machine_name);
2093 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
2094 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
2095 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2097 ret = ads_gen_add(ads, new_dn, mods);
2100 SAFE_FREE(machine_escaped);
2101 ads_msgfree(ads, res);
2102 talloc_destroy(ctx);
2108 * move a machine account to another OU on the ADS server
2109 * @param ads - An intialized ADS_STRUCT
2110 * @param machine_name - the NetBIOS machine name of this account.
2111 * @param org_unit - The LDAP path in which to place this account
2112 * @param moved - whether we moved the machine account (optional)
2113 * @return 0 upon success, or non-zero otherwise
2116 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2117 const char *org_unit, bool *moved)
2121 LDAPMessage *res = NULL;
2122 char *filter = NULL;
2123 char *computer_dn = NULL;
2125 char *computer_rdn = NULL;
2126 bool need_move = False;
2128 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2129 rc = ADS_ERROR(LDAP_NO_MEMORY);
2133 /* Find pre-existing machine */
2134 rc = ads_search(ads, &res, filter, NULL);
2135 if (!ADS_ERR_OK(rc)) {
2139 computer_dn = ads_get_dn(ads, talloc_tos(), res);
2141 rc = ADS_ERROR(LDAP_NO_MEMORY);
2145 parent_dn = ads_parent_dn(computer_dn);
2146 if (strequal(parent_dn, org_unit)) {
2152 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2153 rc = ADS_ERROR(LDAP_NO_MEMORY);
2157 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2158 org_unit, 1, NULL, NULL);
2159 rc = ADS_ERROR(ldap_status);
2162 ads_msgfree(ads, res);
2164 TALLOC_FREE(computer_dn);
2165 SAFE_FREE(computer_rdn);
2167 if (!ADS_ERR_OK(rc)) {
2179 dump a binary result from ldap
2181 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2184 for (i=0; values[i]; i++) {
2185 printf("%s: ", field);
2186 for (j=0; j<values[i]->bv_len; j++) {
2187 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2193 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2196 for (i=0; values[i]; i++) {
2198 DATA_BLOB in = data_blob_const(values[i]->bv_val, values[i]->bv_len);
2201 status = GUID_from_ndr_blob(&in, &guid);
2202 if (NT_STATUS_IS_OK(status)) {
2203 printf("%s: %s\n", field, GUID_string(talloc_tos(), &guid));
2205 printf("%s: INVALID GUID\n", field);
2211 dump a sid result from ldap
2213 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2216 for (i=0; values[i]; i++) {
2219 if (!sid_parse(values[i]->bv_val, values[i]->bv_len, &sid)) {
2222 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
2227 dump ntSecurityDescriptor
2229 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2231 TALLOC_CTX *frame = talloc_stackframe();
2232 struct security_descriptor *psd;
2235 status = unmarshall_sec_desc(talloc_tos(), (uint8 *)values[0]->bv_val,
2236 values[0]->bv_len, &psd);
2237 if (!NT_STATUS_IS_OK(status)) {
2238 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2239 nt_errstr(status)));
2245 ads_disp_sd(ads, talloc_tos(), psd);
2252 dump a string result from ldap
2254 static void dump_string(const char *field, char **values)
2257 for (i=0; values[i]; i++) {
2258 printf("%s: %s\n", field, values[i]);
2263 dump a field from LDAP on stdout
2267 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2272 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2274 {"objectGUID", False, dump_guid},
2275 {"netbootGUID", False, dump_guid},
2276 {"nTSecurityDescriptor", False, dump_sd},
2277 {"dnsRecord", False, dump_binary},
2278 {"objectSid", False, dump_sid},
2279 {"tokenGroups", False, dump_sid},
2280 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2281 {"tokengroupsGlobalandUniversal", False, dump_sid},
2282 {"mS-DS-CreatorSID", False, dump_sid},
2283 {"msExchMailboxGuid", False, dump_guid},
2288 if (!field) { /* must be end of an entry */
2293 for (i=0; handlers[i].name; i++) {
2294 if (strcasecmp_m(handlers[i].name, field) == 0) {
2295 if (!values) /* first time, indicate string or not */
2296 return handlers[i].string;
2297 handlers[i].handler(ads, field, (struct berval **) values);
2301 if (!handlers[i].name) {
2302 if (!values) /* first time, indicate string conversion */
2304 dump_string(field, (char **)values);
2310 * Dump a result from LDAP on stdout
2311 * used for debugging
2312 * @param ads connection to ads server
2313 * @param res Results to dump
2316 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2318 ads_process_results(ads, res, ads_dump_field, NULL);
2322 * Walk through results, calling a function for each entry found.
2323 * The function receives a field name, a berval * array of values,
2324 * and a data area passed through from the start. The function is
2325 * called once with null for field and values at the end of each
2327 * @param ads connection to ads server
2328 * @param res Results to process
2329 * @param fn Function for processing each result
2330 * @param data_area user-defined area to pass to function
2332 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2333 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2338 size_t converted_size;
2340 if (!(ctx = talloc_init("ads_process_results")))
2343 for (msg = ads_first_entry(ads, res); msg;
2344 msg = ads_next_entry(ads, msg)) {
2348 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2349 (LDAPMessage *)msg,&b);
2351 utf8_field=ldap_next_attribute(ads->ldap.ld,
2352 (LDAPMessage *)msg,b)) {
2353 struct berval **ber_vals;
2354 char **str_vals, **utf8_vals;
2358 if (!pull_utf8_talloc(ctx, &field, utf8_field,
2361 DEBUG(0,("ads_process_results: "
2362 "pull_utf8_talloc failed: %s",
2366 string = fn(ads, field, NULL, data_area);
2369 utf8_vals = ldap_get_values(ads->ldap.ld,
2370 (LDAPMessage *)msg, field);
2371 str_vals = ads_pull_strvals(ctx,
2372 (const char **) utf8_vals);
2373 fn(ads, field, (void **) str_vals, data_area);
2374 ldap_value_free(utf8_vals);
2376 ber_vals = ldap_get_values_len(ads->ldap.ld,
2377 (LDAPMessage *)msg, field);
2378 fn(ads, field, (void **) ber_vals, data_area);
2380 ldap_value_free_len(ber_vals);
2382 ldap_memfree(utf8_field);
2385 talloc_free_children(ctx);
2386 fn(ads, NULL, NULL, data_area); /* completed an entry */
2389 talloc_destroy(ctx);
2393 * count how many replies are in a LDAPMessage
2394 * @param ads connection to ads server
2395 * @param res Results to count
2396 * @return number of replies
2398 int ads_count_replies(ADS_STRUCT *ads, void *res)
2400 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2404 * pull the first entry from a ADS result
2405 * @param ads connection to ads server
2406 * @param res Results of search
2407 * @return first entry from result
2409 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2411 return ldap_first_entry(ads->ldap.ld, res);
2415 * pull the next entry from a ADS result
2416 * @param ads connection to ads server
2417 * @param res Results of search
2418 * @return next entry from result
2420 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2422 return ldap_next_entry(ads->ldap.ld, res);
2426 * pull the first message from a ADS result
2427 * @param ads connection to ads server
2428 * @param res Results of search
2429 * @return first message from result
2431 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2433 return ldap_first_message(ads->ldap.ld, res);
2437 * pull the next message from a ADS result
2438 * @param ads connection to ads server
2439 * @param res Results of search
2440 * @return next message from result
2442 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2444 return ldap_next_message(ads->ldap.ld, res);
2448 * pull a single string from a ADS result
2449 * @param ads connection to ads server
2450 * @param mem_ctx TALLOC_CTX to use for allocating result string
2451 * @param msg Results of search
2452 * @param field Attribute to retrieve
2453 * @return Result string in talloc context
2455 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2461 size_t converted_size;
2463 values = ldap_get_values(ads->ldap.ld, msg, field);
2467 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2472 ldap_value_free(values);
2477 * pull an array of strings from a ADS result
2478 * @param ads connection to ads server
2479 * @param mem_ctx TALLOC_CTX to use for allocating result string
2480 * @param msg Results of search
2481 * @param field Attribute to retrieve
2482 * @return Result strings in talloc context
2484 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2485 LDAPMessage *msg, const char *field,
2491 size_t converted_size;
2493 values = ldap_get_values(ads->ldap.ld, msg, field);
2497 *num_values = ldap_count_values(values);
2499 ret = talloc_array(mem_ctx, char *, *num_values + 1);
2501 ldap_value_free(values);
2505 for (i=0;i<*num_values;i++) {
2506 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2509 ldap_value_free(values);
2515 ldap_value_free(values);
2520 * pull an array of strings from a ADS result
2521 * (handle large multivalue attributes with range retrieval)
2522 * @param ads connection to ads server
2523 * @param mem_ctx TALLOC_CTX to use for allocating result string
2524 * @param msg Results of search
2525 * @param field Attribute to retrieve
2526 * @param current_strings strings returned by a previous call to this function
2527 * @param next_attribute The next query should ask for this attribute
2528 * @param num_values How many values did we get this time?
2529 * @param more_values Are there more values to get?
2530 * @return Result strings in talloc context
2532 char **ads_pull_strings_range(ADS_STRUCT *ads,
2533 TALLOC_CTX *mem_ctx,
2534 LDAPMessage *msg, const char *field,
2535 char **current_strings,
2536 const char **next_attribute,
2537 size_t *num_strings,
2541 char *expected_range_attrib, *range_attr;
2542 BerElement *ptr = NULL;
2545 size_t num_new_strings;
2546 unsigned long int range_start;
2547 unsigned long int range_end;
2549 /* we might have been given the whole lot anyway */
2550 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2551 *more_strings = False;
2555 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2557 /* look for Range result */
2558 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2560 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2561 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2562 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2570 /* nothing here - this field is just empty */
2571 *more_strings = False;
2575 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2576 &range_start, &range_end) == 2) {
2577 *more_strings = True;
2579 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2580 &range_start) == 1) {
2581 *more_strings = False;
2583 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2585 ldap_memfree(range_attr);
2586 *more_strings = False;
2591 if ((*num_strings) != range_start) {
2592 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2593 " - aborting range retreival\n",
2594 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2595 ldap_memfree(range_attr);
2596 *more_strings = False;
2600 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2602 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2603 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2604 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2605 range_attr, (unsigned long int)range_end - range_start + 1,
2606 (unsigned long int)num_new_strings));
2607 ldap_memfree(range_attr);
2608 *more_strings = False;
2612 strings = talloc_realloc(mem_ctx, current_strings, char *,
2613 *num_strings + num_new_strings);
2615 if (strings == NULL) {
2616 ldap_memfree(range_attr);
2617 *more_strings = False;
2621 if (new_strings && num_new_strings) {
2622 memcpy(&strings[*num_strings], new_strings,
2623 sizeof(*new_strings) * num_new_strings);
2626 (*num_strings) += num_new_strings;
2628 if (*more_strings) {
2629 *next_attribute = talloc_asprintf(mem_ctx,
2634 if (!*next_attribute) {
2635 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2636 ldap_memfree(range_attr);
2637 *more_strings = False;
2642 ldap_memfree(range_attr);
2648 * pull a single uint32 from a ADS result
2649 * @param ads connection to ads server
2650 * @param msg Results of search
2651 * @param field Attribute to retrieve
2652 * @param v Pointer to int to store result
2653 * @return boolean inidicating success
2655 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2660 values = ldap_get_values(ads->ldap.ld, msg, field);
2664 ldap_value_free(values);
2668 *v = atoi(values[0]);
2669 ldap_value_free(values);
2674 * pull a single objectGUID from an ADS result
2675 * @param ads connection to ADS server
2676 * @param msg results of search
2677 * @param guid 37-byte area to receive text guid
2678 * @return boolean indicating success
2680 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2685 if (!smbldap_talloc_single_blob(talloc_tos(), ads->ldap.ld, msg, "objectGUID",
2690 status = GUID_from_ndr_blob(&blob, guid);
2691 talloc_free(blob.data);
2692 return NT_STATUS_IS_OK(status);
2697 * pull a single struct dom_sid from a ADS result
2698 * @param ads connection to ads server
2699 * @param msg Results of search
2700 * @param field Attribute to retrieve
2701 * @param sid Pointer to sid to store result
2702 * @return boolean inidicating success
2704 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2705 struct dom_sid *sid)
2707 return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
2711 * pull an array of struct dom_sids from a ADS result
2712 * @param ads connection to ads server
2713 * @param mem_ctx TALLOC_CTX for allocating sid array
2714 * @param msg Results of search
2715 * @param field Attribute to retrieve
2716 * @param sids pointer to sid array to allocate
2717 * @return the count of SIDs pulled
2719 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2720 LDAPMessage *msg, const char *field, struct dom_sid **sids)
2722 struct berval **values;
2726 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2731 for (i=0; values[i]; i++)
2735 (*sids) = talloc_array(mem_ctx, struct dom_sid, i);
2737 ldap_value_free_len(values);
2745 for (i=0; values[i]; i++) {
2746 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2748 DEBUG(10, ("pulling SID: %s\n",
2749 sid_string_dbg(&(*sids)[count])));
2754 ldap_value_free_len(values);
2759 * pull a struct security_descriptor from a ADS result
2760 * @param ads connection to ads server
2761 * @param mem_ctx TALLOC_CTX for allocating sid array
2762 * @param msg Results of search
2763 * @param field Attribute to retrieve
2764 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
2765 * @return boolean inidicating success
2767 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2768 LDAPMessage *msg, const char *field,
2769 struct security_descriptor **sd)
2771 struct berval **values;
2774 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2776 if (!values) return false;
2780 status = unmarshall_sec_desc(mem_ctx,
2781 (uint8 *)values[0]->bv_val,
2782 values[0]->bv_len, sd);
2783 if (!NT_STATUS_IS_OK(status)) {
2784 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2785 nt_errstr(status)));
2790 ldap_value_free_len(values);
2795 * in order to support usernames longer than 21 characters we need to
2796 * use both the sAMAccountName and the userPrincipalName attributes
2797 * It seems that not all users have the userPrincipalName attribute set
2799 * @param ads connection to ads server
2800 * @param mem_ctx TALLOC_CTX for allocating sid array
2801 * @param msg Results of search
2802 * @return the username
2804 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2810 /* lookup_name() only works on the sAMAccountName to
2811 returning the username portion of userPrincipalName
2812 breaks winbindd_getpwnam() */
2814 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2815 if (ret && (p = strchr_m(ret, '@'))) {
2820 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2825 * find the update serial number - this is the core of the ldap cache
2826 * @param ads connection to ads server
2827 * @param ads connection to ADS server
2828 * @param usn Pointer to retrieved update serial number
2829 * @return status of search
2831 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2833 const char *attrs[] = {"highestCommittedUSN", NULL};
2837 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2838 if (!ADS_ERR_OK(status))
2841 if (ads_count_replies(ads, res) != 1) {
2842 ads_msgfree(ads, res);
2843 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2846 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2847 ads_msgfree(ads, res);
2848 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2851 ads_msgfree(ads, res);
2855 /* parse a ADS timestring - typical string is
2856 '20020917091222.0Z0' which means 09:12.22 17th September
2858 static time_t ads_parse_time(const char *str)
2864 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2865 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2866 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2875 /********************************************************************
2876 ********************************************************************/
2878 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2880 const char *attrs[] = {"currentTime", NULL};
2885 ADS_STRUCT *ads_s = ads;
2887 if (!(ctx = talloc_init("ads_current_time"))) {
2888 return ADS_ERROR(LDAP_NO_MEMORY);
2891 /* establish a new ldap tcp session if necessary */
2893 if ( !ads->ldap.ld ) {
2894 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2895 ads->server.ldap_server )) == NULL )
2899 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2900 status = ads_connect( ads_s );
2901 if ( !ADS_ERR_OK(status))
2905 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2906 if (!ADS_ERR_OK(status)) {
2910 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2912 ads_msgfree(ads_s, res);
2913 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2917 /* but save the time and offset in the original ADS_STRUCT */
2919 ads->config.current_time = ads_parse_time(timestr);
2921 if (ads->config.current_time != 0) {
2922 ads->auth.time_offset = ads->config.current_time - time(NULL);
2923 DEBUG(4,("KDC time offset is %d seconds\n", ads->auth.time_offset));
2926 ads_msgfree(ads, res);
2928 status = ADS_SUCCESS;
2931 /* free any temporary ads connections */
2932 if ( ads_s != ads ) {
2933 ads_destroy( &ads_s );
2935 talloc_destroy(ctx);
2940 /********************************************************************
2941 ********************************************************************/
2943 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2945 const char *attrs[] = {"domainFunctionality", NULL};
2948 ADS_STRUCT *ads_s = ads;
2950 *val = DS_DOMAIN_FUNCTION_2000;
2952 /* establish a new ldap tcp session if necessary */
2954 if ( !ads->ldap.ld ) {
2955 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2956 ads->server.ldap_server )) == NULL )
2958 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2961 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2962 status = ads_connect( ads_s );
2963 if ( !ADS_ERR_OK(status))
2967 /* If the attribute does not exist assume it is a Windows 2000
2968 functional domain */
2970 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2971 if (!ADS_ERR_OK(status)) {
2972 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2973 status = ADS_SUCCESS;
2978 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2979 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2981 DEBUG(3,("ads_domain_func_level: %d\n", *val));
2984 ads_msgfree(ads, res);
2987 /* free any temporary ads connections */
2988 if ( ads_s != ads ) {
2989 ads_destroy( &ads_s );
2996 * find the domain sid for our domain
2997 * @param ads connection to ads server
2998 * @param sid Pointer to domain sid
2999 * @return status of search
3001 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
3003 const char *attrs[] = {"objectSid", NULL};
3007 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
3009 if (!ADS_ERR_OK(rc)) return rc;
3010 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
3011 ads_msgfree(ads, res);
3012 return ADS_ERROR_SYSTEM(ENOENT);
3014 ads_msgfree(ads, res);
3020 * find our site name
3021 * @param ads connection to ads server
3022 * @param mem_ctx Pointer to talloc context
3023 * @param site_name Pointer to the sitename
3024 * @return status of search
3026 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
3030 const char *dn, *service_name;
3031 const char *attrs[] = { "dsServiceName", NULL };
3033 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3034 if (!ADS_ERR_OK(status)) {
3038 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
3039 if (service_name == NULL) {
3040 ads_msgfree(ads, res);
3041 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3044 ads_msgfree(ads, res);
3046 /* go up three levels */
3047 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
3049 return ADS_ERROR(LDAP_NO_MEMORY);
3052 *site_name = talloc_strdup(mem_ctx, dn);
3053 if (*site_name == NULL) {
3054 return ADS_ERROR(LDAP_NO_MEMORY);
3059 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3064 * find the site dn where a machine resides
3065 * @param ads connection to ads server
3066 * @param mem_ctx Pointer to talloc context
3067 * @param computer_name name of the machine
3068 * @param site_name Pointer to the sitename
3069 * @return status of search
3071 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3075 const char *parent, *filter;
3076 char *config_context = NULL;
3079 /* shortcut a query */
3080 if (strequal(computer_name, ads->config.ldap_server_name)) {
3081 return ads_site_dn(ads, mem_ctx, site_dn);
3084 status = ads_config_path(ads, mem_ctx, &config_context);
3085 if (!ADS_ERR_OK(status)) {
3089 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3090 if (filter == NULL) {
3091 return ADS_ERROR(LDAP_NO_MEMORY);
3094 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3095 filter, NULL, &res);
3096 if (!ADS_ERR_OK(status)) {
3100 if (ads_count_replies(ads, res) != 1) {
3101 ads_msgfree(ads, res);
3102 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3105 dn = ads_get_dn(ads, mem_ctx, res);
3107 ads_msgfree(ads, res);
3108 return ADS_ERROR(LDAP_NO_MEMORY);
3111 /* go up three levels */
3112 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3113 if (parent == NULL) {
3114 ads_msgfree(ads, res);
3116 return ADS_ERROR(LDAP_NO_MEMORY);
3119 *site_dn = talloc_strdup(mem_ctx, parent);
3120 if (*site_dn == NULL) {
3121 ads_msgfree(ads, res);
3123 return ADS_ERROR(LDAP_NO_MEMORY);
3127 ads_msgfree(ads, res);
3133 * get the upn suffixes for a domain
3134 * @param ads connection to ads server
3135 * @param mem_ctx Pointer to talloc context
3136 * @param suffixes Pointer to an array of suffixes
3137 * @param num_suffixes Pointer to the number of suffixes
3138 * @return status of search
3140 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3145 char *config_context = NULL;
3146 const char *attrs[] = { "uPNSuffixes", NULL };
3148 status = ads_config_path(ads, mem_ctx, &config_context);
3149 if (!ADS_ERR_OK(status)) {
3153 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3155 return ADS_ERROR(LDAP_NO_MEMORY);
3158 status = ads_search_dn(ads, &res, base, attrs);
3159 if (!ADS_ERR_OK(status)) {
3163 if (ads_count_replies(ads, res) != 1) {
3164 ads_msgfree(ads, res);
3165 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3168 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3169 if ((*suffixes) == NULL) {
3170 ads_msgfree(ads, res);
3171 return ADS_ERROR(LDAP_NO_MEMORY);
3174 ads_msgfree(ads, res);
3180 * get the joinable ous for a domain
3181 * @param ads connection to ads server
3182 * @param mem_ctx Pointer to talloc context
3183 * @param ous Pointer to an array of ous
3184 * @param num_ous Pointer to the number of ous
3185 * @return status of search
3187 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3188 TALLOC_CTX *mem_ctx,
3193 LDAPMessage *res = NULL;
3194 LDAPMessage *msg = NULL;
3195 const char *attrs[] = { "dn", NULL };
3198 status = ads_search(ads, &res,
3199 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3201 if (!ADS_ERR_OK(status)) {
3205 count = ads_count_replies(ads, res);
3207 ads_msgfree(ads, res);
3208 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3211 for (msg = ads_first_entry(ads, res); msg;
3212 msg = ads_next_entry(ads, msg)) {
3216 dn = ads_get_dn(ads, talloc_tos(), msg);
3218 ads_msgfree(ads, res);
3219 return ADS_ERROR(LDAP_NO_MEMORY);
3222 if (!add_string_to_array(mem_ctx, dn,
3223 (const char ***)ous,
3226 ads_msgfree(ads, res);
3227 return ADS_ERROR(LDAP_NO_MEMORY);
3233 ads_msgfree(ads, res);
3240 * pull a struct dom_sid from an extended dn string
3241 * @param mem_ctx TALLOC_CTX
3242 * @param extended_dn string
3243 * @param flags string type of extended_dn
3244 * @param sid pointer to a struct dom_sid
3245 * @return NT_STATUS_OK on success,
3246 * NT_INVALID_PARAMETER on error,
3247 * NT_STATUS_NOT_FOUND if no SID present
3249 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3250 const char *extended_dn,
3251 enum ads_extended_dn_flags flags,
3252 struct dom_sid *sid)
3257 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3260 /* otherwise extended_dn gets stripped off */
3261 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3262 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3265 * ADS_EXTENDED_DN_HEX_STRING:
3266 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3268 * ADS_EXTENDED_DN_STRING (only with w2k3):
3269 * <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
3271 * Object with no SID, such as an Exchange Public Folder
3272 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3275 p = strchr(dn, ';');
3277 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3280 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3281 DEBUG(5,("No SID present in extended dn\n"));
3282 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3285 p += strlen(";<SID=");
3289 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3294 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3298 case ADS_EXTENDED_DN_STRING:
3299 if (!string_to_sid(sid, p)) {
3300 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3303 case ADS_EXTENDED_DN_HEX_STRING: {
3307 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3309 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3312 if (!sid_parse(buf, buf_len, sid)) {
3313 DEBUG(10,("failed to parse sid\n"));
3314 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3319 DEBUG(10,("unknown extended dn format\n"));
3320 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3323 return ADS_ERROR_NT(NT_STATUS_OK);
3326 /********************************************************************
3327 ********************************************************************/
3329 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3331 LDAPMessage *res = NULL;
3336 status = ads_find_machine_acct(ads, &res, lp_netbios_name());
3337 if (!ADS_ERR_OK(status)) {
3338 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3339 lp_netbios_name()));
3343 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3344 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3348 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3349 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3353 ads_msgfree(ads, res);
3358 /********************************************************************
3359 ********************************************************************/
3361 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3363 LDAPMessage *res = NULL;
3368 status = ads_find_machine_acct(ads, &res, machine_name);
3369 if (!ADS_ERR_OK(status)) {
3370 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3371 lp_netbios_name()));
3375 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3376 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3380 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3381 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3385 ads_msgfree(ads, res);
3390 /********************************************************************
3391 ********************************************************************/
3393 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3395 LDAPMessage *res = NULL;
3400 status = ads_find_machine_acct(ads, &res, lp_netbios_name());
3401 if (!ADS_ERR_OK(status)) {
3402 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3403 lp_netbios_name()));
3407 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3408 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3412 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3413 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3417 ads_msgfree(ads, res);
3424 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3427 * Join a machine to a realm
3428 * Creates the machine account and sets the machine password
3429 * @param ads connection to ads server
3430 * @param machine name of host to add
3431 * @param org_unit Organizational unit to place machine in
3432 * @return status of join
3434 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3435 uint32 account_type, const char *org_unit)
3438 LDAPMessage *res = NULL;
3441 /* machine name must be lowercase */
3442 machine = SMB_STRDUP(machine_name);
3443 strlower_m(machine);
3446 status = ads_find_machine_acct(ads, (void **)&res, machine);
3447 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3448 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3449 status = ads_leave_realm(ads, machine);
3450 if (!ADS_ERR_OK(status)) {
3451 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3452 machine, ads->config.realm));
3457 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3458 if (!ADS_ERR_OK(status)) {
3459 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3464 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3465 if (!ADS_ERR_OK(status)) {
3466 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3472 ads_msgfree(ads, res);
3479 * Delete a machine from the realm
3480 * @param ads connection to ads server
3481 * @param hostname Machine to remove
3482 * @return status of delete
3484 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3489 char *hostnameDN, *host;
3491 LDAPControl ldap_control;
3492 LDAPControl * pldap_control[2] = {NULL, NULL};
3494 pldap_control[0] = &ldap_control;
3495 memset(&ldap_control, 0, sizeof(LDAPControl));
3496 ldap_control.ldctl_oid = discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID);
3498 /* hostname must be lowercase */
3499 host = SMB_STRDUP(hostname);
3500 if (!strlower_m(host)) {
3502 return ADS_ERROR_SYSTEM(EINVAL);
3505 status = ads_find_machine_acct(ads, &res, host);
3506 if (!ADS_ERR_OK(status)) {
3507 DEBUG(0, ("Host account for %s does not exist.\n", host));
3512 msg = ads_first_entry(ads, res);
3515 return ADS_ERROR_SYSTEM(ENOENT);
3518 hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
3519 if (hostnameDN == NULL) {
3521 return ADS_ERROR_SYSTEM(ENOENT);
3524 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3526 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3528 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3531 if (rc != LDAP_SUCCESS) {
3532 const char *attrs[] = { "cn", NULL };
3533 LDAPMessage *msg_sub;
3535 /* we only search with scope ONE, we do not expect any further
3536 * objects to be created deeper */
3538 status = ads_do_search_retry(ads, hostnameDN,
3539 LDAP_SCOPE_ONELEVEL,
3540 "(objectclass=*)", attrs, &res);
3542 if (!ADS_ERR_OK(status)) {
3544 TALLOC_FREE(hostnameDN);
3548 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3549 msg_sub = ads_next_entry(ads, msg_sub)) {
3553 if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
3555 TALLOC_FREE(hostnameDN);
3556 return ADS_ERROR(LDAP_NO_MEMORY);
3559 status = ads_del_dn(ads, dn);
3560 if (!ADS_ERR_OK(status)) {
3561 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3564 TALLOC_FREE(hostnameDN);
3571 /* there should be no subordinate objects anymore */
3572 status = ads_do_search_retry(ads, hostnameDN,
3573 LDAP_SCOPE_ONELEVEL,
3574 "(objectclass=*)", attrs, &res);
3576 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3578 TALLOC_FREE(hostnameDN);
3582 /* delete hostnameDN now */
3583 status = ads_del_dn(ads, hostnameDN);
3584 if (!ADS_ERR_OK(status)) {
3586 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3587 TALLOC_FREE(hostnameDN);
3592 TALLOC_FREE(hostnameDN);
3594 status = ads_find_machine_acct(ads, &res, host);
3595 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3596 DEBUG(3, ("Failed to remove host account.\n"));
3606 * pull all token-sids from an LDAP dn
3607 * @param ads connection to ads server
3608 * @param mem_ctx TALLOC_CTX for allocating sid array
3609 * @param dn of LDAP object
3610 * @param user_sid pointer to struct dom_sid (objectSid)
3611 * @param primary_group_sid pointer to struct dom_sid (self composed)
3612 * @param sids pointer to sid array to allocate
3613 * @param num_sids counter of SIDs pulled
3614 * @return status of token query
3616 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3617 TALLOC_CTX *mem_ctx,
3619 struct dom_sid *user_sid,
3620 struct dom_sid *primary_group_sid,
3621 struct dom_sid **sids,
3625 LDAPMessage *res = NULL;
3627 size_t tmp_num_sids;
3628 struct dom_sid *tmp_sids;
3629 struct dom_sid tmp_user_sid;
3630 struct dom_sid tmp_primary_group_sid;
3632 const char *attrs[] = {
3639 status = ads_search_retry_dn(ads, &res, dn, attrs);
3640 if (!ADS_ERR_OK(status)) {
3644 count = ads_count_replies(ads, res);
3646 ads_msgfree(ads, res);
3647 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3650 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3651 ads_msgfree(ads, res);
3652 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3655 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3656 ads_msgfree(ads, res);
3657 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3661 /* hack to compose the primary group sid without knowing the
3664 struct dom_sid domsid;
3666 sid_copy(&domsid, &tmp_user_sid);
3668 if (!sid_split_rid(&domsid, NULL)) {
3669 ads_msgfree(ads, res);
3670 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3673 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3674 ads_msgfree(ads, res);
3675 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3679 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3681 if (tmp_num_sids == 0 || !tmp_sids) {
3682 ads_msgfree(ads, res);
3683 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3687 *num_sids = tmp_num_sids;
3695 *user_sid = tmp_user_sid;
3698 if (primary_group_sid) {
3699 *primary_group_sid = tmp_primary_group_sid;
3702 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3704 ads_msgfree(ads, res);
3705 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3709 * Find a sAMAccoutName in LDAP
3710 * @param ads connection to ads server
3711 * @param mem_ctx TALLOC_CTX for allocating sid array
3712 * @param samaccountname to search
3713 * @param uac_ret uint32 pointer userAccountControl attribute value
3714 * @param dn_ret pointer to dn
3715 * @return status of token query
3717 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3718 TALLOC_CTX *mem_ctx,
3719 const char *samaccountname,
3721 const char **dn_ret)
3724 const char *attrs[] = { "userAccountControl", NULL };
3726 LDAPMessage *res = NULL;
3730 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3732 if (filter == NULL) {
3733 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3737 status = ads_do_search_all(ads, ads->config.bind_path,
3739 filter, attrs, &res);
3741 if (!ADS_ERR_OK(status)) {
3745 if (ads_count_replies(ads, res) != 1) {
3746 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3750 dn = ads_get_dn(ads, talloc_tos(), res);
3752 status = ADS_ERROR(LDAP_NO_MEMORY);
3756 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3757 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3766 *dn_ret = talloc_strdup(mem_ctx, dn);
3768 status = ADS_ERROR(LDAP_NO_MEMORY);
3774 ads_msgfree(ads, res);
3780 * find our configuration path
3781 * @param ads connection to ads server
3782 * @param mem_ctx Pointer to talloc context
3783 * @param config_path Pointer to the config path
3784 * @return status of search
3786 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3787 TALLOC_CTX *mem_ctx,
3791 LDAPMessage *res = NULL;
3792 const char *config_context = NULL;
3793 const char *attrs[] = { "configurationNamingContext", NULL };
3795 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3796 "(objectclass=*)", attrs, &res);
3797 if (!ADS_ERR_OK(status)) {
3801 config_context = ads_pull_string(ads, mem_ctx, res,
3802 "configurationNamingContext");
3803 ads_msgfree(ads, res);
3804 if (!config_context) {
3805 return ADS_ERROR(LDAP_NO_MEMORY);
3809 *config_path = talloc_strdup(mem_ctx, config_context);
3810 if (!*config_path) {
3811 return ADS_ERROR(LDAP_NO_MEMORY);
3815 return ADS_ERROR(LDAP_SUCCESS);
3819 * find the displayName of an extended right
3820 * @param ads connection to ads server
3821 * @param config_path The config path
3822 * @param mem_ctx Pointer to talloc context
3823 * @param GUID struct of the rightsGUID
3824 * @return status of search
3826 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3827 const char *config_path,
3828 TALLOC_CTX *mem_ctx,
3829 const struct GUID *rights_guid)
3832 LDAPMessage *res = NULL;
3834 const char *attrs[] = { "displayName", NULL };
3835 const char *result = NULL;
3838 if (!ads || !mem_ctx || !rights_guid) {
3842 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3843 GUID_string(mem_ctx, rights_guid));
3848 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3853 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3855 if (!ADS_ERR_OK(rc)) {
3859 if (ads_count_replies(ads, res) != 1) {
3863 result = ads_pull_string(ads, mem_ctx, res, "displayName");
3866 ads_msgfree(ads, res);
3871 * verify or build and verify an account ou
3872 * @param mem_ctx Pointer to talloc context
3873 * @param ads connection to ads server
3875 * @return status of search
3878 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3880 const char **account_ou)
3886 exploded_dn = ldap_explode_dn(*account_ou, 0);
3888 ldap_value_free(exploded_dn);
3892 ou_string = ads_ou_string(ads, *account_ou);
3894 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3897 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3898 ads->config.bind_path);
3899 SAFE_FREE(ou_string);
3902 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3905 exploded_dn = ldap_explode_dn(name, 0);
3907 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3909 ldap_value_free(exploded_dn);