s3:libads/ldap.c: if the client belongs to no site at all any dc is the closest
[kai/samba.git] / source3 / libads / ldap.c
index d60afcd0d553fa2431be9e639dc090e0f4061700..c0bb9c7e2df85346b4bbe80eb3e2062cc3d36da9 100644 (file)
@@ -9,7 +9,7 @@
    
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 #include "includes.h"
+#include "lib/ldb/include/includes.h"
 
 #ifdef HAVE_LDAP
 
@@ -43,7 +43,7 @@
 #define LDAP_SERVER_TREE_DELETE_OID    "1.2.840.113556.1.4.805"
 
 static SIG_ATOMIC_T gotalarm;
-                                                                                                                   
+
 /***************************************************************
  Signal function to tell us we timed out.
 ****************************************************************/
@@ -57,6 +57,10 @@ static void gotalarm_sig(void)
 {
        LDAP *ldp = NULL;
 
+
+       DEBUG(10, ("Opening connection to LDAP server '%s:%d', timeout "
+                  "%u seconds\n", server, port, to));
+
        /* Setup timeout */
        gotalarm = 0;
        CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
@@ -66,8 +70,10 @@ static void gotalarm_sig(void)
        ldp = ldap_open(server, port);
 
        if (ldp == NULL) {
-               DEBUG(2,("Could not open LDAP connection to %s:%d: %s\n",
+               DEBUG(2,("Could not open connection to LDAP server %s:%d: %s\n",
                         server, port, strerror(errno)));
+       } else {
+               DEBUG(10, ("Connected to LDAP server '%s:%d'\n", server, port));
        }
 
        /* Teardown timeout. */
@@ -119,7 +125,7 @@ static int ldap_search_with_timeout(LDAP *ld,
  Do client and server sitename match ?
 **********************************************/
 
-BOOL ads_sitename_match(ADS_STRUCT *ads)
+bool ads_sitename_match(ADS_STRUCT *ads)
 {
        if (ads->config.server_site_name == NULL &&
            ads->config.client_site_name == NULL ) {
@@ -143,16 +149,21 @@ BOOL ads_sitename_match(ADS_STRUCT *ads)
  Is this the closest DC ?
 **********************************************/
 
-BOOL ads_closest_dc(ADS_STRUCT *ads)
+bool ads_closest_dc(ADS_STRUCT *ads)
 {
-       if (ads->config.flags & ADS_CLOSEST) {
-               DEBUG(10,("ads_closest_dc: ADS_CLOSEST flag set\n"));
+       if (ads->config.flags & NBT_SERVER_CLOSEST) {
+               DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag set\n"));
                return True;
        }
 
        /* not sure if this can ever happen */
        if (ads_sitename_match(ads)) {
-               DEBUG(10,("ads_closest_dc: ADS_CLOSEST flag not set but sites match\n"));
+               DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag not set but sites match\n"));
+               return True;
+       }
+
+       if (ads->config.client_site_name == NULL) {
+               DEBUG(10,("ads_closest_dc: client belongs to no site\n"));
                return True;
        }
 
@@ -167,10 +178,12 @@ BOOL ads_closest_dc(ADS_STRUCT *ads)
   try a connection to a given ldap server, returning True and setting the servers IP
   in the ads struct if successful
  */
-BOOL ads_try_connect(ADS_STRUCT *ads, const char *server )
+static bool ads_try_connect(ADS_STRUCT *ads, const char *server, bool gc)
 {
        char *srv;
-       struct cldap_netlogon_reply cldap_reply;
+       struct NETLOGON_SAM_LOGON_RESPONSE_EX cldap_reply;
+       TALLOC_CTX *mem_ctx = NULL;
+       bool ret = false;
 
        if (!server || !*server) {
                return False;
@@ -179,25 +192,31 @@ BOOL ads_try_connect(ADS_STRUCT *ads, const char *server )
        DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n", 
                server, ads->server.realm));
 
+       mem_ctx = talloc_init("ads_try_connect");
+       if (!mem_ctx) {
+               DEBUG(0,("out of memory\n"));
+               return false;
+       }
+
        /* this copes with inet_ntoa brokenness */
        
        srv = SMB_STRDUP(server);
 
        ZERO_STRUCT( cldap_reply );
 
-       if ( !ads_cldap_netlogon( srv, ads->server.realm, &cldap_reply ) ) {
+       if ( !ads_cldap_netlogon_5(mem_ctx, srv, ads->server.realm, &cldap_reply ) ) {
                DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", srv));
-               SAFE_FREE( srv );
-               return False;
+               ret = false;
+               goto out;
        }
 
        /* Check the CLDAP reply flags */
 
-       if ( !(cldap_reply.flags & ADS_LDAP) ) {
+       if ( !(cldap_reply.server_type & NBT_SERVER_LDAP) ) {
                DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
                        srv));
-               SAFE_FREE( srv );
-               return False;
+               ret = false;
+               goto out;
        }
 
        /* Fill in the ads->config values */
@@ -209,30 +228,40 @@ BOOL ads_try_connect(ADS_STRUCT *ads, const char *server )
        SAFE_FREE(ads->config.client_site_name);
        SAFE_FREE(ads->server.workgroup);
 
-       ads->config.flags              = cldap_reply.flags;
-       ads->config.ldap_server_name   = SMB_STRDUP(cldap_reply.hostname);
-       strupper_m(cldap_reply.domain);
-       ads->config.realm              = SMB_STRDUP(cldap_reply.domain);
+       ads->config.flags              = cldap_reply.server_type;
+       ads->config.ldap_server_name   = SMB_STRDUP(cldap_reply.pdc_dns_name);
+       ads->config.realm              = SMB_STRDUP(cldap_reply.dns_domain);
+       strupper_m(ads->config.realm);
        ads->config.bind_path          = ads_build_dn(ads->config.realm);
-       if (*cldap_reply.server_site_name) {
+       if (*cldap_reply.server_site) {
                ads->config.server_site_name =
-                       SMB_STRDUP(cldap_reply.server_site_name);
+                       SMB_STRDUP(cldap_reply.server_site);
        }
-       if (*cldap_reply.client_site_name) {
+       if (*cldap_reply.client_site) {
                ads->config.client_site_name =
-                       SMB_STRDUP(cldap_reply.client_site_name);
+                       SMB_STRDUP(cldap_reply.client_site);
+       }
+       ads->server.workgroup          = SMB_STRDUP(cldap_reply.domain);
+
+       ads->ldap.port = gc ? LDAP_GC_PORT : LDAP_PORT;
+       if (!interpret_string_addr(&ads->ldap.ss, srv, 0)) {
+               DEBUG(1,("ads_try_connect: unable to convert %s "
+                       "to an address\n",
+                       srv));
+               ret = false;
+               goto out;
        }
-               
-       ads->server.workgroup          = SMB_STRDUP(cldap_reply.netbios_domain);
 
-       ads->ldap_port = LDAP_PORT;
-       ads->ldap_ip = *interpret_addr2(srv);
-       SAFE_FREE(srv);
-       
        /* Store our site name. */
-       sitename_store( cldap_reply.domain, cldap_reply.client_site_name );
+       sitename_store( cldap_reply.domain, cldap_reply.client_site);
+       sitename_store( cldap_reply.dns_domain, cldap_reply.client_site);
 
-       return True;
+       ret = true;
+ out:
+       SAFE_FREE(srv);
+       TALLOC_FREE(mem_ctx);
+
+       return ret;
 }
 
 /**********************************************************************
@@ -243,12 +272,14 @@ BOOL ads_try_connect(ADS_STRUCT *ads, const char *server )
 
 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
 {
+       const char *c_domain;
        const char *c_realm;
        int count, i=0;
        struct ip_service *ip_list;
-       pstring realm;
-       BOOL got_realm = False;
-       BOOL use_own_domain = False;
+       const char *realm;
+       const char *domain;
+       bool got_realm = False;
+       bool use_own_domain = False;
        char *sitename;
        NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
 
@@ -256,7 +287,7 @@ static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
 
        /* realm */
        c_realm = ads->server.realm;
-       
+
        if ( !c_realm || !*c_realm ) {
                /* special case where no realm and no workgroup means our own */
                if ( !ads->server.workgroup || !*ads->server.workgroup ) {
@@ -264,33 +295,64 @@ static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
                        c_realm = lp_realm();
                }
        }
-       
-       if (c_realm && *c_realm) 
+
+       if (c_realm && *c_realm)
                got_realm = True;
-                  
-       /* we need to try once with the realm name and fallback to the 
+
+       /* we need to try once with the realm name and fallback to the
           netbios domain name if we fail (if netbios has not been disabled */
-          
+
        if ( !got_realm && !lp_disable_netbios() ) {
                c_realm = ads->server.workgroup;
                if (!c_realm || !*c_realm) {
                        if ( use_own_domain )
                                c_realm = lp_workgroup();
                }
-               
-               if ( !c_realm || !*c_realm ) {
-                       DEBUG(0,("ads_find_dc: no realm or workgroup!  Don't know what to do\n"));
-                       return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
+       }
+
+       if ( !c_realm || !*c_realm ) {
+               DEBUG(0,("ads_find_dc: no realm or workgroup!  Don't know what to do\n"));
+               return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
+       }
+
+       if ( use_own_domain ) {
+               c_domain = lp_workgroup();
+       } else {
+               c_domain = ads->server.workgroup;
+       }
+
+       realm = c_realm;
+       domain = c_domain;
+
+       /*
+        * In case of LDAP we use get_dc_name() as that
+        * creates the custom krb5.conf file
+        */
+       if (!(ads->auth.flags & ADS_AUTH_NO_BIND)) {
+               fstring srv_name;
+               struct sockaddr_storage ip_out;
+
+               DEBUG(6,("ads_find_dc: (ldap) looking for %s '%s'\n",
+                       (got_realm ? "realm" : "domain"), realm));
+
+               if (get_dc_name(domain, realm, srv_name, &ip_out)) {
+                       /*
+                        * we call ads_try_connect() to fill in the
+                        * ads->config details
+                        */
+                       if (ads_try_connect(ads, srv_name, false)) {
+                               return NT_STATUS_OK;
+                       }
                }
+
+               return NT_STATUS_NO_LOGON_SERVERS;
        }
-       
-       pstrcpy( realm, c_realm );
 
        sitename = sitename_fetch(realm);
 
  again:
 
-       DEBUG(6,("ads_find_dc: looking for %s '%s'\n", 
+       DEBUG(6,("ads_find_dc: (cldap) looking for %s '%s'\n",
                (got_realm ? "realm" : "domain"), realm));
 
        status = get_sorted_dc_list(realm, sitename, &ip_list, &count, got_realm);
@@ -300,17 +362,17 @@ static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
                        got_realm = False;
                        goto again;
                }
-               
+
                SAFE_FREE(sitename);
                return status;
        }
 
        /* if we fail this loop, then giveup since all the IP addresses returned were dead */
        for ( i=0; i<count; i++ ) {
-               fstring server;
-               
-               fstrcpy( server, inet_ntoa(ip_list[i].ip) );
-               
+               char server[INET6_ADDRSTRLEN];
+
+               print_sockaddr(server, sizeof(server), &ip_list[i].ss);
+
                if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
                        continue;
 
@@ -333,8 +395,8 @@ static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
                                continue;
                        }
                }
-                       
-               if ( ads_try_connect(ads, server) ) {
+
+               if ( ads_try_connect(ads, server, false) ) {
                        SAFE_FREE(ip_list);
                        SAFE_FREE(sitename);
                        return NT_STATUS_OK;
@@ -361,6 +423,138 @@ static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
        return NT_STATUS_NO_LOGON_SERVERS;
 }
 
+/*********************************************************************
+ *********************************************************************/
+
+static NTSTATUS ads_lookup_site(void)
+{
+       ADS_STRUCT *ads = NULL;
+       ADS_STATUS ads_status;
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+
+       ads = ads_init(lp_realm(), NULL, NULL);
+       if (!ads) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       /* The NO_BIND here will find a DC and set the client site
+          but not establish the TCP connection */
+
+       ads->auth.flags = ADS_AUTH_NO_BIND;
+       ads_status = ads_connect(ads);
+       if (!ADS_ERR_OK(ads_status)) {
+               DEBUG(4, ("ads_lookup_site: ads_connect to our realm failed! (%s)\n",
+                         ads_errstr(ads_status)));
+       }
+       nt_status = ads_ntstatus(ads_status);
+
+       if (ads) {
+               ads_destroy(&ads);
+       }
+
+       return nt_status;
+}
+
+/*********************************************************************
+ *********************************************************************/
+
+static const char* host_dns_domain(const char *fqdn)
+{
+       const char *p = fqdn;
+
+       /* go to next char following '.' */
+
+       if ((p = strchr_m(fqdn, '.')) != NULL) {
+               p++;
+       }
+
+       return p;
+}
+
+
+/**
+ * Connect to the Global Catalog server
+ * @param ads Pointer to an existing ADS_STRUCT
+ * @return status of connection
+ *
+ * Simple wrapper around ads_connect() that fills in the
+ * GC ldap server information
+ **/
+
+ADS_STATUS ads_connect_gc(ADS_STRUCT *ads)
+{
+       TALLOC_CTX *frame = talloc_stackframe();
+       struct dns_rr_srv *gcs_list;
+       int num_gcs;
+       char *realm = ads->server.realm;
+       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+       ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
+       int i;
+       bool done = false;
+       char *sitename = NULL;
+
+       if (!realm)
+               realm = lp_realm();
+
+       if ((sitename = sitename_fetch(realm)) == NULL) {
+               ads_lookup_site();
+               sitename = sitename_fetch(realm);
+       }
+
+       do {
+               /* We try once with a sitename and once without
+                  (unless we don't have a sitename and then we're
+                  done */
+
+               if (sitename == NULL)
+                       done = true;
+
+               nt_status = ads_dns_query_gcs(frame, realm, sitename,
+                                             &gcs_list, &num_gcs);
+
+               SAFE_FREE(sitename);
+
+               if (!NT_STATUS_IS_OK(nt_status)) {
+                       ads_status = ADS_ERROR_NT(nt_status);
+                       goto done;
+               }
+
+               /* Loop until we get a successful connection or have gone
+                  through them all.  When connecting a GC server, make sure that
+                  the realm is the server's DNS name and not the forest root */
+
+               for (i=0; i<num_gcs; i++) {
+                       ads->server.gc = true;
+                       ads->server.ldap_server = SMB_STRDUP(gcs_list[i].hostname);
+                       ads->server.realm = SMB_STRDUP(host_dns_domain(ads->server.ldap_server));
+                       ads_status = ads_connect(ads);
+                       if (ADS_ERR_OK(ads_status)) {
+                               /* Reset the bind_dn to "".  A Global Catalog server
+                                  may host  multiple domain trees in a forest.
+                                  Windows 2003 GC server will accept "" as the search
+                                  path to imply search all domain trees in the forest */
+
+                               SAFE_FREE(ads->config.bind_path);
+                               ads->config.bind_path = SMB_STRDUP("");
+
+
+                               goto done;
+                       }
+                       SAFE_FREE(ads->server.ldap_server);
+                       SAFE_FREE(ads->server.realm);
+               }
+
+               TALLOC_FREE(gcs_list);
+               num_gcs = 0;
+       } while (!done);
+
+done:
+       SAFE_FREE(sitename);
+       talloc_destroy(frame);
+
+       return ads_status;
+}
+
 
 /**
  * Connect to the LDAP server
@@ -372,14 +566,23 @@ ADS_STATUS ads_connect(ADS_STRUCT *ads)
        int version = LDAP_VERSION3;
        ADS_STATUS status;
        NTSTATUS ntstatus;
+       char addr[INET6_ADDRSTRLEN];
 
-       ads->last_attempt = time(NULL);
-       ads->ld = NULL;
+       ZERO_STRUCT(ads->ldap);
+       ads->ldap.last_attempt  = time(NULL);
+       ads->ldap.wrap_type     = ADS_SASLWRAP_TYPE_PLAIN;
 
        /* try with a user specified server */
 
-       if (ads->server.ldap_server && 
-           ads_try_connect(ads, ads->server.ldap_server)) {
+       if (DEBUGLEVEL >= 11) {
+               char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
+               DEBUG(11,("ads_connect: entering\n"));
+               DEBUGADD(11,("%s\n", s));
+               TALLOC_FREE(s);
+       }
+
+       if (ads->server.ldap_server &&
+           ads_try_connect(ads, ads->server.ldap_server, ads->server.gc)) {
                goto got_connection;
        }
 
@@ -388,10 +591,13 @@ ADS_STATUS ads_connect(ADS_STRUCT *ads)
                goto got_connection;
        }
 
-       return ADS_ERROR_NT(ntstatus);
+       status = ADS_ERROR_NT(ntstatus);
+       goto out;
 
 got_connection:
-       DEBUG(3,("Connected to LDAP server %s\n", inet_ntoa(ads->ldap_ip)));
+
+       print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
+       DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
 
        if (!ads->auth.user_name) {
                /* Must use the userPrincipalName value here or sAMAccountName
@@ -405,7 +611,8 @@ got_connection:
        }
 
        if (!ads->auth.kdc_server) {
-               ads->auth.kdc_server = SMB_STRDUP(inet_ntoa(ads->ldap_ip));
+               print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
+               ads->auth.kdc_server = SMB_STRDUP(addr);
        }
 
 #if KRB5_DNS_HACK
@@ -422,48 +629,102 @@ got_connection:
        /* If the caller() requested no LDAP bind, then we are done */
        
        if (ads->auth.flags & ADS_AUTH_NO_BIND) {
-               return ADS_SUCCESS;
+               status = ADS_SUCCESS;
+               goto out;
+       }
+
+       ads->ldap.mem_ctx = talloc_init("ads LDAP connection memory");
+       if (!ads->ldap.mem_ctx) {
+               status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+               goto out;
        }
        
        /* Otherwise setup the TCP LDAP session */
 
-       if ( (ads->ld = ldap_open_with_timeout(ads->config.ldap_server_name, 
-               LDAP_PORT, lp_ldap_timeout())) == NULL )
-       {
-               return ADS_ERROR(LDAP_OPERATIONS_ERROR);
+       ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name,
+                                             ads->ldap.port, lp_ldap_timeout());
+       if (ads->ldap.ld == NULL) {
+               status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
+               goto out;
        }
+       DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
 
        /* cache the successful connection for workgroup and realm */
        if (ads_closest_dc(ads)) {
-               saf_store( ads->server.workgroup, inet_ntoa(ads->ldap_ip));
-               saf_store( ads->server.realm, inet_ntoa(ads->ldap_ip));
+               print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
+               saf_store( ads->server.workgroup, addr);
+               saf_store( ads->server.realm, addr);
        }
 
-       ldap_set_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
+       ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
 
-       status = ADS_ERROR(smb_ldap_start_tls(ads->ld, version));
+       status = ADS_ERROR(smb_ldap_start_tls(ads->ldap.ld, version));
        if (!ADS_ERR_OK(status)) {
-               return status;
+               goto out;
        }
 
        /* fill in the current time and offsets */
        
        status = ads_current_time( ads );
        if ( !ADS_ERR_OK(status) ) {
-               return status;
+               goto out;
        }
 
        /* Now do the bind */
        
        if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
-               return ADS_ERROR(ldap_simple_bind_s( ads->ld, NULL, NULL));
+               status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
+               goto out;
        }
 
        if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
-               return ADS_ERROR(ldap_simple_bind_s( ads->ld, ads->auth.user_name, ads->auth.password));
+               status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
+               goto out;
        }
 
-       return ads_sasl_bind(ads);
+       status = ads_sasl_bind(ads);
+
+ out:
+       if (DEBUGLEVEL >= 11) {
+               char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
+               DEBUG(11,("ads_connect: leaving with: %s\n",
+                       ads_errstr(status)));
+               DEBUGADD(11,("%s\n", s));
+               TALLOC_FREE(s);
+       }
+
+       return status;
+}
+
+/**
+ * Connect to the LDAP server using given credentials
+ * @param ads Pointer to an existing ADS_STRUCT
+ * @return status of connection
+ **/
+ADS_STATUS ads_connect_user_creds(ADS_STRUCT *ads)
+{
+       ads->auth.flags |= ADS_AUTH_USER_CREDS;
+
+       return ads_connect(ads);
+}
+
+/**
+ * Disconnect the LDAP server
+ * @param ads Pointer to an existing ADS_STRUCT
+ **/
+void ads_disconnect(ADS_STRUCT *ads)
+{
+       if (ads->ldap.ld) {
+               ldap_unbind(ads->ldap.ld);
+               ads->ldap.ld = NULL;
+       }
+       if (ads->ldap.wrap_ops && ads->ldap.wrap_ops->disconnect) {
+               ads->ldap.wrap_ops->disconnect(ads);
+       }
+       if (ads->ldap.mem_ctx) {
+               talloc_free(ads->ldap.mem_ctx);
+       }
+       ZERO_STRUCT(ads->ldap);
 }
 
 /*
@@ -514,7 +775,8 @@ static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
 {
        char **values;
        int i;
-       
+       size_t size;
+
        if (!in_vals) return NULL;
        for (i=0; in_vals[i]; i++)
                ; /* count values */
@@ -522,7 +784,10 @@ static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
        if (!values) return NULL;
 
        for (i=0; in_vals[i]; i++) {
-               push_utf8_talloc(ctx, &values[i], in_vals[i]);
+               if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
+                       TALLOC_FREE(values);
+                       return NULL;
+               }
        }
        return values;
 }
@@ -534,6 +799,7 @@ static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
 {
        char **values;
        int i;
+       size_t converted_size;
        
        if (!in_vals) return NULL;
        for (i=0; in_vals[i]; i++)
@@ -542,7 +808,11 @@ static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
        if (!values) return NULL;
 
        for (i=0; in_vals[i]; i++) {
-               pull_utf8_talloc(ctx, &values[i], in_vals[i]);
+               if (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
+                                     &converted_size)) {
+                       DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
+                                "%s", strerror(errno)));
+               }
        }
        return values;
 }
@@ -569,7 +839,8 @@ static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
                                           int *count, struct berval **cookie)
 {
        int rc, i, version;
-       char *utf8_expr, *utf8_path, **search_attrs;
+       char *utf8_expr, *utf8_path, **search_attrs = NULL;
+       size_t converted_size;
        LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
        BerElement *cookie_be = NULL;
        struct berval *cookie_bv= NULL;
@@ -587,8 +858,9 @@ static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
        /* 0 means the conversion worked but the result was empty 
           so we only fail if it's -1.  In any case, it always 
           at least nulls out the dest */
-       if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
-           (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
+       if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
+           !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
+       {
                rc = LDAP_NO_MEMORY;
                goto done;
        }
@@ -598,14 +870,14 @@ static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
        else {
                /* This would be the utf8-encoded version...*/
                /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
-               if (!(str_list_copy(&search_attrs, attrs))) {
+               if (!(search_attrs = str_list_copy(talloc_tos(), attrs))) {
                        rc = LDAP_NO_MEMORY;
                        goto done;
                }
        }
                
        /* Paged results only available on ldap v3 or later */
-       ldap_get_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
+       ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
        if (version < LDAP_VERSION3) {
                rc =  LDAP_NOT_SUPPORTED;
                goto done;
@@ -682,9 +954,9 @@ static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
           leaving this in despite the control that says don't generate
           referrals, in case the server doesn't support it (jmcd)
        */
-       ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
+       ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
 
-       rc = ldap_search_with_timeout(ads->ld, utf8_path, scope, utf8_expr, 
+       rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr, 
                                      search_attrs, 0, controls,
                                      NULL, LDAP_NO_LIMIT,
                                      (LDAPMessage **)res);
@@ -698,7 +970,7 @@ static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
                goto done;
        }
 
-       rc = ldap_parse_result(ads->ld, *res, NULL, NULL, NULL,
+       rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
                                        NULL, &rcontrols,  0);
 
        if (!rcontrols) {
@@ -735,7 +1007,7 @@ done:
        }
  
        /* if/when we decide to utf8-encode attrs, take out this next line */
-       str_list_free(&search_attrs);
+       TALLOC_FREE(search_attrs);
 
        return ADS_ERROR(rc);
 }
@@ -789,8 +1061,8 @@ static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
 
                /* this relies on the way that ldap_add_result_entry() works internally. I hope
                   that this works on all ldap libs, but I have only tested with openldap */
-               for (msg = ads_first_entry(ads, res2); msg; msg = next) {
-                       next = ads_next_entry(ads, msg);
+               for (msg = ads_first_message(ads, res2); msg; msg = next) {
+                       next = ads_next_message(ads, msg);
                        ldap_add_result_entry((LDAPMessage **)res, msg);
                }
                /* note that we do not free res2, as the memory is now
@@ -840,7 +1112,7 @@ static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
  **/
 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
                                int scope, const char *expr, const char **attrs,
-                               BOOL(*fn)(char *, void **, void *), 
+                               bool (*fn)(ADS_STRUCT *, char *, void **, void *), 
                                void *data_area)
 {
        struct berval *cookie = NULL;
@@ -885,6 +1157,7 @@ ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
 {
        int rc;
        char *utf8_expr, *utf8_path, **search_attrs = NULL;
+       size_t converted_size;
        TALLOC_CTX *ctx;
 
        *res = NULL;
@@ -896,8 +1169,9 @@ ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
        /* 0 means the conversion worked but the result was empty 
           so we only fail if it's negative.  In any case, it always 
           at least nulls out the dest */
-       if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
-           (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
+       if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
+           !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
+       {
                DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
                rc = LDAP_NO_MEMORY;
                goto done;
@@ -908,7 +1182,7 @@ ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
        else {
                /* This would be the utf8-encoded version...*/
                /* if (!(search_attrs = ads_push_strvals(ctx, attrs)))  */
-               if (!(str_list_copy(&search_attrs, attrs)))
+               if (!(search_attrs = str_list_copy(talloc_tos(), attrs)))
                {
                        DEBUG(1,("ads_do_search: str_list_copy() failed!"));
                        rc = LDAP_NO_MEMORY;
@@ -917,9 +1191,9 @@ ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
        }
 
        /* see the note in ads_do_paged_search - we *must* disable referrals */
-       ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
+       ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
 
-       rc = ldap_search_with_timeout(ads->ld, utf8_path, scope, utf8_expr,
+       rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
                                      search_attrs, 0, NULL, NULL, 
                                      LDAP_NO_LIMIT,
                                      (LDAPMessage **)res);
@@ -932,7 +1206,7 @@ ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
  done:
        talloc_destroy(ctx);
        /* if/when we decide to utf8-encode attrs, take out this next line */
-       str_list_free(&search_attrs);
+       TALLOC_FREE(search_attrs);
        return ADS_ERROR(rc);
 }
 /**
@@ -995,15 +1269,16 @@ void ads_memfree(ADS_STRUCT *ads, void *mem)
  char *ads_get_dn(ADS_STRUCT *ads, LDAPMessage *msg)
 {
        char *utf8_dn, *unix_dn;
+       size_t converted_size;
 
-       utf8_dn = ldap_get_dn(ads->ld, msg);
+       utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
 
        if (!utf8_dn) {
                DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
                return NULL;
        }
 
-       if (pull_utf8_allocate(&unix_dn, utf8_dn) == (size_t)-1) {
+       if (!pull_utf8_allocate(&unix_dn, utf8_dn, &converted_size)) {
                DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
                        utf8_dn ));
                return NULL;
@@ -1205,6 +1480,7 @@ ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
 {
        int ret,i;
        char *utf8_dn = NULL;
+       size_t converted_size;
        /* 
           this control is needed to modify that contains a currently 
           non-existent attribute (but allowable for the object) to run
@@ -1218,7 +1494,7 @@ ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
        controls[0] = &PermitModify;
        controls[1] = NULL;
 
-       if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) {
+       if (!push_utf8_allocate(&utf8_dn, mod_dn, &converted_size)) {
                return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
        }
 
@@ -1226,7 +1502,7 @@ ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
        for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
        /* make sure the end of the list is NULL */
        mods[i] = NULL;
-       ret = ldap_modify_ext_s(ads->ld, utf8_dn,
+       ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
                                (LDAPMod **) mods, controls, NULL);
        SAFE_FREE(utf8_dn);
        return ADS_ERROR(ret);
@@ -1243,8 +1519,9 @@ ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
 {
        int ret, i;
        char *utf8_dn = NULL;
+       size_t converted_size;
 
-       if (push_utf8_allocate(&utf8_dn, new_dn) == -1) {
+       if (!push_utf8_allocate(&utf8_dn, new_dn, &converted_size)) {
                DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
                return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
        }
@@ -1254,7 +1531,7 @@ ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
        /* make sure the end of the list is NULL */
        mods[i] = NULL;
 
-       ret = ldap_add_s(ads->ld, utf8_dn, (LDAPMod**)mods);
+       ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
        SAFE_FREE(utf8_dn);
        return ADS_ERROR(ret);
 }
@@ -1269,12 +1546,13 @@ ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
 {
        int ret;
        char *utf8_dn = NULL;
-       if (push_utf8_allocate(&utf8_dn, del_dn) == -1) {
+       size_t converted_size;
+       if (!push_utf8_allocate(&utf8_dn, del_dn, &converted_size)) {
                DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
                return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
        }
        
-       ret = ldap_delete_s(ads->ld, utf8_dn);
+       ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
        SAFE_FREE(utf8_dn);
        return ADS_ERROR(ret);
 }
@@ -1420,13 +1698,13 @@ ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
 }
 
 /**
- * Determines the computer account's current KVNO via an LDAP lookup
+ * Determines the an account's current KVNO via an LDAP lookup
  * @param ads An initialized ADS_STRUCT
- * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
- * @return the kvno for the computer account, or -1 in case of a failure.
+ * @param account_name the NT samaccountname.
+ * @return the kvno for the account, or -1 in case of a failure.
  **/
 
-uint32 ads_get_kvno(ADS_STRUCT *ads, const char *machine_name)
+uint32 ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
 {
        LDAPMessage *res = NULL;
        uint32 kvno = (uint32)-1;      /* -1 indicates a failure */
@@ -1435,14 +1713,14 @@ uint32 ads_get_kvno(ADS_STRUCT *ads, const char *machine_name)
        char *dn_string = NULL;
        ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
 
-       DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name));
-       if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
+       DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
+       if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
                return kvno;
        }
        ret = ads_search(ads, &res, filter, attrs);
        SAFE_FREE(filter);
-       if (!ADS_ERR_OK(ret) && ads_count_replies(ads, res)) {
-               DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name));
+       if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
+               DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
                ads_msgfree(ads, res);
                return kvno;
        }
@@ -1477,6 +1755,28 @@ uint32 ads_get_kvno(ADS_STRUCT *ads, const char *machine_name)
        return kvno;
 }
 
+/**
+ * Determines the computer account's current KVNO via an LDAP lookup
+ * @param ads An initialized ADS_STRUCT
+ * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
+ * @return the kvno for the computer account, or -1 in case of a failure.
+ **/
+
+uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
+{
+       char *computer_account = NULL;
+       uint32_t kvno = -1;
+
+       if (asprintf(&computer_account, "%s$", machine_name) < 0) {
+               return kvno;
+       }
+
+       kvno = ads_get_kvno(ads, computer_account);
+       free(computer_account);
+
+       return kvno;
+}
+
 /**
  * This clears out all registered spn's for a given hostname
  * @param ads An initilaized ADS_STRUCT
@@ -1714,7 +2014,7 @@ done:
 **/
 
 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name, 
-                                 const char *org_unit, BOOL *moved)
+                                 const char *org_unit, bool *moved)
 {
        ADS_STATUS rc;
        int ldap_status;
@@ -1723,7 +2023,7 @@ ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
        char *computer_dn = NULL;
        char *parent_dn;
        char *computer_rdn = NULL;
-       BOOL need_move = False;
+       bool need_move = False;
 
        if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
                rc = ADS_ERROR(LDAP_NO_MEMORY);
@@ -1754,7 +2054,7 @@ ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
                goto done;
        }
 
-       ldap_status = ldap_rename_s(ads->ld, computer_dn, computer_rdn, 
+       ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn, 
                                    org_unit, 1, NULL, NULL);
        rc = ADS_ERROR(ldap_status);
 
@@ -1778,7 +2078,7 @@ done:
 /*
   dump a binary result from ldap
 */
-static void dump_binary(const char *field, struct berval **values)
+static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
 {
        int i, j;
        for (i=0; values[i]; i++) {
@@ -1790,58 +2090,57 @@ static void dump_binary(const char *field, struct berval **values)
        }
 }
 
-static void dump_guid(const char *field, struct berval **values)
+static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
 {
        int i;
-       UUID_FLAT guid;
        for (i=0; values[i]; i++) {
+
+               UUID_FLAT guid;
+               struct GUID tmp;
+
                memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
-               printf("%s: %s\n", field, 
-                      smb_uuid_string_static(smb_uuid_unpack_static(guid)));
+               smb_uuid_unpack(guid, &tmp);
+               printf("%s: %s\n", field, GUID_string(talloc_tos(), &tmp));
        }
 }
 
 /*
   dump a sid result from ldap
 */
-static void dump_sid(const char *field, struct berval **values)
+static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
 {
        int i;
        for (i=0; values[i]; i++) {
                DOM_SID sid;
+               fstring tmp;
                sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
-               printf("%s: %s\n", field, sid_string_static(&sid));
+               printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
        }
 }
 
 /*
   dump ntSecurityDescriptor
 */
-static void dump_sd(const char *filed, struct berval **values)
+static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
 {
-       prs_struct ps;
-       
-       SEC_DESC   *psd = 0;
-       TALLOC_CTX *ctx = 0;
+       TALLOC_CTX *frame = talloc_stackframe();
+       struct security_descriptor *psd;
+       NTSTATUS status;
 
-       if (!(ctx = talloc_init("sec_io_desc")))
+       status = unmarshall_sec_desc(talloc_tos(), (uint8 *)values[0]->bv_val,
+                                    values[0]->bv_len, &psd);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
+                         nt_errstr(status)));
+               TALLOC_FREE(frame);
                return;
+       }
 
