Removed global_myworkgroup, global_myname, global_myscope. Added liberal
[samba.git] / source3 / libads / ldap.c
index 7e51c203ca413e4c0e4d599caea5772cf454fbd1..d5cd56001be8a34058322a28d835bdcadf2b0771 100644 (file)
@@ -22,7 +22,7 @@
 
 #include "includes.h"
 
-#ifdef HAVE_ADS
+#ifdef HAVE_LDAP
 
 /**
  * @file ldap.c
  * codepoints in UTF-8).  This may have to change at some point
  **/
 
+
+/*
+  try a connection to a given ldap server, returning True and setting the servers IP
+  in the ads struct if successful
+ */
+static BOOL ads_try_connect(ADS_STRUCT *ads, const char *server, unsigned port)
+{
+       char *srv;
+
+       if (!server || !*server) {
+               return False;
+       }
+
+       DEBUG(5,("ads_try_connect: trying ldap server '%s' port %u\n", server, port));
+
+       /* this copes with inet_ntoa brokenness */
+       srv = strdup(server);
+
+       ads->ld = ldap_open(srv, port);
+       if (!ads->ld) {
+               free(srv);
+               return False;
+       }
+       ads->ldap_port = port;
+       ads->ldap_ip = *interpret_addr2(srv);
+       free(srv);
+
+       return True;
+}
+
+/*
+  try a connection to a given ldap server, based on URL, returning True if successful
+ */
+static BOOL ads_try_connect_uri(ADS_STRUCT *ads)
+{
+#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;
+}
+
+/* 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)
+{
+       return ip_compare(&ip1->ip, &ip2->ip);
+}
+
+/* try connecting to a ldap server via DNS */
+static BOOL ads_try_dns(ADS_STRUCT *ads)
+{
+       const char *c_realm;
+       const char *ptr;
+       char *realm;
+       char *list = NULL;
+       pstring tok;
+       struct ldap_ip *ip_list;
+       int count, i=0;
+
+       c_realm = ads->server.realm;
+       if (!c_realm || !*c_realm) {
+               c_realm = lp_realm();
+       }
+       if (!c_realm || !*c_realm) {
+               c_realm = ads->server.workgroup;
+       }
+       if (!c_realm || !*c_realm) {
+               c_realm = lp_workgroup();
+       }
+       if (!c_realm) {
+               return False;
+       }
+       realm = smb_xstrdup(c_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);
+
+       count = count_chars(list, ' ') + 1;
+       ip_list = malloc(count * sizeof(struct ldap_ip));
+       if (!ip_list) {
+               return False;
+       }
+
+       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);
+               }
+               ip_list[i].ip = *interpret_addr2(tok);
+               ip_list[i].port = port;
+               if (!is_zero_ip(ip_list[i].ip)) {
+                       i++;
+               }
+       }
+       free(list);
+
+       count = i;
+
+       /* 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);      
+       }
+
+       for (i=0;i<count;i++) {
+               if (ads_try_connect(ads, inet_ntoa(ip_list[i].ip), ip_list[i].port)) {
+                       free(ip_list);
+                       return True;
+               }
+       }
+
+       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, pdc_ip;
+       int count;
+       int i;
+       const 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_pdc_ip(workgroup, &pdc_ip)) { 
+               DEBUG(6,("ads_try_netbios: trying server '%s'\n", 
+                        inet_ntoa(pdc_ip)));
+               if (ads_try_connect(ads, inet_ntoa(pdc_ip), LDAP_PORT))
+                       return True;
+       }
+
+       /* now any DC, including backups */
+       if (get_dc_list(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
  * @param ads Pointer to an existing ADS_STRUCT
 ADS_STATUS ads_connect(ADS_STRUCT *ads)
 {
        int version = LDAP_VERSION3;
-       int code;
        ADS_STATUS status;
 
        ads->last_attempt = time(NULL);
-
        ads->ld = NULL;
 
-       if (ads->ldap_server) {
-               ads->ld = ldap_open(ads->ldap_server, ads->ldap_port);
+       /* try with a URL based server */
+
+       if (ads->server.ldap_uri &&
+           ads_try_connect_uri(ads)) {
+               goto got_connection;
        }
 
-       /* if that failed then try each of the BDC's in turn */
-       if (!ads->ld) {
-               struct in_addr *ip_list;
-               int count;
-
-               if (get_dc_list(False, ads->workgroup, &ip_list, &count)) {
-                       int i;
-                       for (i=0;i<count;i++) {
-                               ads->ld = ldap_open(inet_ntoa(ip_list[i]),
-                                                   ads->ldap_port);
-                               if (ads->ld) break;
-                       }
-                       if (ads->ld) {
-                               SAFE_FREE(ads->ldap_server);
-                               ads->ldap_server = strdup(inet_ntoa(ip_list[i]));
-                       }
-                       free(ip_list);
-               }
+       /* try with a user specified server */
+       if (ads->server.ldap_server && 
+           ads_try_connect(ads, ads->server.ldap_server, LDAP_PORT)) {
+               goto got_connection;
        }
 
-       if (!ads->ld) {
-               return ADS_ERROR_SYSTEM(errno);
+       /* 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)) {
+               goto got_connection;
        }
 
-       DEBUG(3,("Connected to LDAP server %s\n", ads->ldap_server));
+       /* try via DNS */
+       if (ads_try_dns(ads)) {
+               goto got_connection;
+       }
+
+       /* try via netbios lookups */
+       if (!lp_disable_netbios() && ads_try_netbios(ads)) {
+               goto got_connection;
+       }
+
+       return ADS_ERROR_SYSTEM(errno?errno:ENOENT);
+
+got_connection:
+       DEBUG(3,("Connected to LDAP server %s\n", inet_ntoa(ads->ldap_ip)));
 
        status = ads_server_info(ads);
        if (!ADS_ERR_OK(status)) {
@@ -90,20 +273,43 @@ ADS_STATUS ads_connect(ADS_STRUCT *ads)
 
        ldap_set_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
 
+       if (!ads->auth.user_name) {
+               /* by default use the machine account */
+               fstring myname;
+               fstrcpy(myname, global_myname());
+               strlower(myname);
+               asprintf(&ads->auth.user_name, "HOST/%s", myname);
+       }
+
+       if (!ads->auth.realm) {
+               ads->auth.realm = strdup(ads->config.realm);
+       }
+
+       if (!ads->auth.kdc_server) {
+               ads->auth.kdc_server = strdup(inet_ntoa(ads->ldap_ip));
+       }
+
 #if KRB5_DNS_HACK
        /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
           to MIT kerberos to work (tridge) */
        {
                char *env;
-               asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->server_realm);
-               setenv(env, inet_ntoa(*interpret_addr2(ads->ldap_server)), 1);
+               asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
+               setenv(env, ads->auth.kdc_server, 1);
                free(env);
        }
 #endif
 
-       if (ads->password) {
-               if ((code = ads_kinit_password(ads)))
-                       return ADS_ERROR_KRB5(code);
+       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);
@@ -161,7 +367,7 @@ 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, (void **) &values[i], in_vals[i]);
+               push_utf8_talloc(ctx, &values[i], in_vals[i]);
        }
        return values;
 }
@@ -180,7 +386,7 @@ 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, (void **) &values[i], in_vals[i]);
+               pull_utf8_talloc(ctx, &values[i], in_vals[i]);
        }
        return values;
 }
