Update my copyrights according to my agreement with IBM
[bbaumbach/samba-autobuild/.git] / source3 / libads / ldap.c
index 2cfbedc6d4ea9ec007f2dc3bf0fca8027772fdc8..5a12288b167308247d16f23397d5aee891f108d5 100644 (file)
@@ -3,7 +3,7 @@
    ads (active directory) utility library
    Copyright (C) Andrew Tridgell 2001
    Copyright (C) Remus Koos 2001
-   Copyright (C) Jim McDonough 2002
+   Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002
    
    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
@@ -22,7 +22,7 @@
 
 #include "includes.h"
 
-#ifdef HAVE_ADS
+#ifdef HAVE_LDAP
 
 /**
  * @file ldap.c
@@ -41,6 +41,9 @@
 /*
   try a connection to a given ldap server, returning True and setting the servers IP
   in the ads struct if successful
+  
+  TODO : add a negative connection cache in here leveraged off of the one
+  found in the rpc code.  --jerry
  */
 static BOOL ads_try_connect(ADS_STRUCT *ads, const char *server, unsigned port)
 {
@@ -63,138 +66,121 @@ static BOOL ads_try_connect(ADS_STRUCT *ads, const char *server, unsigned port)
        ads->ldap_port = port;
        ads->ldap_ip = *interpret_addr2(srv);
        free(srv);
+
        return True;
 }
 
-/* used by the IP comparison function */
-struct ldap_ip {
-       struct in_addr ip;
-       unsigned port;
-};
-
-/* compare 2 ldap IPs by nearness to our interfaces - used in qsort */
-static int ldap_ip_compare(struct ldap_ip *ip1, struct ldap_ip *ip2)
+/*
+  try a connection to a given ldap server, based on URL, returning True if successful
+ */
+static BOOL ads_try_connect_uri(ADS_STRUCT *ads)
 {
-       return ip_compare(&ip1->ip, &ip2->ip);
+#if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
+       DEBUG(5,("ads_try_connect: trying ldap server at URI '%s'\n", 
+                ads->server.ldap_uri));
+
+       
+       if (ldap_initialize((LDAP**)&(ads->ld), ads->server.ldap_uri) == LDAP_SUCCESS) {
+               return True;
+       }
+       DEBUG(0, ("ldap_initialize: %s\n", strerror(errno)));
+       
+#else 
+
+       DEBUG(1, ("no URL support in LDAP libs!\n"));
+#endif
+
+       return False;
 }
 