-       /* prepare data */
-       prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
-       prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
-       prs_set_offset(&ps,0);
-
-       /* parse secdesc */
-       if (!sec_io_desc("sd", &psd, &ps, 1)) {
-               prs_mem_free(&ps);
-               talloc_destroy(ctx);
-               return;
+       if (psd) {
+               ads_disp_sd(ads, talloc_tos(), psd);
        }
-       if (psd) ads_disp_sd(psd);
 
-       prs_mem_free(&ps);
-       talloc_destroy(ctx);
+       TALLOC_FREE(frame);
 }
 
 /*
@@ -1860,12 +2159,12 @@ static void dump_string(const char *field, char **values)
   used for debugging
 */
 
-static BOOL ads_dump_field(char *field, void **values, void *data_area)
+static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
 {
        const struct {
                const char *name;
-               BOOL string;
-               void (*handler)(const char *, struct berval **);
+               bool string;
+               void (*handler)(ADS_STRUCT *, const char *, struct berval **);
        } handlers[] = {
                {"objectGUID", False, dump_guid},
                {"netbootGUID", False, dump_guid},
@@ -1876,6 +2175,7 @@ static BOOL ads_dump_field(char *field, void **values, void *data_area)
                {"tokenGroupsNoGCAcceptable", False, dump_sid},
                {"tokengroupsGlobalandUniversal", False, dump_sid},
                {"mS-DS-CreatorSID", False, dump_sid},
+               {"msExchMailboxGuid", False, dump_guid},
                {NULL, True, NULL}
        };
        int i;
@@ -1889,7 +2189,7 @@ static BOOL ads_dump_field(char *field, void **values, void *data_area)
                if (StrCaseCmp(handlers[i].name, field) == 0) {
                        if (!values) /* first time, indicate string or not */
                                return handlers[i].string;
-                       handlers[i].handler(field, (struct berval **) values);
+                       handlers[i].handler(ads, field, (struct berval **) values);
                        break;
                }
        }