@@ -219,8 +425,8 @@ ADS_STATUS ads_do_paged_search(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, (void **) &utf8_exp, exp) < 0) ||
-           (push_utf8_talloc(ctx, (void **) &utf8_path, bind_path) < 0)) {
+       if ((push_utf8_talloc(ctx, &utf8_exp, exp) < 0) ||
+           (push_utf8_talloc(ctx, &utf8_path, bind_path) < 0)) {
                rc = LDAP_NO_MEMORY;
                goto done;
        }
@@ -230,8 +436,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, (char **) attrs)))
-               {
+               if (!(str_list_copy(&search_attrs, attrs))) {
                        rc = LDAP_NO_MEMORY;
                        goto done;
                }
@@ -436,14 +641,17 @@ ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
        char *utf8_exp, *utf8_path, **search_attrs = NULL;
        TALLOC_CTX *ctx;
 
-       if (!(ctx = talloc_init()))
+       if (!(ctx = talloc_init())) {
+               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, (void **) &utf8_exp, exp) < 0) ||
-           (push_utf8_talloc(ctx, (void **) &utf8_path, bind_path) < 0)) {
+       if ((push_utf8_talloc(ctx, &utf8_exp, exp) < 0) ||
+           (push_utf8_talloc(ctx, &utf8_path, bind_path) < 0)) {
+               DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
                rc = LDAP_NO_MEMORY;
                goto done;
        }
@@ -453,8 +661,9 @@ ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
        else {
                /* This would be the utf8-encoded version...*/
                /* if (!(search_attrs = ads_push_strvals(ctx, attrs)))  */
-               if (!(str_list_copy(&search_attrs, (char **) attrs)))
+               if (!(str_list_copy(&search_attrs, attrs)))
                {
+                       DEBUG(1,("ads_do_search: str_list_copy() failed!"));
                        rc = LDAP_NO_MEMORY;
                        goto done;
                }
@@ -494,7 +703,7 @@ ADS_STATUS ads_search(ADS_STRUCT *ads, void **res,
                      const char *exp, 
                      const char **attrs)
 {
-       return ads_do_search(ads, ads->bind_path, LDAP_SCOPE_SUBTREE, 
+       return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE, 
                             exp, attrs, res);
 }
 
@@ -565,7 +774,11 @@ ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, void **res, const char *host)
 
        /* the easiest way to find a machine account anywhere in the tree
           is to look for hostname$ */
-       asprintf(&exp, "(samAccountName=%s$)", host);
+       if (asprintf(&exp, "(samAccountName=%s$)", host) == -1) {
+               DEBUG(1, ("asprintf failed!\n"));
+               return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+       }
+       
        status = ads_search(ads, res, exp, attrs);
        free(exp);
        return status;
@@ -649,7 +862,11 @@ static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods, 
                       const char *name, const char *val)
 {
-       const char *values[2] = {val, NULL};
+       const char *values[2];
+
+       values[0] = val;
+       values[1] = NULL;
+
        if (!val)
                return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
        return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, 
@@ -684,7 +901,10 @@ ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods, 
                              const char *name, const struct berval *val)
 {
-       const struct berval *values[2] = {val, NULL};
+       const struct berval *values[2];
+
+       values[0] = val;
+       values[1] = NULL;
        if (!val)
                return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
        return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
@@ -707,7 +927,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];
@@ -715,13 +935,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);
@@ -739,7 +961,10 @@ 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++);
@@ -761,7 +986,11 @@ 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);
+       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(ads->ld, utf8_dn ? utf8_dn : del_dn);
        return ADS_ERROR(ret);
 }