-/* try connecting to a ldap server via DNS */
-static BOOL ads_try_dns(ADS_STRUCT *ads)
+/**********************************************************************
+ Try to find an AD dc using our internal name resolution routines
+ Try the realm first and then then workgroup name if netbios is not 
+ disabled
+**********************************************************************/
+
+static BOOL ads_find_dc(ADS_STRUCT *ads)
 {
-       char *realm, *ptr;
-       char *list = NULL;
-       pstring tok;
-       struct ldap_ip *ip_list;
+       const char *c_realm;
        int count, i=0;
+       struct ip_service *ip_list;
+       pstring realm;
+       BOOL got_realm = False;
+       BOOL use_own_domain = False;
 
-       realm = ads->server.realm;
-       if (!realm || !*realm) {
-               realm = lp_realm();
-       }
-       if (!realm || !*realm) {
-               realm = ads->server.workgroup;
-       }
-       if (!realm || !*realm) {
-               realm = lp_workgroup();
-       }
-       if (!realm) {
-               return False;
-       }
-       realm = smb_xstrdup(realm);
-
-       DEBUG(6,("ads_try_dns: looking for realm '%s'\n", realm));
-       if (ldap_domain2hostlist(realm, &list) != LDAP_SUCCESS) {
-               SAFE_FREE(realm);
-               return False;
-       }
-
-       DEBUG(6,("ads_try_dns: ldap realm '%s' host list '%s'\n", realm, list));
-       SAFE_FREE(realm);
+       /* if the realm and workgroup are both empty, assume they are ours */
 
-       count = count_chars(list, ' ') + 1;
-       ip_list = malloc(count * sizeof(struct ldap_ip));
-       if (!ip_list) {
-               return False;
+       /* 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 ) {
+                       use_own_domain = True;
+                       c_realm = lp_realm();
+               }
        }
-
-       ptr = list;
-       while (next_token(&ptr, tok, " ", sizeof(tok))) {
-               unsigned port = LDAP_PORT;
-               char *p = strchr(tok, ':');
-               if (p) {
-                       *p = 0;
-                       port = atoi(p+1);
+       
+       if (c_realm && *c_realm) 
+               got_realm = True;
+                  
+again:
+       /* 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();
                }
-               ip_list[i].ip = *interpret_addr2(tok);
-               ip_list[i].port = port;
-               if (!is_zero_ip(ip_list[i].ip)) {
-                       i++;
+               
+               if ( !c_realm || !*c_realm ) {
+                       DEBUG(0,("ads_find_dc: no realm or workgroup!  Don't know what to do\n"));
+                       return False;
                }
        }
-       free(list);
+       
+       pstrcpy( realm, c_realm );
 
-       count = i;
+       DEBUG(6,("ads_find_dc: looking for %s '%s'\n", 
+               (got_realm ? "realm" : "domain"), realm));
 
-       /* we sort the list of addresses by closeness to our interfaces. This
-          tries to prevent us using a DC on the other side of the country */
-       if (count > 1) {
-               qsort(ip_list, count, sizeof(struct ldap_ip), 
-                     QSORT_CAST ldap_ip_compare);      
+       if ( !get_sorted_dc_list(realm, &ip_list, &count, got_realm) ) {
+               /* fall back to netbios if we can */
+               if ( got_realm && !lp_disable_netbios() ) {
+                       got_realm = False;
+                       goto again;
+               }
+               
+               return False;
        }
-
-       for (i=0;i<count;i++) {
-               if (ads_try_connect(ads, inet_ntoa(ip_list[i].ip), ip_list[i].port)) {
-                       free(ip_list);
+                       
+       /* if we fail this loop, then giveup since all the IP addresses returned were dead */
+       for ( i=0; i<count; i++ ) {
+               /* since this is an ads conection request, default to LDAP_PORT is not set */
+               int port = (ip_list[i].port!=PORT_NONE) ? ip_list[i].port : LDAP_PORT;
+               fstring server;
+               
+               fstrcpy( server, inet_ntoa(ip_list[i].ip) );
+               
+               if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
+                       continue;
+                       
+               if ( ads_try_connect(ads, server, port) ) {
+                       SAFE_FREE(ip_list);
                        return True;
                }
+               
+               /* keep track of failures */
+               add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
        }
 
        SAFE_FREE(ip_list);
+       
        return False;
 }
 
-/* try connecting to a ldap server via netbios */
-static BOOL ads_try_netbios(ADS_STRUCT *ads)
-{
-       struct in_addr *ip_list;
-       int count;
-       int i;
-       char *workgroup = ads->server.workgroup;
-
-       if (!workgroup) {
-               workgroup = lp_workgroup();
-       }
-
-       DEBUG(6,("ads_try_netbios: looking for workgroup '%s'\n", workgroup));
-
-       /* try the PDC first */
-       if (get_dc_list(True, workgroup, &ip_list, &count)) { 
-               for (i=0;i<count;i++) {
-                       DEBUG(6,("ads_try_netbios: trying server '%s'\n", 
-                                inet_ntoa(ip_list[i])));
-                       if (ads_try_connect(ads, inet_ntoa(ip_list[i]), LDAP_PORT)) {
-                               free(ip_list);
-                               return True;
-                       }
-               }
-               free(ip_list);
-       }
-
-       /* now any DC, including backups */
-       if (get_dc_list(False, workgroup, &ip_list, &count)) { 
-               for (i=0;i<count;i++) {
-                       DEBUG(6,("ads_try_netbios: trying server '%s'\n", 
-                                inet_ntoa(ip_list[i])));
-                       if (ads_try_connect(ads, inet_ntoa(ip_list[i]), LDAP_PORT)) {
-                               free(ip_list);
-                               return True;
-                       }
-               }
-               free(ip_list);
-       }
-
-       return False;
-}
 
 /**
  * Connect to the LDAP server
@@ -204,32 +190,25 @@ static BOOL ads_try_netbios(ADS_STRUCT *ads)
 ADS_STATUS ads_connect(ADS_STRUCT *ads)
 {
        int version = LDAP_VERSION3;
-       int code;
        ADS_STATUS status;
 
        ads->last_attempt = time(NULL);
        ads->ld = NULL;
 
-       /* try with a user specified server */
-       if (ads->server.ldap_server && 
-           ads_try_connect(ads, ads->server.ldap_server, LDAP_PORT)) {
-               goto got_connection;
-       }
+       /* try with a URL based server */
 
-       /* try with a smb.conf ads server setting if we are connecting
-           to the primary workgroup or realm */
-       if (!ads->server.foreign &&
-           ads_try_connect(ads, lp_ads_server(), LDAP_PORT)) {
+       if (ads->server.ldap_uri &&
+           ads_try_connect_uri(ads)) {
                goto got_connection;
        }
 
-       /* try via DNS */
-       if (ads_try_dns(ads)) {
+       /* try with a user specified server */
+       if (ads->server.ldap_server && 
+           ads_try_connect(ads, ads->server.ldap_server, LDAP_PORT)) {
                goto got_connection;
        }
 
-       /* try via netbios lookups */
-       if (!lp_disable_netbios() && ads_try_netbios(ads)) {
+       if (ads_find_dc(ads)) {
                goto got_connection;
        }
 
@@ -248,10 +227,9 @@ got_connection:
 
        if (!ads->auth.user_name) {
                /* by default use the machine account */
-               extern pstring global_myname;
                fstring myname;
-               fstrcpy(myname, global_myname);
-               strlower(myname);
+               fstrcpy(myname, global_myname());
+               strlower_m(myname);
                asprintf(&ads->auth.user_name, "HOST/%s", myname);
        }
 
@@ -274,10 +252,18 @@ got_connection:
        }
 #endif
 
-       if (ads->auth.no_bind) {
+       if (ads->auth.flags & ADS_AUTH_NO_BIND) {
                return ADS_SUCCESS;
        }
 
+       if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
+               return ADS_ERROR(ldap_simple_bind_s( ads->ld, NULL, NULL));
+       }
+
+       if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
+               return ADS_ERROR(ldap_simple_bind_s( ads->ld, ads->auth.user_name, ads->auth.password));
+       }
+
        return ads_sasl_bind(ads);
 }
 
@@ -291,6 +277,8 @@ static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
        if (!in_val) return NULL;
 
        value = talloc_zero(ctx, sizeof(struct berval));
+       if (value == NULL)
+               return NULL;
        if (in_val->bv_len == 0) return value;
 
        value->bv_len = in_val->bv_len;
@@ -363,8 +351,8 @@ static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
  *  again when the entire search is complete 
  * @param ads connection to ads server 
  * @param bind_path Base dn for the search
- * @param scope Scope of search (LDAP_BASE | LDAP_ONE | LDAP_SUBTREE)
- * @param exp Search expression - specified in local charset
+ * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
+ * @param expr Search expression - specified in local charset
  * @param attrs Attributes to retrieve - specified in utf8 or ascii
  * @param res ** which will contain results - free res* with ads_msgfree()
  * @param count Number of entries retrieved on this page
@@ -372,12 +360,12 @@ static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
  * @return status of search
  **/
 ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
-                              int scope, const char *exp,
+                              int scope, const char *expr,
                               const char **attrs, void **res, 
                               int *count, void **cookie)
 {
        int rc, i, version;
-       char *utf8_exp, *utf8_path, **search_attrs;
+       char *utf8_expr, *utf8_path, **search_attrs;
        LDAPControl PagedResults, NoReferrals, *controls[3], **rcontrols; 
        BerElement *cookie_be = NULL;
        struct berval *cookie_bv= NULL;
@@ -385,14 +373,14 @@ ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
 
        *res = NULL;
 
-       if (!(ctx = talloc_init()))
+       if (!(ctx = talloc_init("ads_do_paged_search")))
                return ADS_ERROR(LDAP_NO_MEMORY);
 
        /* 0 means the conversion worked but the result was empty 
-          so we only fail if it's negative.  In any case, it always 
+          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_exp, exp) < 0) ||
-           (push_utf8_talloc(ctx, &utf8_path, bind_path) < 0)) {
+       if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
+           (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
                rc = LDAP_NO_MEMORY;
                goto done;
        }
@@ -402,8 +390,7 @@ ADS_STATUS ads_do_paged_search(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 (!(str_list_copy(&search_attrs, attrs))) {
                        rc = LDAP_NO_MEMORY;
                        goto done;
                }
@@ -453,7 +440,7 @@ ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
        */
        ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
 
-       rc = ldap_search_ext_s(ads->ld, utf8_path, scope, utf8_exp, 
+       rc = ldap_search_ext_s(ads->ld, utf8_path, scope, utf8_expr
                               search_attrs, 0, controls,
                               NULL, NULL, LDAP_NO_LIMIT, (LDAPMessage **)res);
 
@@ -461,7 +448,7 @@ ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
        ber_bvfree(cookie_bv);
 
        if (rc) {
-               DEBUG(3,("ldap_search_ext_s(%s) -> %s\n", exp, ldap_err2string(rc)));
+               DEBUG(3,("ldap_search_ext_s(%s) -> %s\n", expr, ldap_err2string(rc)));
                goto done;
        }
 
@@ -504,21 +491,21 @@ done:
  * all entries in a large search.
  * @param ads connection to ads server 
  * @param bind_path Base dn for the search
- * @param scope Scope of search (LDAP_BASE | LDAP_ONE | LDAP_SUBTREE)
- * @param exp Search expression
+ * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
+ * @param expr Search expression
  * @param attrs Attributes to retrieve
  * @param res ** which will contain results - free res* with ads_msgfree()
  * @return status of search
  **/
 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
-                            int scope, const char *exp,
+                            int scope, const char *expr,
                             const char **attrs, void **res)
 {
        void *cookie = NULL;
        int count = 0;
        ADS_STATUS status;
 
-       status = ads_do_paged_search(ads, bind_path, scope, exp, attrs, res,
+       status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, res,
                                     &count, &cookie);
 
        if (!ADS_ERR_OK(status)) return status;
@@ -528,7 +515,7 @@ ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
                ADS_STATUS status2;
                LDAPMessage *msg, *next;
 
-               status2 = ads_do_paged_search(ads, bind_path, scope, exp, 
+               status2 = ads_do_paged_search(ads, bind_path, scope, expr
                                              attrs, &res2, &count, &cookie);
 
                if (!ADS_ERR_OK(status2)) break;
@@ -551,15 +538,15 @@ ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
  *  runs the function as each page is returned, using ads_process_results()
  * @param ads connection to ads server
  * @param bind_path Base dn for the search
- * @param scope Scope of search (LDAP_BASE | LDAP_ONE | LDAP_SUBTREE)
- * @param exp Search expression - specified in local charset
+ * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
+ * @param expr Search expression - specified in local charset
  * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
  * @param fn Function which takes attr name, values list, and data_area
  * @param data_area Pointer which is passed to function on each call
  * @return status of search
  **/
 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
-                               int scope, const char *exp, const char **attrs,
+                               int scope, const char *expr, const char **attrs,
                                BOOL(*fn)(char *, void **, void *), 
                                void *data_area)
 {
@@ -568,7 +555,7 @@ ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
        ADS_STATUS status;
        void *res;
 
-       status = ads_do_paged_search(ads, bind_path, scope, exp, attrs, &res,
+       status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
                                     &count, &cookie);
 
        if (!ADS_ERR_OK(status)) return status;
@@ -577,7 +564,7 @@ ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
        ads_msgfree(ads, res);
 
        while (cookie) {
-               status = ads_do_paged_search(ads, bind_path, scope, exp, attrs,
+               status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
                                             &res, &count, &cookie);
 
                if (!ADS_ERR_OK(status)) break;
@@ -593,29 +580,32 @@ ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
  * Do a search with a timeout.
  * @param ads connection to ads server
  * @param bind_path Base dn for the search
- * @param scope Scope of search (LDAP_BASE | LDAP_ONE | LDAP_SUBTREE)
- * @param exp Search expression
+ * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
+ * @param expr Search expression
  * @param attrs Attributes to retrieve
  * @param res ** which will contain results - free res* with ads_msgfree()
  * @return status of search
  **/
 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope, 
-                        const char *exp,
+                        const char *expr,
                         const char **attrs, void **res)
 {
        struct timeval timeout;
        int rc;
-       char *utf8_exp, *utf8_path, **search_attrs = NULL;
+       char *utf8_expr, *utf8_path, **search_attrs = NULL;
        TALLOC_CTX *ctx;
 
-       if (!(ctx = talloc_init()))
+       if (!(ctx = talloc_init("ads_do_search"))) {
+               DEBUG(1,("ads_do_search: talloc_init() failed!"));
                return ADS_ERROR(LDAP_NO_MEMORY);
+       }
 
        /* 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_exp, exp) < 0) ||
-           (push_utf8_talloc(ctx, &utf8_path, bind_path) < 0)) {
+       if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
+           (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
+               DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
                rc = LDAP_NO_MEMORY;
                goto done;
        }
@@ -627,6 +617,7 @@ ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
                /* if (!(search_attrs = ads_push_strvals(ctx, attrs)))  */
                if (!(str_list_copy(&search_attrs, attrs)))
                {
+                       DEBUG(1,("ads_do_search: str_list_copy() failed!"));
                        rc = LDAP_NO_MEMORY;
                        goto done;
                }
@@ -639,7 +630,7 @@ ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
        /* see the note in ads_do_paged_search - we *must* disable referrals */
        ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
 
-       rc = ldap_search_ext_s(ads->ld, utf8_path, scope, utf8_exp,
+       rc = ldap_search_ext_s(ads->ld, utf8_path, scope, utf8_expr,
                               search_attrs, 0, NULL, NULL, 
                               &timeout, LDAP_NO_LIMIT, (LDAPMessage **)res);
 
@@ -658,16 +649,16 @@ ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
  * Do a general ADS search
  * @param ads connection to ads server
  * @param res ** which will contain results - free res* with ads_msgfree()
- * @param exp Search expression
+ * @param expr Search expression
  * @param attrs Attributes to retrieve
  * @return status of search
  **/
 ADS_STATUS ads_search(ADS_STRUCT *ads, void **res, 
-                     const char *exp, 
+                     const char *expr
                      const char **attrs)
 {
        return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE, 
-                            exp, attrs, res);
+                            expr, attrs, res);
 }
 
 /**
@@ -709,14 +700,15 @@ void ads_memfree(ADS_STRUCT *ads, void *mem)
 /**
  * Get a dn from search results
  * @param ads connection to ads server
- * @param res Search results
+ * @param msg Search result
  * @return dn string
  **/
-char *ads_get_dn(ADS_STRUCT *ads, void *res)
+char *ads_get_dn(ADS_STRUCT *ads, void *msg)
 {
        char *utf8_dn, *unix_dn;
 
-       utf8_dn = ldap_get_dn(ads->ld, res);
+       utf8_dn = ldap_get_dn(ads->ld, msg);
+
        pull_utf8_allocate((void **) &unix_dn, utf8_dn);
        ldap_memfree(utf8_dn);
        return unix_dn;
@@ -732,14 +724,18 @@ char *ads_get_dn(ADS_STRUCT *ads, void *res)
 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, void **res, const char *host)
 {
        ADS_STATUS status;
-       char *exp;
+       char *expr;
        const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
 
        /* the easiest way to find a machine account anywhere in the tree
           is to look for hostname$ */
-       asprintf(&exp, "(samAccountName=%s$)", host);
-       status = ads_search(ads, res, exp, attrs);
-       free(exp);
+       if (asprintf(&expr, "(samAccountName=%s$)", host) == -1) {
+               DEBUG(1, ("asprintf failed!\n"));
+               return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+       }
+       
+       status = ads_search(ads, res, expr, attrs);
+       free(expr);
        return status;
 }
 
@@ -772,18 +768,18 @@ static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
 {
        int curmod;
        LDAPMod **modlist = (LDAPMod **) *mods;
-       void **values;
+       struct berval **ber_values = NULL;
+       char **char_values = NULL;
 
        if (!invals) {
-               values = NULL;
                mod_op = LDAP_MOD_DELETE;
        } else {
                if (mod_op & LDAP_MOD_BVALUES)
-                       values = (void **) ads_dup_values(ctx, 
-                                          (const struct berval **)invals);
+                       ber_values = ads_dup_values(ctx, 
+                                               (const struct berval **)invals);
                else
-                       values = (void **) ads_push_strvals(ctx, 
-                                                  (const char **) invals);
+                       char_values = ads_push_strvals(ctx, 
+                                                 (const char **) invals);
        }
 
        /* find the first empty slot */
@@ -802,10 +798,14 @@ static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
        if (!(modlist[curmod] = talloc_zero(ctx, sizeof(LDAPMod))))
                return ADS_ERROR(LDAP_NO_MEMORY);
        modlist[curmod]->mod_type = talloc_strdup(ctx, name);
-       if (mod_op & LDAP_MOD_BVALUES)
-               modlist[curmod]->mod_bvalues = (struct berval **) values;
-       else
-               modlist[curmod]->mod_values = (char **) values;
+       if (mod_op & LDAP_MOD_BVALUES) {
+               modlist[curmod]->mod_bvalues = ber_values;
+       } else if (mod_op & LDAP_MOD_DELETE) {
+               modlist[curmod]->mod_values = NULL;
+       } else {
+               modlist[curmod]->mod_values = char_values;
+       }
+
        modlist[curmod]->mod_op = mod_op;
        return ADS_ERROR(LDAP_SUCCESS);
 }
@@ -886,7 +886,7 @@ ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
           non-existent attribute (but allowable for the object) to run
        */
        LDAPControl PermitModify = {
-               "1.2.840.113556.1.4.1413",
+               ADS_PERMIT_MODIFY_OID,
                {0, NULL},
                (char) 1};
        LDAPControl *controls[2];
@@ -894,13 +894,15 @@ ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
        controls[0] = &PermitModify;
        controls[1] = NULL;
 
-       push_utf8_allocate((void **) &utf8_dn, mod_dn);
+       if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) {
+               return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+       }
 
        /* find the end of the list, marked by NULL or -1 */
        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 ? utf8_dn : mod_dn,
+       ret = ldap_modify_ext_s(ads->ld, utf8_dn,
                                (LDAPMod **) mods, controls, NULL);
        SAFE_FREE(utf8_dn);
        return ADS_ERROR(ret);
@@ -918,14 +920,17 @@ ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
        int ret, i;
        char *utf8_dn = NULL;
 
-       push_utf8_allocate((void **) &utf8_dn, new_dn);
+       if (push_utf8_allocate(&utf8_dn, new_dn) == -1) {
+               DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
+               return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+       }
        
        /* find the end of the list, marked by NULL or -1 */
        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_add_s(ads->ld, utf8_dn ? utf8_dn : new_dn, mods);
+       ret = ldap_add_s(ads->ld, utf8_dn, mods);
        SAFE_FREE(utf8_dn);
        return ADS_ERROR(ret);
 }
@@ -940,8 +945,12 @@ ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
 {
        int ret;
        char *utf8_dn = NULL;
-       push_utf8_allocate((void **) &utf8_dn, del_dn);
-       ret = ldap_delete(ads->ld, utf8_dn ? utf8_dn : del_dn);
+       if (push_utf8_allocate(&utf8_dn, del_dn) == -1) {
+               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);
        return ADS_ERROR(ret);
 }
 
@@ -967,17 +976,21 @@ char *ads_ou_string(const char *org_unit)
   add a machine account to the ADS server
 */
 static ADS_STATUS ads_add_machine_acct(ADS_STRUCT *ads, const char *hostname, 
+                                      uint32 account_type,
                                       const char *org_unit)
 {
-       ADS_STATUS ret;
+       ADS_STATUS ret, status;
        char *host_spn, *host_upn, *new_dn, *samAccountName, *controlstr;
        char *ou_str;
        TALLOC_CTX *ctx;
        ADS_MODLIST mods;
        const char *objectClass[] = {"top", "person", "organizationalPerson",
                                     "user", "computer", NULL};
+       const char *servicePrincipalName[5] = {NULL, NULL, NULL, NULL, NULL};
+       char *psp, *psp2;
+       unsigned acct_control;
 
-       if (!(ctx = talloc_init_named("machine_account")))
+       if (!(ctx = talloc_init("machine_account")))
                return ADS_ERROR(LDAP_NO_MEMORY);
 
        ret = ADS_ERROR(LDAP_NO_MEMORY);
@@ -987,17 +1000,38 @@ static ADS_STATUS ads_add_machine_acct(ADS_STRUCT *ads, const char *hostname,
        if (!(host_upn = talloc_asprintf(ctx, "%s@%s", host_spn, ads->config.realm)))
                goto done;
        ou_str = ads_ou_string(org_unit);
+       if (!ou_str) {
+               DEBUG(1, ("ads_ou_string returned NULL (malloc failure?)\n"));
+               goto done;
+       }
        new_dn = talloc_asprintf(ctx, "cn=%s,%s,%s", hostname, ou_str, 
                                 ads->config.bind_path);
+       servicePrincipalName[0] = talloc_asprintf(ctx, "HOST/%s", hostname);
+       psp = talloc_asprintf(ctx, "HOST/%s.%s", 
+                             hostname, 
+                             ads->config.realm);
+       strlower_m(&psp[5]);
+       servicePrincipalName[1] = psp;
+       servicePrincipalName[2] = talloc_asprintf(ctx, "CIFS/%s", hostname);
+       psp2 = talloc_asprintf(ctx, "CIFS/%s.%s", 
+                              hostname, 
+                              ads->config.realm);
+       strlower_m(&psp2[5]);
+       servicePrincipalName[3] = psp2;
+
        free(ou_str);
        if (!new_dn)
                goto done;
 
        if (!(samAccountName = talloc_asprintf(ctx, "%s$", hostname)))
                goto done;
-       if (!(controlstr = talloc_asprintf(ctx, "%u", 
-                  UF_DONT_EXPIRE_PASSWD | UF_WORKSTATION_TRUST_ACCOUNT | 
-                  UF_TRUSTED_FOR_DELEGATION | UF_USE_DES_KEY_ONLY)))
+
+       acct_control = account_type | UF_DONT_EXPIRE_PASSWD;
+#ifndef ENCTYPE_ARCFOUR_HMAC
+       acct_control |= UF_USE_DES_KEY_ONLY;
+#endif
+
+       if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control)))
                goto done;
 
        if (!(mods = ads_init_mods(ctx)))
@@ -1007,15 +1041,27 @@ static ADS_STATUS ads_add_machine_acct(ADS_STRUCT *ads, const char *hostname,
        ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
        ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
        ads_mod_str(ctx, &mods, "userPrincipalName", host_upn);
-       ads_mod_str(ctx, &mods, "servicePrincipalName", host_spn);
+       ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
        ads_mod_str(ctx, &mods, "dNSHostName", hostname);
        ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
        ads_mod_str(ctx, &mods, "operatingSystem", "Samba");
        ads_mod_str(ctx, &mods, "operatingSystemVersion", VERSION);
 
-       ads_gen_add(ads, new_dn, mods);
-       ret = ads_set_machine_sd(ads, hostname, new_dn);
+       ret = ads_gen_add(ads, new_dn, mods);
 
+       if (!ADS_ERR_OK(ret))
+               goto done;
+
+       /* Do not fail if we can't set security descriptor
+        * it shouldn't be mandatory and probably we just 
+        * don't have enough rights to do it.
+        */
+       status = ads_set_machine_sd(ads, hostname, new_dn);
+
+       if (!ADS_ERR_OK(status)) {
+               DEBUG(0, ("Warning: ads_set_machine_sd: %s\n",
+                               ads_errstr(status)));
+       }
 done:
        talloc_destroy(ctx);
        return ret;
@@ -1036,6 +1082,23 @@ static void dump_binary(const char *field, struct berval **values)
        }
 }
 
+struct uuid {
+        uint32   i1;
+        uint16   i2;
+        uint16   i3;
+        uint8    s[8];
+};
+
+static void dump_guid(const char *field, struct berval **values)
+{
+       int i;
+       GUID guid;
+       for (i=0; values[i]; i++) {
+               memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
+               printf("%s: %s\n", field, smb_uuid_string_static(guid));
+       }
+}
+
 /*
   dump a sid result from ldap
 */
@@ -1059,13 +1122,13 @@ static void dump_sd(const char *filed, struct berval **values)
        SEC_DESC   *psd = 0;
        TALLOC_CTX *ctx = 0;
 
-       if (!(ctx = talloc_init_named("sec_io_desc")))
+       if (!(ctx = talloc_init("sec_io_desc")))
                return;
 
        /* prepare data */
        prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
-       prs_append_data(&ps, values[0]->bv_val, values[0]->bv_len);
-       ps.data_offset = 0;
+       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)) {
@@ -1097,15 +1160,16 @@ static void dump_string(const char *field, char **values)
 
 static BOOL ads_dump_field(char *field, void **values, void *data_area)
 {
-       struct {
-               char *name;
+       const struct {
+               const char *name;
                BOOL string;
                void (*handler)(const char *, struct berval **);
        } handlers[] = {
-               {"objectGUID", False, dump_binary},
+               {"objectGUID", False, dump_guid},
                {"nTSecurityDescriptor", False, dump_sd},
                {"dnsRecord", False, dump_binary},
                {"objectSid", False, dump_sid},
+               {"tokenGroups", False, dump_sid},
                {NULL, True, NULL}
        };
        int i;
@@ -1161,7 +1225,7 @@ void ads_process_results(ADS_STRUCT *ads, void *res,
        void *msg;
        TALLOC_CTX *ctx;
 
-       if (!(ctx = talloc_init()))
+       if (!(ctx = talloc_init("ads_process_results")))
                return;
 
        for (msg = ads_first_entry(ads, res); msg; 
@@ -1225,7 +1289,8 @@ int ads_count_replies(ADS_STRUCT *ads, void *res)
  * @param org_unit Organizational unit to place machine in
  * @return status of join
  **/
-ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *hostname, const char *org_unit)
+ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *hostname, 
+                         uint32 account_type, const char *org_unit)
 {
        ADS_STATUS status;
        LDAPMessage *res;
@@ -1233,7 +1298,7 @@ ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *hostname, const char *org
 
        /* hostname must be lowercase */
        host = strdup(hostname);
-       strlower(host);
+       strlower_m(host);
 
        status = ads_find_machine_acct(ads, (void **)&res, host);
        if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
@@ -1246,7 +1311,7 @@ ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *hostname, const char *org
                }
        }
 
-       status = ads_add_machine_acct(ads, host, org_unit);
+       status = ads_add_machine_acct(ads, host, account_type, org_unit);
        if (!ADS_ERR_OK(status)) {
                DEBUG(0, ("ads_add_machine_acct: %s\n", ads_errstr(status)));
                return status;
@@ -1272,13 +1337,13 @@ ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *hostname, const char *org
 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
 {
        ADS_STATUS status;
-       void *res;
+       void *res, *msg;
        char *hostnameDN, *host; 
        int rc;
 
        /* hostname must be lowercase */
        host = strdup(hostname);
-       strlower(host);
+       strlower_m(host);
 
        status = ads_find_machine_acct(ads, &res, host);
        if (!ADS_ERR_OK(status)) {
@@ -1286,7 +1351,12 @@ ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
            return status;
        }
 
-       hostnameDN = ads_get_dn(ads, (LDAPMessage *)res);
+       msg = ads_first_entry(ads, res);
+       if (!msg) {
+               return ADS_ERROR_SYSTEM(ENOENT);
+       }
+
+       hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
        rc = ldap_delete_s(ads->ld, hostnameDN);
        ads_memfree(ads, hostnameDN);
        if (rc != LDAP_SUCCESS) {
@@ -1313,13 +1383,12 @@ ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
  **/
 ADS_STATUS ads_set_machine_sd(ADS_STRUCT *ads, const char *hostname, char *dn)
 {
-       const char     *attrs[] = {"ntSecurityDescriptor", "objectSid", 0};
-       char           *exp     = 0;
+       const char     *attrs[] = {"nTSecurityDescriptor", "objectSid", 0};
+       char           *expr     = 0;
        size_t          sd_size = 0;
-       struct berval **bvals   = 0;
        struct berval   bval = {0, NULL};
-       prs_struct      ps;
        prs_struct      ps_wire;
+       char           *escaped_hostname = escape_ldap_string_alloc(hostname);
 
        LDAPMessage *res  = 0;
        LDAPMessage *msg  = 0;
@@ -1328,96 +1397,98 @@ ADS_STATUS ads_set_machine_sd(ADS_STRUCT *ads, const char *hostname, char *dn)
        NTSTATUS    status;
        ADS_STATUS  ret;
        DOM_SID     sid;
-       SEC_DESC   *psd = 0;
-       TALLOC_CTX *ctx = 0;    
+       SEC_DESC   *psd = NULL;
+       TALLOC_CTX *ctx = NULL; 
+
+       /* Avoid segmentation fault in prs_mem_free if
+        * we have to bail out before prs_init */
+       ps_wire.is_dynamic = False;
 
        if (!ads) return ADS_ERROR(LDAP_SERVER_DOWN);
 
        ret = ADS_ERROR(LDAP_SUCCESS);
 
-       asprintf(&exp, "(samAccountName=%s$)", hostname);
-       ret = ads_search(ads, (void *) &res, exp, attrs);
+       if (!escaped_hostname) {
+               return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+       }
+
+       if (asprintf(&expr, "(samAccountName=%s$)", escaped_hostname) == -1) {
+               DEBUG(1, ("ads_set_machine_sd: asprintf failed!\n"));
+               SAFE_FREE(escaped_hostname);
+               return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+       }
+
+       SAFE_FREE(escaped_hostname);
+
+       ret = ads_search(ads, (void *) &res, expr, attrs);
 
        if (!ADS_ERR_OK(ret)) return ret;
 
-       msg   = ads_first_entry(ads, res);
-       bvals = ldap_get_values_len(ads->ld, msg, attrs[0]);
-       ads_pull_sid(ads, msg, attrs[1], &sid); 
-       ads_msgfree(ads, res);
-#if 0
-       file_save("/tmp/sec_desc.old", bvals[0]->bv_val, bvals[0]->bv_len);
-#endif
-       if (!(ctx = talloc_init_named("sec_io_desc")))
-               return ADS_ERROR(LDAP_NO_MEMORY);
+       if ( !(msg = ads_first_entry(ads, res) )) {
+               ret = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
+               goto ads_set_sd_error;
+       }
+
+       if (!ads_pull_sid(ads, msg, attrs[1], &sid)) {
+               ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+               goto ads_set_sd_error;
+       }
 
-       prs_init(&ps, bvals[0]->bv_len, ctx, UNMARSHALL);
-       prs_append_data(&ps, bvals[0]->bv_val, bvals[0]->bv_len);
-       ps.data_offset = 0;
-       ldap_value_free_len(bvals);
+       if (!(ctx = talloc_init("sec_io_desc"))) {
+               ret =  ADS_ERROR(LDAP_NO_MEMORY);
+               goto ads_set_sd_error;
+       }
 
-       if (!sec_io_desc("sd", &psd, &ps, 1))
+       if (!ads_pull_sd(ads, ctx, msg, attrs[0], &psd)) {
+               ret = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
                goto ads_set_sd_error;
+       }
 
        status = sec_desc_add_sid(ctx, &psd, &sid, SEC_RIGHTS_FULL_CTRL, &sd_size);
 
-       if (!NT_STATUS_IS_OK(status))
+       if (!NT_STATUS_IS_OK(status)) {
+               ret = ADS_ERROR_NT(status);
                goto ads_set_sd_error;
+       }
 
-       prs_init(&ps_wire, sd_size, ctx, MARSHALL);
-       if (!sec_io_desc("sd_wire", &psd, &ps_wire, 1))
+       if (!prs_init(&ps_wire, sd_size, ctx, MARSHALL)) {
+               ret = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+       }
+
+       if (!sec_io_desc("sd_wire", &psd, &ps_wire, 1)) {
+               ret = ADS_ERROR(LDAP_NO_MEMORY);
                goto ads_set_sd_error;
+       }
 
 #if 0
        file_save("/tmp/sec_desc.new", ps_wire.data_p, sd_size);
 #endif
        if (!(mods = ads_init_mods(ctx))) return ADS_ERROR(LDAP_NO_MEMORY);
 
-       bval.bv_len = sd_size;
-       bval.bv_val = prs_data_p(&ps_wire);
-       ads_mod_ber(ctx, &mods, attrs[0], &bval);
-       ret = ads_gen_mod(ads, dn, mods);
+       bval.bv_len = prs_offset(&ps_wire);
+       bval.bv_val = talloc(ctx, bval.bv_len);
+       if (!bval.bv_val) {
+               ret = ADS_ERROR(LDAP_NO_MEMORY);
+               goto ads_set_sd_error;
+       }
 
-       prs_mem_free(&ps);
-       prs_mem_free(&ps_wire);
-       talloc_destroy(ctx);
-       return ret;
+       prs_set_offset(&ps_wire, 0);
+
+       if (!prs_copy_data_out(bval.bv_val, &ps_wire, bval.bv_len)) {
+               ret = ADS_ERROR(LDAP_NO_MEMORY);
+               goto ads_set_sd_error;          
+       }
+
+       ret = ads_mod_ber(ctx, &mods, attrs[0], &bval);
+       if (ADS_ERR_OK(ret)) {
+               ret = ads_gen_mod(ads, dn, mods);
+       }
 
 ads_set_sd_error:
-       prs_mem_free(&ps);
+       ads_msgfree(ads, res);
        prs_mem_free(&ps_wire);
        talloc_destroy(ctx);
-       return ADS_ERROR(LDAP_NO_MEMORY);
-}
-
-/**
- * Set the machine account password
- * @param ads connection to ads server
- * @param hostname machine whose password is being set
- * @param password new password
- * @return status of password change
- **/
-ADS_STATUS ads_set_machine_password(ADS_STRUCT *ads,
-                                   const char *hostname, 
-                                   const char *password)
-{
-       ADS_STATUS status;
-       char *host = strdup(hostname);
-       char *principal; 
-
-       strlower(host);
-
-       /*
-         we need to use the '$' form of the name here, as otherwise the
-         server might end up setting the password for a user instead
-        */
-       asprintf(&principal, "%s$@%s", host, ads->auth.realm);
-       
-       status = krb5_set_password(ads->auth.kdc_server, principal, password);
-       
-       free(host);
-       free(principal);
-
-       return status;
+       return ret;
 }
 
 /**
@@ -1456,15 +1527,16 @@ char *ads_pull_string(ADS_STRUCT *ads,
        char **values;
        char *ret = NULL;
        char *ux_string;
-       int rc;
+       size_t rc;
 
        values = ldap_get_values(ads->ld, msg, field);
-       if (!values) return NULL;
+       if (!values)
+               return NULL;
        
        if (values[0]) {
                rc = pull_utf8_talloc(mem_ctx, &ux_string, 
                                      values[0]);
-               if (rc != -1)
+               if (rc != (size_t)-1)
                        ret = ux_string;
                
        }
@@ -1488,15 +1560,22 @@ char **ads_pull_strings(ADS_STRUCT *ads,
        int i, n;
 
        values = ldap_get_values(ads->ld, msg, field);
-       if (!values) return NULL;
+       if (!values)
+               return NULL;
 
-       for (i=0;values[i];i++) /* noop */ ;
+       for (i=0;values[i];i++)
+               /* noop */ ;
        n = i;
 
        ret = talloc(mem_ctx, sizeof(char *) * (n+1));
+       if (!ret) {
+               ldap_value_free(values);
+               return NULL;
+       }
 
        for (i=0;i<n;i++) {
                if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
+                       ldap_value_free(values);
                        return NULL;
                }
        }
@@ -1521,7 +1600,8 @@ BOOL ads_pull_uint32(ADS_STRUCT *ads,
        char **values;
 
        values = ldap_get_values(ads->ld, msg, field);
-       if (!values) return False;
+       if (!values)
+               return False;
        if (!values[0]) {
                ldap_value_free(values);
                return False;
@@ -1532,6 +1612,33 @@ BOOL ads_pull_uint32(ADS_STRUCT *ads,
        return True;
 }
 
+/**
+ * pull a single objectGUID from an ADS result
+ * @param ads connection to ADS server
+ * @param msg results of search
+ * @param guid 37-byte area to receive text guid
+ * @return boolean indicating success
+ **/
+BOOL ads_pull_guid(ADS_STRUCT *ads,
+                  void *msg, GUID *guid)
+{
+       char **values;
+
+       values = ldap_get_values(ads->ld, msg, "objectGUID");
+       if (!values)
+               return False;
+       
+       if (values[0]) {
+               memcpy(guid, values[0], sizeof(GUID));
+               ldap_value_free(values);
+               return True;
+       }
+       ldap_value_free(values);
+       return False;
+
+}
+
+
 /**
  * pull a single DOM_SID from a ADS result
  * @param ads connection to ads server
@@ -1548,11 +1655,11 @@ BOOL ads_pull_sid(ADS_STRUCT *ads,
 
        values = ldap_get_values_len(ads->ld, msg, field);
 
-       if (!values) return False;
+       if (!values)
+               return False;
 
-       if (values[0]) {
+       if (values[0])
                ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
-       }
        
        ldap_value_free_len(values);
        return ret;
@@ -1576,22 +1683,86 @@ int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
 
        values = ldap_get_values_len(ads->ld, msg, field);
 
-       if (!values) return 0;
+       if (!values)
+               return 0;
 
-       for (i=0; values[i]; i++) /* nop */ ;
+       for (i=0; values[i]; i++)
+               /* nop */ ;
 
        (*sids) = talloc(mem_ctx, sizeof(DOM_SID) * i);
+       if (!(*sids)) {
+               ldap_value_free_len(values);
+               return 0;
+       }
 
        count = 0;
        for (i=0; values[i]; i++) {
                ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
-               if (ret) count++;
+               if (ret) {
+                       fstring sid;
+                       DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid, &(*sids)[count])));
+                       count++;
+               }
        }
        
        ldap_value_free_len(values);
        return count;
 }
 
+/**
+ * pull a SEC_DESC from a ADS result
+ * @param ads connection to ads server
+ * @param mem_ctx TALLOC_CTX for allocating sid array
+ * @param msg Results of search
+ * @param field Attribute to retrieve
+ * @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,
+                 void *msg, const char *field, SEC_DESC **sd)
+{
+       struct berval **values;
+       prs_struct      ps;
+       BOOL ret = False;
+
+       values = ldap_get_values_len(ads->ld, msg, field);
+
+       if (!values) return False;
+
+       if (values[0]) {
+               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);
+       }
+       
+       ldap_value_free_len(values);
+       return ret;
+}
+
+/* 
+ * in order to support usernames longer than 21 characters we need to 
+ * use both the sAMAccountName and the userPrincipalName attributes 
+ * It seems that not all users have the userPrincipalName attribute set
+ *
+ * @param ads connection to ads server
+ * @param mem_ctx TALLOC_CTX for allocating sid array
+ * @param msg Results of search
+ * @return the username
+ */
+char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, void *msg)
+{
+       char *ret, *p;
+
+       ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
+       if (ret && (p = strchr(ret, '@'))) {
+               *p = 0;
+               return ret;
+       }
+       return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
+}
+
 
 /**
  * find the update serial number - this is the core of the ldap cache
@@ -1618,6 +1789,26 @@ ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
        return ADS_SUCCESS;
 }
 
+/* parse a ADS timestring - typical string is
+   '20020917091222.0Z0' which means 09:12.22 17th September
+   2002, timezone 0 */
+static time_t ads_parse_time(const char *str)
+{
+       struct tm tm;
+
+       ZERO_STRUCT(tm);
+
+       if (sscanf(str, "%4d%2d%2d%2d%2d%2d", 
+                  &tm.tm_year, &tm.tm_mon, &tm.tm_mday, 
+                  &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
+               return 0;
+       }
+       tm.tm_year -= 1900;
+       tm.tm_mon -= 1;
+
+       return timegm(&tm);
+}
+
 
 /**
  * Find the servers name and realm - this can be done before authentication 
@@ -1628,22 +1819,37 @@ ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
  **/
 ADS_STATUS ads_server_info(ADS_STRUCT *ads)
 {
-       const char *attrs[] = {"ldapServiceName", NULL};
+       const char *attrs[] = {"ldapServiceName", "currentTime", NULL};
        ADS_STATUS status;
        void *res;
-       char **values;
+       char *value;
        char *p;
+       char *timestr;
+       TALLOC_CTX *ctx;
+
+       if (!(ctx = talloc_init("ads_server_info"))) {
+               return ADS_ERROR(LDAP_NO_MEMORY);
+       }
 
        status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
        if (!ADS_ERR_OK(status)) return status;
 
-       values = ldap_get_values(ads->ld, res, "ldapServiceName");
-       if (!values || !values[0]) return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
+       value = ads_pull_string(ads, ctx, res, "ldapServiceName");
+       if (!value) {
+               return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
+       }
+
+       timestr = ads_pull_string(ads, ctx, res, "currentTime");
+       if (!timestr) {
+               return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
+       }
 
-       p = strchr(values[0], ':');
+       ldap_msgfree(res);
+
+       p = strchr(value, ':');
        if (!p) {
-               ldap_value_free(values);
-               ldap_msgfree(res);
+               talloc_destroy(ctx);
+               DEBUG(1, ("ads_server_info: returned ldap server name did not contain a ':' so was deemed invalid\n"));
                return ADS_ERROR(LDAP_DECODING_ERROR);
        }
 
@@ -1652,8 +1858,8 @@ ADS_STATUS ads_server_info(ADS_STRUCT *ads)
        ads->config.ldap_server_name = strdup(p+1);
        p = strchr(ads->config.ldap_server_name, '$');
        if (!p || p[1] != '@') {
-               ldap_value_free(values);
-               ldap_msgfree(res);
+               talloc_destroy(ctx);
+               DEBUG(1, ("ads_server_info: returned ldap server name (%s) does not contain '$@' so was deemed invalid\n", ads->config.ldap_server_name));
                SAFE_FREE(ads->config.ldap_server_name);
                return ADS_ERROR(LDAP_DECODING_ERROR);
        }
@@ -1666,79 +1872,18 @@ ADS_STATUS ads_server_info(ADS_STRUCT *ads)
        ads->config.realm = strdup(p+2);
        ads->config.bind_path = ads_build_dn(ads->config.realm);
 
-       DEBUG(3,("got ldap server name %s@%s\n", 
-                ads->config.ldap_server_name, ads->config.realm));
-
-       return ADS_SUCCESS;
-}
-
-
-/**
- * find the list of trusted domains
- * @param ads connection to ads server
- * @param mem_ctx TALLOC_CTX for allocating results
- * @param num_trusts pointer to number of trusts
- * @param names pointer to trusted domain name list
- * @param sids pointer to list of sids of trusted domains
- * @return the count of SIDs pulled
- **/
-ADS_STATUS ads_trusted_domains(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, 
-                              int *num_trusts, 
-                              char ***names, 
-                              char ***alt_names,
-                              DOM_SID **sids)
-{
-       const char *attrs[] = {"name", "flatname", "securityIdentifier", 
-                              "trustDirection", NULL};
-       ADS_STATUS status;
-       void *res, *msg;
-       int count, i;
-
-       *num_trusts = 0;
-
-       status = ads_search(ads, &res, "(objectcategory=trustedDomain)", attrs);
-       if (!ADS_ERR_OK(status)) return status;
-
-       count = ads_count_replies(ads, res);
-       if (count == 0) {
-               ads_msgfree(ads, res);
-               return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
-       }
-
-       (*names) = talloc(mem_ctx, sizeof(char *) * count);
-       (*alt_names) = talloc(mem_ctx, sizeof(char *) * count);
-       (*sids) = talloc(mem_ctx, sizeof(DOM_SID) * count);
-       if (! *names || ! *sids) return ADS_ERROR(LDAP_NO_MEMORY);
+       DEBUG(3,("got ldap server name %s@%s, using bind path: %s\n", 
+                ads->config.ldap_server_name, ads->config.realm,
+                ads->config.bind_path));
 
-       for (i=0, msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
-               uint32 direction;
+       ads->config.current_time = ads_parse_time(timestr);
 
-               /* direction is a 2 bit bitfield, 1 means they trust us 
-                  but we don't trust them, so we should not list them
-                  as users from that domain can't login */
-               if (ads_pull_uint32(ads, msg, "trustDirection", &direction) &&
-                   direction == 1) {
-                       continue;
-               }
-               
-               (*names)[i] = ads_pull_string(ads, mem_ctx, msg, "name");
-               (*alt_names)[i] = ads_pull_string(ads, mem_ctx, msg, "flatname");
-
-               if ((*alt_names)[i] && (*alt_names)[i][0]) {
-                       /* we prefer the flatname as the primary name
-                          for consistency with RPC */
-                       char *name = (*alt_names)[i];
-                       (*alt_names)[i] = (*names)[i];
-                       (*names)[i] = name;
-               }
-               if (ads_pull_sid(ads, msg, "securityIdentifier", &(*sids)[i])) {
-                       i++;
-               }
+       if (ads->config.current_time != 0) {
+               ads->auth.time_offset = ads->config.current_time - time(NULL);
+               DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
        }
 
-       ads_msgfree(ads, res);
-
-       *num_trusts = i;
+       talloc_destroy(ctx);
 
        return ADS_SUCCESS;
 }
@@ -1770,10 +1915,17 @@ ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
    for the domain, but there isn't a simple query to do this. Instead
    we look for the principle names on the DCs account and find one that has 
    the right form, then extract the netbios name of the domain from that
+
+   NOTE! better method is this:
+
+bin/net -Uadministrator%XXXXX ads search '(&(objectclass=crossref)(dnsroot=VNET3.HOME.SAMBA.ORG))'  nETBIOSName 
+
+but you need to force the bind path to match the configurationNamingContext from the rootDSE
+
 */
 ADS_STATUS ads_workgroup_name(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char **workgroup)
 {
-       char *exp;
+       char *expr;
        ADS_STATUS rc;
        char **principles;
        char *prefix;
@@ -1784,10 +1936,10 @@ ADS_STATUS ads_workgroup_name(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char **workg
 
        (*workgroup) = NULL;
 
-       asprintf(&exp, "(&(objectclass=computer)(dnshostname=%s.%s))", 
+       asprintf(&expr, "(&(objectclass=computer)(dnshostname=%s.%s))", 
                 ads->config.ldap_server_name, ads->config.realm);
-       rc = ads_search(ads, &res, exp, attrs);
-       free(exp);
+       rc = ads_search(ads, &res, expr, attrs);
+       free(expr);
 
        if (!ADS_ERR_OK(rc)) {
                return rc;