@@ -1925,11 +2225,12 @@ static BOOL ads_dump_field(char *field, void **values, void *data_area)
  * @param data_area user-defined area to pass to function
  **/
  void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
-                         BOOL(*fn)(char *, void **, void *),
+                         bool (*fn)(ADS_STRUCT *, char *, void **, void *),
                          void *data_area)
 {
        LDAPMessage *msg;
        TALLOC_CTX *ctx;
+       size_t converted_size;
 
        if (!(ctx = talloc_init("ads_process_results")))
                return;
@@ -1939,30 +2240,37 @@ static BOOL ads_dump_field(char *field, void **values, void *data_area)
                char *utf8_field;
                BerElement *b;
        
-               for (utf8_field=ldap_first_attribute(ads->ld,
+               for (utf8_field=ldap_first_attribute(ads->ldap.ld,
                                                     (LDAPMessage *)msg,&b); 
                     utf8_field;
-                    utf8_field=ldap_next_attribute(ads->ld,
+                    utf8_field=ldap_next_attribute(ads->ldap.ld,
                                                    (LDAPMessage *)msg,b)) {
                        struct berval **ber_vals;
                        char **str_vals, **utf8_vals;
                        char *field;
-                       BOOL string; 
+                       bool string; 
+
+                       if (!pull_utf8_talloc(ctx, &field, utf8_field,
+                                             &converted_size))
+                       {
+                               DEBUG(0,("ads_process_results: "
+                                        "pull_utf8_talloc failed: %s",
+                                        strerror(errno)));
+                       }
 
-                       pull_utf8_talloc(ctx, &field, utf8_field);
-                       string = fn(field, NULL, data_area);
+                       string = fn(ads, field, NULL, data_area);
 
                        if (string) {
-                               utf8_vals = ldap_get_values(ads->ld,
+                               utf8_vals = ldap_get_values(ads->ldap.ld,
                                                 (LDAPMessage *)msg, field);
                                str_vals = ads_pull_strvals(ctx, 
                                                  (const char **) utf8_vals);
-                               fn(field, (void **) str_vals, data_area);
+                               fn(ads, field, (void **) str_vals, data_area);
                                ldap_value_free(utf8_vals);
                        } else {
-                               ber_vals = ldap_get_values_len(ads->ld, 
+                               ber_vals = ldap_get_values_len(ads->ldap.ld
                                                 (LDAPMessage *)msg, field);
-                               fn(field, (void **) ber_vals, data_area);
+                               fn(ads, field, (void **) ber_vals, data_area);
 
                                ldap_value_free_len(ber_vals);
                        }
@@ -1970,7 +2278,7 @@ static BOOL ads_dump_field(char *field, void **values, void *data_area)
                }
                ber_free(b, 0);
                talloc_free_children(ctx);
-               fn(NULL, NULL, data_area); /* completed an entry */
+               fn(ads, NULL, NULL, data_area); /* completed an entry */
 
        }
        talloc_destroy(ctx);
@@ -1984,7 +2292,7 @@ static BOOL ads_dump_field(char *field, void **values, void *data_area)
  **/
 int ads_count_replies(ADS_STRUCT *ads, void *res)
 {
-       return ldap_count_entries(ads->ld, (LDAPMessage *)res);
+       return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
 }
 
 /**
@@ -1995,7 +2303,7 @@ int ads_count_replies(ADS_STRUCT *ads, void *res)
  **/
  LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
 {
-       return ldap_first_entry(ads->ld, res);
+       return ldap_first_entry(ads->ldap.ld, res);
 }
 
 /**
@@ -2006,7 +2314,29 @@ int ads_count_replies(ADS_STRUCT *ads, void *res)
  **/
  LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
 {
-       return ldap_next_entry(ads->ld, res);
+       return ldap_next_entry(ads->ldap.ld, res);
+}
+
+/**
+ * pull the first message from a ADS result
+ * @param ads connection to ads server
+ * @param res Results of search
+ * @return first message from result
+ **/
+ LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
+{
+       return ldap_first_message(ads->ldap.ld, res);
+}
+
+/**
+ * pull the next message from a ADS result
+ * @param ads connection to ads server
+ * @param res Results of search
+ * @return next message from result
+ **/
+ LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
+{
+       return ldap_next_message(ads->ldap.ld, res);
 }
 
 /**
@@ -2023,18 +2353,16 @@ int ads_count_replies(ADS_STRUCT *ads, void *res)
        char **values;
        char *ret = NULL;
        char *ux_string;
-       size_t rc;
+       size_t converted_size;
 
-       values = ldap_get_values(ads->ld, msg, field);
+       values = ldap_get_values(ads->ldap.ld, msg, field);
        if (!values)
                return NULL;
        
-       if (values[0]) {
-               rc = pull_utf8_talloc(mem_ctx, &ux_string, 
-                                     values[0]);
-               if (rc != (size_t)-1)
-                       ret = ux_string;
-               
+       if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
+                                         &converted_size))
+       {
+               ret = ux_string;
        }
        ldap_value_free(values);
        return ret;
@@ -2055,8 +2383,9 @@ int ads_count_replies(ADS_STRUCT *ads, void *res)
        char **values;
        char **ret = NULL;
        int i;
+       size_t converted_size;
 
-       values = ldap_get_values(ads->ld, msg, field);
+       values = ldap_get_values(ads->ldap.ld, msg, field);
        if (!values)
                return NULL;
 
@@ -2069,7 +2398,9 @@ int ads_count_replies(ADS_STRUCT *ads, void *res)
        }
 
        for (i=0;i<*num_values;i++) {
-               if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
+               if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
+                                     &converted_size))
+               {
                        ldap_value_free(values);
                        return NULL;
                }
@@ -2099,7 +2430,7 @@ int ads_count_replies(ADS_STRUCT *ads, void *res)
                               char **current_strings,
                               const char **next_attribute,
                               size_t *num_strings,
-                              BOOL *more_strings)
+                              bool *more_strings)
 {
        char *attr;
        char *expected_range_attrib, *range_attr;
@@ -2119,9 +2450,9 @@ int ads_count_replies(ADS_STRUCT *ads, void *res)
        expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
 
        /* look for Range result */
-       for (attr = ldap_first_attribute(ads->ld, (LDAPMessage *)msg, &ptr); 
+       for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr); 
             attr; 
-            attr = ldap_next_attribute(ads->ld, (LDAPMessage *)msg, ptr)) {
+            attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
                /* we ignore the fact that this is utf8, as all attributes are ascii... */
                if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
                        range_attr = attr;
@@ -2216,12 +2547,12 @@ int ads_count_replies(ADS_STRUCT *ads, void *res)
  * @param v Pointer to int to store result
  * @return boolean inidicating success
 */
BOOL ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
                      uint32 *v)
 {
        char **values;
 
-       values = ldap_get_values(ads->ld, msg, field);
+       values = ldap_get_values(ads->ldap.ld, msg, field);
        if (!values)
                return False;
        if (!values[0]) {
@@ -2241,12 +2572,12 @@ int ads_count_replies(ADS_STRUCT *ads, void *res)
  * @param guid 37-byte area to receive text guid
  * @return boolean indicating success
  **/
BOOL ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
 {
        char **values;
        UUID_FLAT flat_guid;
 
-       values = ldap_get_values(ads->ld, msg, "objectGUID");
+       values = ldap_get_values(ads->ldap.ld, msg, "objectGUID");
        if (!values)
                return False;
        
@@ -2270,13 +2601,13 @@ int ads_count_replies(ADS_STRUCT *ads, void *res)
  * @param sid Pointer to sid to store result
  * @return boolean inidicating success
 */
BOOL ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
                   DOM_SID *sid)
 {
        struct berval **values;
-       BOOL ret = False;
+       bool ret = False;
 
-       values = ldap_get_values_len(ads->ld, msg, field);
+       values = ldap_get_values_len(ads->ldap.ld, msg, field);
 
        if (!values)
                return False;
@@ -2301,10 +2632,10 @@ int ads_count_replies(ADS_STRUCT *ads, void *res)
                   LDAPMessage *msg, const char *field, DOM_SID **sids)
 {
        struct berval **values;
-       BOOL ret;
+       bool ret;
        int count, i;
 
-       values = ldap_get_values_len(ads->ld, msg, field);
+       values = ldap_get_values_len(ads->ldap.ld, msg, field);
 
        if (!values)
                return 0;
@@ -2326,8 +2657,8 @@ int ads_count_replies(ADS_STRUCT *ads, void *res)
        for (i=0; values[i]; i++) {
                ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
                if (ret) {
-                       fstring sid;
-                       DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid, &(*sids)[count])));
+                       DEBUG(10, ("pulling SID: %s\n",
+                                  sid_string_dbg(&(*sids)[count])));
                        count++;
                }
        }
@@ -2345,24 +2676,26 @@ int ads_count_replies(ADS_STRUCT *ads, void *res)
  * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
  * @return boolean inidicating success
 */
BOOL ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
                  LDAPMessage *msg, const char *field, SEC_DESC **sd)
 {
        struct berval **values;
-       BOOL ret = False;
+       bool ret = true;
 
-       values = ldap_get_values_len(ads->ld, msg, field);
+       values = ldap_get_values_len(ads->ldap.ld, msg, field);
 
-       if (!values) return False;
+       if (!values) return false;
 
        if (values[0]) {
-               prs_struct ps;
-               prs_init(&ps, values[0]->bv_len, mem_ctx, UNMARSHALL);
-               prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
-               prs_set_offset(&ps,0);
-
-               ret = sec_io_desc("sd", sd, &ps, 1);
-               prs_mem_free(&ps);
+               NTSTATUS status;
+               status = unmarshall_sec_desc(mem_ctx,
+                                            (uint8 *)values[0]->bv_val,
+                                            values[0]->bv_len, sd);
+               if (!NT_STATUS_IS_OK(status)) {
+                       DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
+                                 nt_errstr(status)));
+                       ret = false;
+               }
        }
        
        ldap_value_free_len(values);
@@ -2468,7 +2801,7 @@ ADS_STATUS ads_current_time(ADS_STRUCT *ads)
 
         /* establish a new ldap tcp session if necessary */
 
-       if ( !ads->ld ) {
+       if ( !ads->ldap.ld ) {
                if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup, 
                        ads->server.ldap_server )) == NULL )
                {
@@ -2529,10 +2862,11 @@ ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
 
         /* establish a new ldap tcp session if necessary */
 
-       if ( !ads->ld ) {
+       if ( !ads->ldap.ld ) {
                if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup, 
                        ads->server.ldap_server )) == NULL )
                {
+                       status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
                        goto done;
                }
                ads_s->auth.flags = ADS_AUTH_ANON_BIND;
@@ -2649,8 +2983,8 @@ ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const c
 {
        ADS_STATUS status;
        LDAPMessage *res;
-       const char *parent, *config_context, *filter;
-       const char *attrs[] = { "configurationNamingContext", NULL };
+       const char *parent, *filter;
+       char *config_context = NULL;
        char *dn;
 
        /* shortcut a query */
@@ -2658,26 +2992,18 @@ ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const c
                return ads_site_dn(ads, mem_ctx, site_dn);
        }
 
-       status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
+       status = ads_config_path(ads, mem_ctx, &config_context);
        if (!ADS_ERR_OK(status)) {
                return status;
        }
 
-       config_context = ads_pull_string(ads, mem_ctx, res, "configurationNamingContext");
-       if (config_context == NULL) {
-               ads_msgfree(ads, res);
-               return ADS_ERROR(LDAP_NO_MEMORY);
-       }
-
        filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
        if (filter == NULL) {
-               ads_msgfree(ads, res);
                return ADS_ERROR(LDAP_NO_MEMORY);
        }
 
-       ads_msgfree(ads, res);
-
-       status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE, filter, NULL, &res);
+       status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE, 
+                              filter, NULL, &res);
        if (!ADS_ERR_OK(status)) {
                return status;
        }