@@ -797,6 +1026,9 @@ static ADS_STATUS ads_add_machine_acct(ADS_STRUCT *ads, const char *hostname,
        ADS_MODLIST mods;
        const char *objectClass[] = {"top", "person", "organizationalPerson",
                                     "user", "computer", NULL};
+       const char *servicePrincipalName[3] = {NULL, NULL, NULL};
+       char *psp;
+       unsigned acct_control;
 
        if (!(ctx = talloc_init_named("machine_account")))
                return ADS_ERROR(LDAP_NO_MEMORY);
@@ -805,20 +1037,34 @@ static ADS_STATUS ads_add_machine_acct(ADS_STRUCT *ads, const char *hostname,
 
        if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", hostname)))
                goto done;
-       if (!(host_upn = talloc_asprintf(ctx, "%s@%s", host_spn, ads->realm)))
+       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->bind_path);
+                                ads->config.bind_path);
+       servicePrincipalName[0] = talloc_asprintf(ctx, "HOST/%s", hostname);
+       psp = talloc_asprintf(ctx, "HOST/%s.%s", 
+                                                 hostname, 
+                                                 ads->config.realm);
+       strlower(&psp[5]);
+       servicePrincipalName[1] = psp;
+
        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 = UF_WORKSTATION_TRUST_ACCOUNT | 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)))
@@ -828,7 +1074,7 @@ 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");
@@ -857,6 +1103,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, uuid_string_static(guid));
+       }
+}
+
 /*
   dump a sid result from ldap
 */
@@ -903,11 +1166,11 @@ static void dump_sd(const char *filed, struct berval **values)
 /*
   dump a string result from ldap
 */
-static void dump_string(const char *field, struct berval **values)
+static void dump_string(const char *field, char **values)
 {
        int i;
        for (i=0; values[i]; i++) {
-               printf("%s: %s\n", field, values[i]->bv_val);
+               printf("%s: %s\n", field, values[i]);
        }
 }
 
@@ -923,8 +1186,9 @@ static BOOL ads_dump_field(char *field, void **values, void *data_area)
                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},
                {NULL, True, NULL}
        };
@@ -946,7 +1210,7 @@ static BOOL ads_dump_field(char *field, void **values, void *data_area)
        if (!handlers[i].name) {
                if (!values) /* first time, indicate string conversion */
                        return True;
-               dump_string(field, (struct berval **) values);
+               dump_string(field, (char **)values);
        }
        return False;
 }
@@ -999,7 +1263,7 @@ void ads_process_results(ADS_STRUCT *ads, void *res,
                        char *field;
                        BOOL string; 
 
-                       pull_utf8_talloc(ctx, (void **) &field, utf8_field);
+                       pull_utf8_talloc(ctx, &field, utf8_field);
                        string = fn(field, NULL, data_area);
 
                        if (string) {
@@ -1061,7 +1325,7 @@ ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *hostname, const char *org
                status = ads_leave_realm(ads, host);
                if (!ADS_ERR_OK(status)) {
                        DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n", 
-                                 host, ads->realm));
+                                 host, ads->config.realm));
                        return status;
                }
        }
