Ensure that only parse_prs.c access internal members of the prs_struct.
[samba.git] / source3 / libads / ldap.c
index 385a9bd93f90e326d4ab79d410c11485363f1009..47a94f0a08d364696ef55906d5433c487828d805 100644 (file)
@@ -22,7 +22,7 @@
 
 #include "includes.h"
 
-#ifdef HAVE_ADS
+#ifdef HAVE_LDAP
 
 /**
  * @file ldap.c
@@ -67,6 +67,29 @@ static BOOL ads_try_connect(ADS_STRUCT *ads, const char *server, unsigned port)
        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;
@@ -82,26 +105,28 @@ static int ldap_ip_compare(struct ldap_ip *ip1, struct ldap_ip *ip2)
 /* try connecting to a ldap server via DNS */
 static BOOL ads_try_dns(ADS_STRUCT *ads)
 {
-       char *realm, *ptr;
+       const char *c_realm;
+       const char *ptr;
+       char *realm;
        char *list = NULL;
        pstring tok;
        struct ldap_ip *ip_list;
        int count, i=0;
 
-       realm = ads->server.realm;
-       if (!realm || !*realm) {
-               realm = lp_realm();
+       c_realm = ads->server.realm;
+       if (!c_realm || !*c_realm) {
+               c_realm = lp_realm();
        }
-       if (!realm || !*realm) {
-               realm = ads->server.workgroup;
+       if (!c_realm || !*c_realm) {
+               c_realm = ads->server.workgroup;
        }
-       if (!realm || !*realm) {
-               realm = lp_workgroup();
+       if (!c_realm || !*c_realm) {
+               c_realm = lp_workgroup();
        }
-       if (!realm) {
+       if (!c_realm) {
                return False;
        }
-       realm = smb_xstrdup(realm);
+       realm = smb_xstrdup(c_realm);
 
        DEBUG(6,("ads_try_dns: looking for realm '%s'\n", realm));
        if (ldap_domain2hostlist(realm, &list) != LDAP_SUCCESS) {
@@ -157,10 +182,11 @@ static BOOL ads_try_dns(ADS_STRUCT *ads)
 /* try connecting to a ldap server via netbios */
 static BOOL ads_try_netbios(ADS_STRUCT *ads)
 {
-       struct in_addr *ip_list;
+       struct in_addr *ip_list, pdc_ip;
        int count;
        int i;
-       char *workgroup = ads->server.workgroup;
+       const char *workgroup = ads->server.workgroup;
+       BOOL list_ordered;
 
        if (!workgroup) {
                workgroup = lp_workgroup();
@@ -169,20 +195,15 @@ static BOOL ads_try_netbios(ADS_STRUCT *ads)
        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);
+       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(False, workgroup, &ip_list, &count)) { 
+       if (get_dc_list(workgroup, &ip_list, &count, &list_ordered)) { 
                for (i=0;i<count;i++) {
                        DEBUG(6,("ads_try_netbios: trying server '%s'\n", 
                                 inet_ntoa(ip_list[i])));
@@ -210,6 +231,13 @@ ADS_STATUS ads_connect(ADS_STRUCT *ads)
        ads->last_attempt = time(NULL);
        ads->ld = NULL;
 
+       /* try with a URL based server */
+
+       if (ads->server.ldap_uri &&
+           ads_try_connect_uri(ads)) {
+               goto got_connection;
+       }
+
        /* try with a user specified server */
        if (ads->server.ldap_server && 
            ads_try_connect(ads, ads->server.ldap_server, LDAP_PORT)) {
@@ -248,9 +276,8 @@ got_connection:
 
        if (!ads->auth.user_name) {
                /* by default use the machine account */
-               extern pstring global_myname;
                fstring myname;
-               fstrcpy(myname, global_myname);
+               fstrcpy(myname, global_myname());
                strlower(myname);
                asprintf(&ads->auth.user_name, "HOST/%s", myname);
        }
@@ -278,6 +305,14 @@ got_connection:
                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 +326,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;
@@ -385,7 +422,7 @@ 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 
@@ -402,8 +439,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;
                }
@@ -608,7 +644,7 @@ 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("ads_do_search"))) {
                DEBUG(1,("ads_do_search: talloc_init() failed!"));
                return ADS_ERROR(LDAP_NO_MEMORY);
        }
@@ -741,7 +777,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;
@@ -890,7 +930,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];
@@ -898,13 +938,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);
@@ -922,14 +964,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);
 }
@@ -944,8 +989,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(ads->ld, utf8_dn);
        return ADS_ERROR(ret);
 }
 
@@ -980,8 +1029,11 @@ 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[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);
@@ -991,17 +1043,37 @@ 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(&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(&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 = 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)))
@@ -1011,7 +1083,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");
@@ -1040,6 +1112,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
 */
@@ -1063,13 +1152,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)) {
@@ -1101,12 +1190,12 @@ 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},
@@ -1165,7 +1254,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; 
@@ -1320,10 +1409,9 @@ 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;
+       char           *escaped_hostname = escape_ldap_string_alloc(hostname);
 
        LDAPMessage *res  = 0;
        LDAPMessage *msg  = 0;
@@ -1339,37 +1427,50 @@ 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 (!escaped_hostname) {
+               return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+       }
+
+       if (asprintf(&exp, "(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, 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);
+       if ( !(msg = ads_first_entry(ads, res) )) {
+               ret = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
+               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);
+       ads_pull_sid(ads, msg, attrs[1], &sid); 
+       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 (!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);
@@ -1377,51 +1478,21 @@ ADS_STATUS ads_set_machine_sd(ADS_STRUCT *ads, const char *hostname, char *dn)
        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);
+       bval.bv_val = talloc(ctx, sd_size);
+       if (!bval.bv_val) {
+               ret = ADS_ERROR(LDAP_NO_MEMORY);
+               goto ads_set_sd_error;
+       }
+       prs_copy_all_data_out((char *)&bval.bv_val, &ps_wire);
+
        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; 
-
-       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, ads->auth.time_offset);
-       
-       free(host);
-       free(principal);
-
-       return status;
+       return ret;
 }
 
 /**
@@ -1463,7 +1534,8 @@ char *ads_pull_string(ADS_STRUCT *ads,
        int 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, 
@@ -1492,15 +1564,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;
                }
        }
@@ -1525,7 +1604,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;
@@ -1536,6 +1616,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
@@ -1552,11 +1659,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;
@@ -1580,22 +1687,83 @@ 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)
+                       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
@@ -1660,7 +1828,7 @@ ADS_STATUS ads_server_info(ADS_STRUCT *ads)
        char *timestr;
        TALLOC_CTX *ctx;
 
-       if (!(ctx = talloc_init())) {
+       if (!(ctx = talloc_init("ads_server_info"))) {
                return ADS_ERROR(LDAP_NO_MEMORY);
        }
 
@@ -1692,8 +1860,8 @@ ADS_STATUS ads_server_info(ADS_STRUCT *ads)
        p = strchr(ads->config.ldap_server_name, '$');
        if (!p || p[1] != '@') {
                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);
-               DEBUG(1, ("ads_server_info: returned ldap server name did not contain '$@' so was deemed invalid\n"));
                return ADS_ERROR(LDAP_DECODING_ERROR);
        }
 
@@ -1705,8 +1873,9 @@ 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));
+       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->config.current_time = ads_parse_time(timestr);
 
@@ -1818,6 +1987,13 @@ 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)
 {