@@ -2705,7 +3031,7 @@ ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const c
        if (*site_dn == NULL) {
                ads_msgfree(ads, res);
                ads_memfree(ads, dn);
-               ADS_ERROR(LDAP_NO_MEMORY);
+               return ADS_ERROR(LDAP_NO_MEMORY);
        }
 
        ads_memfree(ads, dn);
@@ -2726,34 +3052,27 @@ ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffix
 {
        ADS_STATUS status;
        LDAPMessage *res;
-       const char *config_context, *base;
-       const char *attrs[] = { "configurationNamingContext", NULL };
-       const char *attrs2[] = { "uPNSuffixes", NULL };
+       const char *base;
+       char *config_context = NULL;
+       const char *attrs[] = { "uPNSuffixes", NULL };
 
-       status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
+       status = ads_config_path(ads, mem_ctx, &config_context);
        if (!ADS_ERR_OK(status)) {
                return status;
        }
 
-       config_context = ads_pull_string(ads, mem_ctx, res, "configurationNamingContext");
-       if (config_context == NULL) {
-               ads_msgfree(ads, res);
-               return ADS_ERROR(LDAP_NO_MEMORY);
-       }
-
-       ads_msgfree(ads, res);
-
        base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
        if (base == NULL) {
                return ADS_ERROR(LDAP_NO_MEMORY);
        }
 
-       status = ads_search_dn(ads, &res, base, attrs2); 
+       status = ads_search_dn(ads, &res, base, attrs);
        if (!ADS_ERR_OK(status)) {
                return status;
        }
 
        if (ads_count_replies(ads, res) != 1) {
+               ads_msgfree(ads, res);
                return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
        }
 
@@ -2768,85 +3087,151 @@ ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffix
        return status;
 }
 