@@ -1136,9 +1400,7 @@ ADS_STATUS ads_set_machine_sd(ADS_STRUCT *ads, const char *hostname, char *dn)
        const char     *attrs[] = {"ntSecurityDescriptor", "objectSid", 0};
        char           *exp     = 0;
        size_t          sd_size = 0;
-       struct berval **bvals   = 0;
        struct berval   bval = {0, NULL};
-       prs_struct      ps;
        prs_struct      ps_wire;
 
        LDAPMessage *res  = 0;
@@ -1155,37 +1417,39 @@ ADS_STATUS ads_set_machine_sd(ADS_STRUCT *ads, const char *hostname, char *dn)
 
        ret = ADS_ERROR(LDAP_SUCCESS);
 
-       asprintf(&exp, "(samAccountName=%s$)", hostname);
+       if (asprintf(&exp, "(samAccountName=%s$)", hostname) == -1) {
+               DEBUG(1, ("ads_set_machine_sd: asprintf failed!\n"));
+               return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+       }
+
        ret = ads_search(ads, (void *) &res, exp, 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);
-
-       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_named("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 (!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);
@@ -1197,48 +1461,11 @@ ADS_STATUS ads_set_machine_sd(ADS_STRUCT *ads, const char *hostname, char *dn)
        ads_mod_ber(ctx, &mods, attrs[0], &bval);
        ret = ads_gen_mod(ads, dn, mods);
 
-       prs_mem_free(&ps);
-       prs_mem_free(&ps_wire);
-       talloc_destroy(ctx);
-       return ret;
-
 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; 
-
-        if (!ads->kdc_server) {
-               DEBUG(0, ("Unable to find KDC server\n"));
-               return ADS_ERROR(LDAP_SERVER_DOWN);
-       }
-
-       strlower(host);
-
-       asprintf(&principal, "%s@%s", host, ads->realm);
-       
-       status = krb5_set_password(ads->kdc_server, principal, password);
-       
-       free(host);
-       free(principal);
-
-       return status;
+       return ret;
 }
 
 /**
@@ -1283,7 +1510,7 @@ char *ads_pull_string(ADS_STRUCT *ads,
        if (!values) return NULL;
        
        if (values[0]) {
-               rc = pull_utf8_talloc(mem_ctx, (void **)&ux_string, 
+               rc = pull_utf8_talloc(mem_ctx, &ux_string, 
                                      values[0]);
                if (rc != -1)
                        ret = ux_string;
@@ -1293,6 +1520,41 @@ char *ads_pull_string(ADS_STRUCT *ads,
        return ret;
 }
 
+/**
+ * pull an array of strings from a ADS result
+ * @param ads connection to ads server
+ * @param mem_ctx TALLOC_CTX to use for allocating result string
+ * @param msg Results of search
+ * @param field Attribute to retrieve
+ * @return Result strings in talloc context
+ **/
+char **ads_pull_strings(ADS_STRUCT *ads, 
+                      TALLOC_CTX *mem_ctx, void *msg, const char *field)
+{
+       char **values;
+       char **ret = NULL;
+       int i, n;
+
+       values = ldap_get_values(ads->ld, msg, field);
+       if (!values) return NULL;
+
+       for (i=0;values[i];i++) /* noop */ ;
+       n = i;
+
+       ret = talloc(mem_ctx, sizeof(char *) * (n+1));
+
+       for (i=0;i<n;i++) {
+               if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
+                       return NULL;
+               }
+       }
+       ret[i] = NULL;
+
+       ldap_value_free(values);
+       return ret;
+}
+
+
 /**
  * pull a single uint32 from a ADS result
  * @param ads connection to ads server
@@ -1378,6 +1640,60 @@ int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
        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_append_data(&ps, values[0]->bv_val, values[0]->bv_len);
+               ps.data_offset = 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
@@ -1404,6 +1720,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 
@@ -1414,52 +1750,71 @@ 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())) {
+               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);
        }
 
-       SAFE_FREE(ads->ldap_server_name);
+       SAFE_FREE(ads->config.ldap_server_name);
 
-       ads->ldap_server_name = strdup(p+1);
-       p = strchr(ads->ldap_server_name, '$');
+       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);
-               SAFE_FREE(ads->ldap_server_name);
+               talloc_destroy(ctx);
+               SAFE_FREE(ads->config.ldap_server_name);
+               DEBUG(1, ("ads_server_info: returned ldap server name did not contain '$@' so was deemed invalid\n"));
                return ADS_ERROR(LDAP_DECODING_ERROR);
        }
 
        *p = 0;
 
-       SAFE_FREE(ads->server_realm);
-       SAFE_FREE(ads->bind_path);
+       SAFE_FREE(ads->config.realm);
+       SAFE_FREE(ads->config.bind_path);
+
+       ads->config.realm = strdup(p+2);
+       ads->config.bind_path = ads_build_dn(ads->config.realm);
+
+       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));
 
-       ads->server_realm = strdup(p+2);
-       ads->bind_path = ads_build_dn(ads->server_realm);
+       ads->config.current_time = ads_parse_time(timestr);
 
-       /* in case the realm isn't configured in smb.conf */
-       if (!ads->realm || !ads->realm[0]) {
-               SAFE_FREE(ads->realm);
-               ads->realm = strdup(ads->server_realm);
+       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));
        }
 
-       DEBUG(3,("got ldap server name %s@%s\n", 
-                ads->ldap_server_name, ads->realm));
+       talloc_destroy(ctx);
 
        return ADS_SUCCESS;
 }
@@ -1475,9 +1830,13 @@ ADS_STATUS ads_server_info(ADS_STRUCT *ads)
  * @return the count of SIDs pulled
  **/
 ADS_STATUS ads_trusted_domains(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, 
-                              int *num_trusts, char ***names, DOM_SID **sids)
+                              int *num_trusts, 
+                              char ***names, 
+                              char ***alt_names,
+                              DOM_SID **sids)
 {
-       const char *attrs[] = {"flatName", "securityIdentifier", NULL};
+       const char *attrs[] = {"name", "flatname", "securityIdentifier", 
+                              "trustDirection", NULL};
        ADS_STATUS status;
        void *res, *msg;
        int count, i;
@@ -1494,11 +1853,31 @@ ADS_STATUS ads_trusted_domains(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
        }
 
        (*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);
 
        for (i=0, msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
-               (*names)[i] = ads_pull_string(ads, mem_ctx, msg, "flatName");
+               uint32 direction;
+
+               /* 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++;
                }
@@ -1523,7 +1902,7 @@ ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
        void *res;
        ADS_STATUS rc;
 
-       rc = ads_do_search(ads, ads->bind_path, LDAP_SCOPE_BASE, "(objectclass=*)", 
+       rc = ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)", 
                           attrs, &res);
        if (!ADS_ERR_OK(rc)) return rc;
        if (!ads_pull_sid(ads, res, "objectSid", sid)) {
@@ -1534,4 +1913,66 @@ ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
        return ADS_SUCCESS;
 }
 
+/* this is rather complex - we need to find the allternate (netbios) name
+   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
+*/
+ADS_STATUS ads_workgroup_name(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char **workgroup)
+{
+       char *exp;
+       ADS_STATUS rc;
+       char **principles;
+       char *prefix;
+       int prefix_length;
+       int i;
+       void *res;
+       const char *attrs[] = {"servicePrincipalName", NULL};
+
+       (*workgroup) = NULL;
+
+       asprintf(&exp, "(&(objectclass=computer)(dnshostname=%s.%s))", 
+                ads->config.ldap_server_name, ads->config.realm);
+       rc = ads_search(ads, &res, exp, attrs);
+       free(exp);
+
+       if (!ADS_ERR_OK(rc)) {
+               return rc;
+       }
+
+       principles = ads_pull_strings(ads, mem_ctx, res, "servicePrincipalName");
+
+       ads_msgfree(ads, res);
+
+       if (!principles) {
+               return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
+       }
+
+       asprintf(&prefix, "HOST/%s.%s/", 
+                ads->config.ldap_server_name, 
+                ads->config.realm);
+
+       prefix_length = strlen(prefix);
+
+       for (i=0;principles[i]; i++) {
+               if (strncasecmp(principles[i], prefix, prefix_length) == 0 &&
+                   strcasecmp(ads->config.realm, principles[i]+prefix_length) != 0 &&
+                   !strchr(principles[i]+prefix_length, '.')) {
+                       /* found an alternate (short) name for the domain. */
+                       DEBUG(3,("Found alternate name '%s' for realm '%s'\n",
+                                principles[i]+prefix_length, 
+                                ads->config.realm));
+                       (*workgroup) = talloc_strdup(mem_ctx, principles[i]+prefix_length);
+                       break;
+               }
+       }
+       free(prefix);
+
+       if (!*workgroup) {
+               return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
+       }
+       
+       return ADS_SUCCESS;
+}
+
 #endif