+/**
+ * get the joinable ous for a domain
+ * @param ads connection to ads server
+ * @param mem_ctx Pointer to talloc context
+ * @param ous Pointer to an array of ous
+ * @param num_ous Pointer to the number of ous
+ * @return status of search
+ **/
+ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
+                               TALLOC_CTX *mem_ctx,
+                               char ***ous,
+                               size_t *num_ous)
+{
+       ADS_STATUS status;
+       LDAPMessage *res = NULL;
+       LDAPMessage *msg = NULL;
+       const char *attrs[] = { "dn", NULL };
+       int count = 0;
+
+       status = ads_search(ads, &res,
+                           "(|(objectClass=domain)(objectclass=organizationalUnit))",
+                           attrs);
+       if (!ADS_ERR_OK(status)) {
+               return status;
+       }
+
+       count = ads_count_replies(ads, res);
+       if (count < 1) {
+               ads_msgfree(ads, res);
+               return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
+       }
+
+       for (msg = ads_first_entry(ads, res); msg;
+            msg = ads_next_entry(ads, msg)) {
+
+               char *dn = NULL;
+
+               dn = ads_get_dn(ads, msg);
+               if (!dn) {
+                       ads_msgfree(ads, res);
+                       return ADS_ERROR(LDAP_NO_MEMORY);
+               }
+
+               if (!add_string_to_array(mem_ctx, dn,
+                                        (const char ***)ous,
+                                        (int *)num_ous)) {
+                       ads_memfree(ads, dn);
+                       ads_msgfree(ads, res);
+                       return ADS_ERROR(LDAP_NO_MEMORY);
+               }
+
+               ads_memfree(ads, dn);
+       }
+
+       ads_msgfree(ads, res);
+
+       return status;
+}
+
+
 /**
  * pull a DOM_SID from an extended dn string
- * @param mem_ctx TALLOC_CTX 
+ * @param mem_ctx TALLOC_CTX
  * @param extended_dn string
  * @param flags string type of extended_dn
  * @param sid pointer to a DOM_SID
- * @return boolean inidicating success
+ * @return NT_STATUS_OK on success,
+ *        NT_INVALID_PARAMETER on error,
+ *        NT_STATUS_NOT_FOUND if no SID present
  **/
-BOOL ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx, 
-                                 const char *extended_dn, 
-                                 enum ads_extended_dn_flags flags, 
-                                 DOM_SID *sid)
+ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
+                                       const char *extended_dn,
+                                       enum ads_extended_dn_flags flags,
+                                       DOM_SID *sid)
 {
        char *p, *q, *dn;
 
        if (!extended_dn) {
-               return False;
+               return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
        }
 
        /* otherwise extended_dn gets stripped off */
        if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
-               return False;
+               return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
        }
-       /* 
+       /*
         * ADS_EXTENDED_DN_HEX_STRING:
         * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
         *
         * ADS_EXTENDED_DN_STRING (only with w2k3):
-       <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
+        * <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
+        *
+        * Object with no SID, such as an Exchange Public Folder
+        * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
         */
 
        p = strchr(dn, ';');
        if (!p) {
-               return False;
+               return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
        }
 
        if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
-               return False;
+               DEBUG(5,("No SID present in extended dn\n"));
+               return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
        }
 
        p += strlen(";<SID=");
 
        q = strchr(p, '>');
        if (!q) {
-               return False;
+               return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
        }
-       
+
        *q = '\0';
 
        DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
 
        switch (flags) {
-       
+
        case ADS_EXTENDED_DN_STRING:
                if (!string_to_sid(sid, p)) {
-                       return False;
+                       return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
                }
                break;
        case ADS_EXTENDED_DN_HEX_STRING: {
-               pstring buf;
+               fstring buf;
                size_t buf_len;
 
-               buf_len = strhex_to_str(buf, strlen(p), p);
+               buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
                if (buf_len == 0) {
-                       return False;
+                       return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
                }
 
                if (!sid_parse(buf, buf_len, sid)) {
                        DEBUG(10,("failed to parse sid\n"));
-                       return False;
+                       return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
                }
                break;
                }
        default:
                DEBUG(10,("unknown extended dn format\n"));
-               return False;
+               return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
        }
 
-       return True;
+       return ADS_ERROR_NT(NT_STATUS_OK);
 }
 
 /**
@@ -2859,18 +3244,19 @@ BOOL ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
  * @param sids pointer to sid array to allocate
  * @return the count of SIDs pulled
  **/
- int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads, 
-                                  TALLOC_CTX *mem_ctx, 
-                                  LDAPMessage *msg, 
+ int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads,
+                                  TALLOC_CTX *mem_ctx,
+                                  LDAPMessage *msg,
                                   const char *field,
                                   enum ads_extended_dn_flags flags,
                                   DOM_SID **sids)
 {
        int i;
-       size_t dn_count;
+       ADS_STATUS rc;
+       size_t dn_count, ret_count = 0;
        char **dn_strings;
 
-       if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field, 
+       if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field,
                                           &dn_count)) == NULL) {
                return 0;
        }
@@ -2882,18 +3268,25 @@ BOOL ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
        }
 
        for (i=0; i<dn_count; i++) {
-
-               if (!ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i], 
-                                                 flags, &(*sids)[i])) {
-                       TALLOC_FREE(*sids);
-                       TALLOC_FREE(dn_strings);
-                       return 0;
+               rc = ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i],
+                                                 flags, &(*sids)[i]);
+               if (!ADS_ERR_OK(rc)) {
+                       if (NT_STATUS_EQUAL(ads_ntstatus(rc),
+                           NT_STATUS_NOT_FOUND)) {
+                               continue;
+                       }
+                       else {
+                               TALLOC_FREE(*sids);
+                               TALLOC_FREE(dn_strings);
+                               return 0;
+                       }
                }
+               ret_count++;
        }
 
        TALLOC_FREE(dn_strings);
 
-       return dn_count;
+       return ret_count;
 }
 
 /********************************************************************
@@ -2937,26 +3330,26 @@ char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
        ADS_STATUS status;
        int count = 0;
        char *name = NULL;
-       
-       status = ads_find_machine_acct(ads, &res, global_myname());
+
+       status = ads_find_machine_acct(ads, &res, machine_name);
        if (!ADS_ERR_OK(status)) {
                DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
                        global_myname()));
                goto out;
        }
-               
+
        if ( (count = ads_count_replies(ads, res)) != 1 ) {
                DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
                goto out;
        }
-               
+
        if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
                DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
        }
 
 out:
        ads_msgfree(ads, res);
-       
+
        return name;
 }
 
@@ -3087,7 +3480,7 @@ ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
 
        hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
 
-       rc = ldap_delete_ext_s(ads->ld, hostnameDN, pldap_control, NULL);
+       rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
        if (rc) {
                DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
        }else {
@@ -3272,4 +3665,213 @@ ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
        return ADS_ERROR_LDAP(LDAP_SUCCESS);
 }
 
+/**
+ * Find a sAMAccoutName in LDAP
+ * @param ads connection to ads server
+ * @param mem_ctx TALLOC_CTX for allocating sid array
+ * @param samaccountname to search
+ * @param uac_ret uint32 pointer userAccountControl attribute value
+ * @param dn_ret pointer to dn
+ * @return status of token query
+ **/
+ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
+                              TALLOC_CTX *mem_ctx,
+                              const char *samaccountname,
+                              uint32 *uac_ret,
+                              const char **dn_ret)
+{
+       ADS_STATUS status;
+       const char *attrs[] = { "userAccountControl", NULL };
+       const char *filter;
+       LDAPMessage *res = NULL;
+       char *dn = NULL;
+       uint32 uac = 0;
+
+       filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
+               samaccountname);
+       if (filter == NULL) {
+               status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+               goto out;
+       }
+
+       status = ads_do_search_all(ads, ads->config.bind_path,
+                                  LDAP_SCOPE_SUBTREE,
+                                  filter, attrs, &res);
+       
+       if (!ADS_ERR_OK(status)) {
+               goto out;
+       }
+
+       if (ads_count_replies(ads, res) != 1) {
+               status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
+               goto out;
+       }
+
+       dn = ads_get_dn(ads, res);
+       if (dn == NULL) {
+               status = ADS_ERROR(LDAP_NO_MEMORY);
+               goto out;
+       }
+
+       if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
+               status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
+               goto out;
+       }
+
+       if (uac_ret) {
+               *uac_ret = uac;
+       }
+
+       if (dn_ret) {
+               *dn_ret = talloc_strdup(mem_ctx, dn);
+               if (!*dn_ret) {
+                       status = ADS_ERROR(LDAP_NO_MEMORY);
+                       goto out;
+               }
+       }
+ out:
+       ads_memfree(ads, dn);
+       ads_msgfree(ads, res);
+
+       return status;
+}
+
+/**
+ * find our configuration path 
+ * @param ads connection to ads server
+ * @param mem_ctx Pointer to talloc context
+ * @param config_path Pointer to the config path
+ * @return status of search
+ **/
+ADS_STATUS ads_config_path(ADS_STRUCT *ads, 
+                          TALLOC_CTX *mem_ctx, 
+                          char **config_path)
+{
+       ADS_STATUS status;
+       LDAPMessage *res = NULL;
+       const char *config_context = NULL;
+       const char *attrs[] = { "configurationNamingContext", NULL };
+
+       status = ads_do_search(ads, "", LDAP_SCOPE_BASE, 
+                              "(objectclass=*)", attrs, &res);
+       if (!ADS_ERR_OK(status)) {
+               return status;
+       }
+
+       config_context = ads_pull_string(ads, mem_ctx, res, 
+                                        "configurationNamingContext");
+       ads_msgfree(ads, res);
+       if (!config_context) {
+               return ADS_ERROR(LDAP_NO_MEMORY);
+       }
+
+       if (config_path) {
+               *config_path = talloc_strdup(mem_ctx, config_context);
+               if (!*config_path) {
+                       return ADS_ERROR(LDAP_NO_MEMORY);
+               }
+       }
+
+       return ADS_ERROR(LDAP_SUCCESS);
+}
+
+/**
+ * find the displayName of an extended right 
+ * @param ads connection to ads server
+ * @param config_path The config path
+ * @param mem_ctx Pointer to talloc context
+ * @param GUID struct of the rightsGUID
+ * @return status of search
+ **/
+const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads, 
+                                               const char *config_path, 
+                                               TALLOC_CTX *mem_ctx, 
+                                               const struct GUID *rights_guid)
+{
+       ADS_STATUS rc;
+       LDAPMessage *res = NULL;
+       char *expr = NULL;
+       const char *attrs[] = { "displayName", NULL };
+       const char *result = NULL;
+       const char *path;
+
+       if (!ads || !mem_ctx || !rights_guid) {
+               goto done;
+       }
+
+       expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)", 
+                              GUID_string(mem_ctx, rights_guid));
+       if (!expr) {
+               goto done;
+       }
+
+       path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
+       if (!path) {
+               goto done;
+       }
+
+       rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE, 
+                                expr, attrs, &res);
+       if (!ADS_ERR_OK(rc)) {
+               goto done;
+       }
+
+       if (ads_count_replies(ads, res) != 1) {
+               goto done;
+       }
+
+       result = ads_pull_string(ads, mem_ctx, res, "displayName");
+
+ done:
+       ads_msgfree(ads, res);
+       return result;
+       
+}
+
+/**
+ * verify or build and verify an account ou
+ * @param mem_ctx Pointer to talloc context
+ * @param ads connection to ads server
+ * @param account_ou
+ * @return status of search
+ **/
+
+ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
+                          ADS_STRUCT *ads,
+                          const char **account_ou)
+{
+       struct ldb_dn *name_dn = NULL;
+       const char *name = NULL;
+       char *ou_string = NULL;
+
+       name_dn = ldb_dn_explode(mem_ctx, *account_ou);
+       if (name_dn) {
+               return ADS_SUCCESS;
+       }
+
+       ou_string = ads_ou_string(ads, *account_ou);
+       if (!ou_string) {
+               return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
+       }
+
+       name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
+                              ads->config.bind_path);
+       SAFE_FREE(ou_string);
+       if (!name) {
+               return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
+       }
+
+       name_dn = ldb_dn_explode(mem_ctx, name);
+       if (!name_dn) {
+               return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
+       }
+
+       *account_ou = talloc_strdup(mem_ctx, name);
+       if (!*account_ou) {
+               return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
+       }
+
+       return ADS_SUCCESS;
+}
+
 #endif