Don't use 'dn', this attribute does not exist with the LDAP backend,
[samba.git] / source4 / dsdb / samdb / cracknames.c
index 16aa616983c4b413f3d31453a1c756ee6ac44d74..b9333e451bade34a437f63b53f749673cb0977d0 100644 (file)
@@ -9,7 +9,7 @@
    
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 #include "includes.h"
 #include "librpc/gen_ndr/drsuapi.h"
 #include "rpc_server/common/common.h"
+#include "lib/ldb/include/ldb.h"
 #include "lib/ldb/include/ldb_errors.h"
 #include "system/kerberos.h"
 #include "auth/kerberos/kerberos.h"
-#include "libcli/ldap/ldap.h"
+#include "libcli/ldap/ldap_ndr.h"
 #include "libcli/security/security.h"
 #include "librpc/gen_ndr/ndr_misc.h"
 #include "auth/auth.h"
-#include "db_wrap.h"
+#include "util/util_ldb.h"
 #include "dsdb/samdb/samdb.h"
+#include "param/param.h"
 
 static WERROR DsCrackNameOneFilter(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
                                   struct smb_krb5_context *smb_krb5_context,
@@ -46,6 +47,33 @@ static WERROR DsCrackNameOneSyntactical(TALLOC_CTX *mem_ctx,
                                        struct ldb_dn *name_dn, const char *name, 
                                        struct drsuapi_DsNameInfo1 *info1);
 
+static WERROR dns_domain_from_principal(TALLOC_CTX *mem_ctx, struct smb_krb5_context *smb_krb5_context, 
+                                       const char *name, 
+                                       struct drsuapi_DsNameInfo1 *info1) 
+{
+       krb5_error_code ret;
+       krb5_principal principal;
+       /* perhaps it's a principal with a realm, so return the right 'domain only' response */
+       char **realm;
+       ret = krb5_parse_name_flags(smb_krb5_context->krb5_context, name, 
+                                   KRB5_PRINCIPAL_PARSE_MUST_REALM, &principal);
+       if (ret) {
+               info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
+               return WERR_OK;
+       }
+
+       /* This isn't an allocation assignemnt, so it is free'ed with the krb5_free_principal */
+       realm = krb5_princ_realm(smb_krb5_context->krb5_context, principal);
+       
+       info1->dns_domain_name  = talloc_strdup(mem_ctx, *realm);
+       krb5_free_principal(smb_krb5_context->krb5_context, principal);
+       
+       W_ERROR_HAVE_NO_MEMORY(info1->dns_domain_name);
+       
+       info1->status = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY;
+       return WERR_OK;
+}              
+
 static enum drsuapi_DsNameStatus LDB_lookup_spn_alias(krb5_context context, struct ldb_context *ldb_ctx, 
                                                      TALLOC_CTX *mem_ctx,
                                                      const char *alias_from,
@@ -69,11 +97,11 @@ static enum drsuapi_DsNameStatus LDB_lookup_spn_alias(krb5_context context, stru
                return DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
        }
 
-       service_dn = ldb_dn_new(tmp_ctx, ldb_ctx, "CN=Directory Service,CN=Windows NT,CN=Services,CN=Configuration");
-       if ( ! ldb_dn_add_base(service_dn, samdb_base_dn(ldb_ctx))) {
+       service_dn = ldb_dn_new(tmp_ctx, ldb_ctx, "CN=Directory Service,CN=Windows NT,CN=Services");
+       if ( ! ldb_dn_add_base(service_dn, samdb_config_dn(ldb_ctx))) {
                return DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
        }
-       service_dn_str = ldb_dn_linearize(tmp_ctx, service_dn);
+       service_dn_str = ldb_dn_alloc_linearized(tmp_ctx, service_dn);
        if ( ! service_dn_str) {
                return DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
        }
@@ -81,12 +109,15 @@ static enum drsuapi_DsNameStatus LDB_lookup_spn_alias(krb5_context context, stru
        ret = ldb_search(ldb_ctx, service_dn, LDB_SCOPE_BASE, "(objectClass=nTDSService)",
                         directory_attrs, &res);
 
-       if (ret != LDB_SUCCESS) {
+       if (ret != LDB_SUCCESS && ret != LDB_ERR_NO_SUCH_OBJECT) {
                DEBUG(1, ("ldb_search: dn: %s not found: %s", service_dn_str, ldb_errstring(ldb_ctx)));
                return DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
+       } else if (ret == LDB_ERR_NO_SUCH_OBJECT) {
+               DEBUG(1, ("ldb_search: dn: %s not found", service_dn_str));
+               return DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
        } else if (res->count != 1) {
                talloc_free(res);
-               DEBUG(1, ("ldb_search: dn: %s found %d times!", service_dn_str, res->count));
+               DEBUG(1, ("ldb_search: dn: %s not found", service_dn_str));
                return DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
        }
        talloc_steal(tmp_ctx, res);
@@ -152,7 +183,7 @@ static WERROR DsCrackNameSPNAlias(struct ldb_context *sam_ctx, TALLOC_CTX *mem_c
        WERROR wret;
        krb5_error_code ret;
        krb5_principal principal;
-       const char *service;
+       const char *service, *dns_name;
        char *new_service;
        char *new_princ;
        enum drsuapi_DsNameStatus namestatus;
@@ -175,19 +206,24 @@ static WERROR DsCrackNameSPNAlias(struct ldb_context *sam_ctx, TALLOC_CTX *mem_c
                return WERR_OK;
        }
        service = principal->name.name_string.val[0];
+       dns_name = principal->name.name_string.val[1];
        
        /* MAP it */
        namestatus = LDB_lookup_spn_alias(smb_krb5_context->krb5_context, 
                                          sam_ctx, mem_ctx, 
                                          service, &new_service);
        
-       if (namestatus != DRSUAPI_DS_NAME_STATUS_OK) {
-               info1->status = namestatus;
+       if (namestatus == DRSUAPI_DS_NAME_STATUS_NOT_FOUND) {
+               info1->status           = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY;
+               info1->dns_domain_name  = talloc_strdup(mem_ctx, dns_name);
+               if (!info1->dns_domain_name) {
+                       krb5_free_principal(smb_krb5_context->krb5_context, principal);
+                       return WERR_NOMEM;
+               }
                return WERR_OK;
-       }
-       
-       if (ret != 0) {
-               info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
+       } else if (namestatus != DRSUAPI_DS_NAME_STATUS_OK) {
+               info1->status = namestatus;
+               krb5_free_principal(smb_krb5_context->krb5_context, principal);
                return WERR_OK;
        }
        
@@ -203,15 +239,22 @@ static WERROR DsCrackNameSPNAlias(struct ldb_context *sam_ctx, TALLOC_CTX *mem_c
        ret = krb5_unparse_name_flags(smb_krb5_context->krb5_context, principal, 
                                      KRB5_PRINCIPAL_UNPARSE_NO_REALM, &new_princ);
 
-       krb5_free_principal(smb_krb5_context->krb5_context, principal);
-       
        if (ret) {
+               krb5_free_principal(smb_krb5_context->krb5_context, principal);
                return WERR_NOMEM;
        }
        
        wret = DsCrackNameOneName(sam_ctx, mem_ctx, format_flags, format_offered, format_desired,
                                  new_princ, info1);
        free(new_princ);
+       if (W_ERROR_IS_OK(wret) && (info1->status == DRSUAPI_DS_NAME_STATUS_NOT_FOUND)) {
+               info1->status           = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY;
+               info1->dns_domain_name  = talloc_strdup(mem_ctx, dns_name);
+               if (!info1->dns_domain_name) {
+                       wret = WERR_NOMEM;
+               }
+       }
+       krb5_free_principal(smb_krb5_context->krb5_context, principal);
        return wret;
 }
 
@@ -222,6 +265,7 @@ static WERROR DsCrackNameUPN(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
                             uint32_t format_flags, uint32_t format_offered, uint32_t format_desired,
                             const char *name, struct drsuapi_DsNameInfo1 *info1)
 {
+       int ldb_ret;
        WERROR status;
        const char *domain_filter = NULL;
        const char *result_filter = NULL;
@@ -229,7 +273,9 @@ static WERROR DsCrackNameUPN(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
        krb5_principal principal;
        char **realm;
        char *unparsed_name_short;
-
+       const char *domain_attrs[] = { NULL };
+       struct ldb_result *domain_res = NULL;
+       
        /* Prevent recursion */
        if (!name) {
                info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
@@ -243,12 +289,33 @@ static WERROR DsCrackNameUPN(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
                return WERR_OK;
        }
        
-       domain_filter = NULL;
        realm = krb5_princ_realm(smb_krb5_context->krb5_context, principal);
-       domain_filter = talloc_asprintf(mem_ctx, 
-                                       "(&(&(|(&(dnsRoot=%s)(nETBIOSName=*))(nETBIOSName=%s))(objectclass=crossRef))(ncName=*))",
-                                       ldb_binary_encode_string(mem_ctx, *realm), 
-                                       ldb_binary_encode_string(mem_ctx, *realm));
+
+       ldb_ret = ldb_search_exp_fmt(sam_ctx, mem_ctx, &domain_res, 
+                                    samdb_partitions_dn(sam_ctx, mem_ctx), 
+                                    LDB_SCOPE_ONELEVEL,
+                                    domain_attrs,
+                                    "(&(&(|(&(dnsRoot=%s)(nETBIOSName=*))(nETBIOSName=%s))(objectclass=crossRef))(ncName=*))",
+                                    ldb_binary_encode_string(mem_ctx, *realm), 
+                                    ldb_binary_encode_string(mem_ctx, *realm));
+
+       if (ldb_ret != LDB_SUCCESS) {
+               DEBUG(2, ("DsCrackNameUPN domain ref search failed: %s", ldb_errstring(sam_ctx)));
+               info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
+               return WERR_OK;
+       }
+       
+       switch (domain_res->count) {
+       case 1:
+               break;
+       case 0:
+               return dns_domain_from_principal(mem_ctx, smb_krb5_context, 
+                                                name, info1);
+       default:
+               info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE;
+               return WERR_OK;
+       }
+       
        ret = krb5_unparse_name_flags(smb_krb5_context->krb5_context, principal, 
                                      KRB5_PRINCIPAL_UNPARSE_NO_REALM, &unparsed_name_short);
        krb5_free_principal(smb_krb5_context->krb5_context, principal);
@@ -261,6 +328,9 @@ static WERROR DsCrackNameUPN(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
        /* This may need to be extended for more userPrincipalName variations */
        result_filter = talloc_asprintf(mem_ctx, "(&(objectClass=user)(samAccountName=%s))", 
                                        ldb_binary_encode_string(mem_ctx, unparsed_name_short));
+
+       domain_filter = talloc_asprintf(mem_ctx, "(distinguishedName=%s)", ldb_dn_get_linearized(domain_res->msgs[0]->dn));
+
        if (!result_filter || !domain_filter) {
                free(unparsed_name_short);
                return WERR_NOMEM;
@@ -271,6 +341,7 @@ static WERROR DsCrackNameUPN(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
                                      NULL, unparsed_name_short, domain_filter, result_filter, 
                                      info1);
        free(unparsed_name_short);
+
        return status;
 }
 
@@ -286,7 +357,10 @@ WERROR DsCrackNameOneName(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
        struct ldb_dn *name_dn = NULL;
 
        struct smb_krb5_context *smb_krb5_context;
-       ret = smb_krb5_init_context(mem_ctx, &smb_krb5_context);
+       ret = smb_krb5_init_context(mem_ctx, 
+                                   (struct event_context *)ldb_get_opaque(sam_ctx, "EventContext"), 
+                                   (struct loadparm_context *)ldb_get_opaque(sam_ctx, "loadparm"), 
+                                   &smb_krb5_context);
                                
        if (ret) {
                return WERR_NOMEM;
@@ -306,24 +380,58 @@ WERROR DsCrackNameOneName(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
 
        /* here we need to set the domain_filter and/or the result_filter */
        switch (format_offered) {
-       case DRSUAPI_DS_NAME_FORMAT_CANONICAL: {
-               char *str;
+       case DRSUAPI_DS_NAME_FORMAT_CANONICAL:
+       case DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX:
+       {
+               char *str, *s, *account;
+               
+               if (strlen(name) == 0) {
+                       info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
+                       return WERR_OK;
+               }
                
                str = talloc_strdup(mem_ctx, name);
                W_ERROR_HAVE_NO_MEMORY(str);
                
-               if (strlen(str) == 0 || str[strlen(str)-1] != '/') {
+               if (format_offered == DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX) {
+                       /* Look backwards for the \n, and replace it with / */
+                       s = strrchr(str, '\n');
+                       if (!s) {
+                               info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
+                               return WERR_OK;
+                       }
+                       s[0] = '/';
+               }
+
+               s = strchr(str, '/');
+               if (!s) {
+                       /* there must be at least one / */
                        info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
                        return WERR_OK;
                }
                
-               str[strlen(str)-1] = '\0';
-               
-               domain_filter = talloc_asprintf(mem_ctx, 
-                                               "(&(&(&(dnsRoot=%s)(objectclass=crossRef)))(nETBIOSName=*)(ncName=*))", 
-                                               ldb_binary_encode_string(mem_ctx, str));
+               s[0] = '\0';
+               s++;
+
+               domain_filter = talloc_asprintf(mem_ctx, "(&(objectClass=crossRef)(ncName=%s))", 
+                                               ldb_dn_get_linearized(samdb_dns_domain_to_dn(sam_ctx, mem_ctx, str)));
                W_ERROR_HAVE_NO_MEMORY(domain_filter);
-               
+
+               /* There may not be anything after the domain component (search for the domain itself) */
+               if (s[0]) {
+                       
+                       account = strrchr(s, '/');
+                       if (!account) {
+                               account = s;
+                       } else {
+                               account++;
+                       }
+                       account = ldb_binary_encode_string(mem_ctx, account);
+                       W_ERROR_HAVE_NO_MEMORY(account);
+                       result_filter = talloc_asprintf(mem_ctx, "(name=%s)",
+                                                       account);              
+                       W_ERROR_HAVE_NO_MEMORY(result_filter);
+               }
                break;
        }
        case DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT: {
@@ -452,32 +560,19 @@ WERROR DsCrackNameOneName(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
                krb5_principal principal;
                char *unparsed_name_short;
                char *service;
+               ret = krb5_parse_name(smb_krb5_context->krb5_context, name, &principal);
+               if (ret == 0 && principal->name.name_string.len < 2) {
+                       info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
+                       krb5_free_principal(smb_krb5_context->krb5_context, principal);
+                       return WERR_OK;
+               }
                ret = krb5_parse_name_flags(smb_krb5_context->krb5_context, name, 
                                            KRB5_PRINCIPAL_PARSE_NO_REALM, &principal);
                if (ret) {
-                       /* perhaps it's a principal with a realm, so return the right 'domain only' response */
-                       char **realm;
-                       ret = krb5_parse_name_flags(smb_krb5_context->krb5_context, name, 
-                                                   KRB5_PRINCIPAL_PARSE_MUST_REALM, &principal);
-                       if (ret) {
-                               info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
-                               return WERR_OK;
-                       }
-                               
-                       /* This isn't an allocation assignemnt, so it is free'ed with the krb5_free_principal */
-                       realm = krb5_princ_realm(smb_krb5_context->krb5_context, principal);
-
-                       info1->dns_domain_name  = talloc_strdup(info1, *realm);
                        krb5_free_principal(smb_krb5_context->krb5_context, principal);
-       
-                       W_ERROR_HAVE_NO_MEMORY(info1->dns_domain_name);
 
-                       info1->status = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY;
-                       return WERR_OK;
-                       
-               } else if (principal->name.name_string.len < 2) {
-                       info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
-                       return WERR_OK;
+                       return dns_domain_from_principal(mem_ctx, smb_krb5_context,
+                                                        name, info1);
                }
 
                domain_filter = NULL;
@@ -582,11 +677,14 @@ static WERROR DsCrackNameOneFilter(struct ldb_context *sam_ctx, TALLOC_CTX *mem_
                                   struct drsuapi_DsNameInfo1 *info1)
 {
        int ldb_ret;
-       struct ldb_message **domain_res = NULL;
+       struct ldb_result *domain_res = NULL;
        const char * const *domain_attrs;
        const char * const *result_attrs;
        struct ldb_message **result_res = NULL;
-       struct ldb_dn *result_basedn;
+       struct ldb_message *result = NULL;
+       struct ldb_dn *result_basedn = NULL;
+       int i;
+       char *p;
        struct ldb_dn *partitions_basedn = samdb_partitions_dn(sam_ctx, mem_ctx);
 
        const char * const _domain_attrs_1779[] = { "ncName", "dnsRoot", NULL};
@@ -596,7 +694,7 @@ static WERROR DsCrackNameOneFilter(struct ldb_context *sam_ctx, TALLOC_CTX *mem_
        const char * const _result_attrs_canonical[] = { "canonicalName", NULL };
 
        const char * const _domain_attrs_nt4[] = { "ncName", "dnsRoot", "nETBIOSName", NULL};
-       const char * const _result_attrs_nt4[] = { "sAMAccountName", "objectSid", NULL};
+       const char * const _result_attrs_nt4[] = { "sAMAccountName", "objectSid", "objectClass", NULL};
                
        const char * const _domain_attrs_guid[] = { "ncName", "dnsRoot", NULL};
        const char * const _result_attrs_guid[] = { "objectGUID", NULL};
@@ -604,6 +702,9 @@ static WERROR DsCrackNameOneFilter(struct ldb_context *sam_ctx, TALLOC_CTX *mem_
        const char * const _domain_attrs_display[] = { "ncName", "dnsRoot", NULL};
        const char * const _result_attrs_display[] = { "displayName", "samAccountName", NULL};
 
+       const char * const _domain_attrs_none[] = { "ncName", "dnsRoot" , NULL};
+       const char * const _result_attrs_none[] = { NULL};
+
        /* here we need to set the attrs lists for domain and result lookups */
        switch (format_desired) {
        case DRSUAPI_DS_NAME_FORMAT_FQDN_1779:
@@ -628,52 +729,125 @@ static WERROR DsCrackNameOneFilter(struct ldb_context *sam_ctx, TALLOC_CTX *mem_
                result_attrs = _result_attrs_display;
                break;
        default:
-               return WERR_OK;
+               domain_attrs = _domain_attrs_none;
+               result_attrs = _result_attrs_none;
+               break;
        }
 
        if (domain_filter) {
                /* if we have a domain_filter look it up and set the result_basedn and the dns_domain_name */
-               ldb_ret = gendb_search(sam_ctx, mem_ctx, partitions_basedn, &domain_res, domain_attrs,
-                                      "%s", domain_filter);
-       } else {
-               ldb_ret = gendb_search(sam_ctx, mem_ctx, partitions_basedn, &domain_res, domain_attrs,
-                                      "(ncName=%s)", ldb_dn_linearize(mem_ctx, samdb_base_dn(sam_ctx)));
-       } 
+               ldb_ret = ldb_search_exp_fmt(sam_ctx, mem_ctx, &domain_res, 
+                                            partitions_basedn,
+                                            LDB_SCOPE_ONELEVEL,
+                                            domain_attrs,
+                                            "%s", domain_filter);
+               
+               if (ldb_ret != LDB_SUCCESS) {
+                       DEBUG(2, ("DsCrackNameOneFilter domain ref search failed: %s", ldb_errstring(sam_ctx)));
+                       info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
+                       return WERR_OK;
+               }
+               
+               switch (domain_res->count) {
+               case 1:
+                       break;
+               case 0:
+                       info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
+                       return WERR_OK;
+               default:
+                       info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE;
+                       return WERR_OK;
+               }
 
-       switch (ldb_ret) {
-       case 1:
-               break;
-       case 0:
-               info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
-               return WERR_OK;
-       case -1:
+               info1->dns_domain_name  = samdb_result_string(domain_res->msgs[0], "dnsRoot", NULL);
+               W_ERROR_HAVE_NO_MEMORY(info1->dns_domain_name);
+               info1->status           = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY;
+       } else {
+               info1->dns_domain_name  = NULL;
                info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
-               return WERR_OK;
-       default:
-               info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE;
-               return WERR_OK;
        }
 
-       info1->dns_domain_name  = samdb_result_string(domain_res[0], "dnsRoot", NULL);
-       W_ERROR_HAVE_NO_MEMORY(info1->dns_domain_name);
-       info1->status           = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY;
-
        if (result_filter) {
-               result_basedn = samdb_result_dn(sam_ctx, mem_ctx, domain_res[0], "ncName", NULL);
-               
-               ldb_ret = gendb_search(sam_ctx, mem_ctx, result_basedn, &result_res,
-                                      result_attrs, "%s", result_filter);
+               int ret;
+               struct ldb_result *res;
+               if (domain_res) {
+                       result_basedn = samdb_result_dn(sam_ctx, mem_ctx, domain_res->msgs[0], "ncName", NULL);
+                       
+                       ret = ldb_search_exp_fmt(sam_ctx, mem_ctx, &res, 
+                                                result_basedn, LDB_SCOPE_SUBTREE, 
+                                                result_attrs, "%s", result_filter);
+                       if (ret != LDB_SUCCESS) {
+                               talloc_free(result_res);
+                               info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
+                               return WERR_OK;
+                       }
+                       ldb_ret = res->count;
+                       result_res = res->msgs;
+               } else {
+                       /* search with the 'phantom root' flag */
+                       struct ldb_request *req;
+
+                       res = talloc_zero(mem_ctx, struct ldb_result);
+                       W_ERROR_HAVE_NO_MEMORY(res);
+                       
+                       ret = ldb_build_search_req(&req, sam_ctx, mem_ctx,
+                                                  ldb_get_root_basedn(sam_ctx),
+                                                  LDB_SCOPE_SUBTREE,
+                                                  result_filter,
+                                                  result_attrs,
+                                                  NULL,
+                                                  res,
+                                                  ldb_search_default_callback);
+                       if (ret == LDB_SUCCESS) {
+                               struct ldb_search_options_control *search_options;
+                               search_options = talloc(req, struct ldb_search_options_control);
+                               W_ERROR_HAVE_NO_MEMORY(search_options);
+                               search_options->search_options = LDB_SEARCH_OPTION_PHANTOM_ROOT;
+
+                               ret = ldb_request_add_control(req, LDB_CONTROL_SEARCH_OPTIONS_OID, false, search_options);
+                       }
+                       if (ret != LDB_SUCCESS) {
+                               talloc_free(res);
+                               info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
+                               return WERR_OK;
+                       }
+                       
+                       ldb_set_timeout(sam_ctx, req, 0); /* use default timeout */
+                       
+                       ret = ldb_request(sam_ctx, req);
+                       
+                       if (ret == LDB_SUCCESS) {
+                               ret = ldb_wait(req->handle, LDB_WAIT_ALL);
+                       }
+                       
+                       talloc_free(req);
+
+                       if (ret != LDB_SUCCESS) {
+                               DEBUG(2, ("DsCrackNameOneFilter phantom root search failed: %s", 
+                                         ldb_errstring(sam_ctx)));
+                               info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
+                               return WERR_OK;
+                       }
+                       ldb_ret = res->count;
+                       result_res = res->msgs;
+               }
        } else if (format_offered == DRSUAPI_DS_NAME_FORMAT_FQDN_1779) {
                ldb_ret = gendb_search_dn(sam_ctx, mem_ctx, name_dn, &result_res,
                                          result_attrs);
-       } else {
-               name_dn = samdb_result_dn(sam_ctx, mem_ctx, domain_res[0], "ncName", NULL);
+       } else if (domain_res) {
+               name_dn = samdb_result_dn(sam_ctx, mem_ctx, domain_res->msgs[0], "ncName", NULL);
                ldb_ret = gendb_search_dn(sam_ctx, mem_ctx, name_dn, &result_res,
                                          result_attrs);
+       } else {
+               /* Can't happen */
+               DEBUG(0, ("LOGIC ERROR: DsCrackNameOneFilter domain ref search not availible: This can't happen..."));
+               info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
+               return WERR_OK;
        }
 
        switch (ldb_ret) {
        case 1:
+               result = result_res[0];
                break;
        case 0:
                switch (format_offered) {
@@ -691,24 +865,59 @@ static WERROR DsCrackNameOneFilter(struct ldb_context *sam_ctx, TALLOC_CTX *mem_
                info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
                return WERR_OK;
        case -1:
+               DEBUG(2, ("DsCrackNameOneFilter result search failed: %s", ldb_errstring(sam_ctx)));
                info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
                return WERR_OK;
        default:
-               info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE;
-               return WERR_OK;
+               switch (format_offered) {
+               case DRSUAPI_DS_NAME_FORMAT_CANONICAL:
+               case DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX:
+               {
+                       const char *canonical_name = NULL; /* Not required, but we get warnings... */
+                       /* We may need to manually filter further */
+                       for (i = 0; i < ldb_ret; i++) {
+                               switch (format_offered) {
+                               case DRSUAPI_DS_NAME_FORMAT_CANONICAL:
+                                       canonical_name = ldb_dn_canonical_string(mem_ctx, result_res[i]->dn);
+                                       break;
+                               case DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX:
+                                       canonical_name = ldb_dn_canonical_ex_string(mem_ctx, result_res[i]->dn);
+                                       break;
+                               }
+                               if (strcasecmp_m(canonical_name, name) == 0) {
+                                       result = result_res[i];
+                                       break;
+                               }
+                       }
+                       if (!result) {
+                               info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
+                               return WERR_OK;
+                       }
+               }
+               default:
+                       info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE;
+                       return WERR_OK;
+               }
        }
 
-       /* here we can use result_res[0] and domain_res[0] */
+       info1->dns_domain_name = ldb_dn_canonical_string(mem_ctx, result->dn);
+       W_ERROR_HAVE_NO_MEMORY(info1->dns_domain_name);
+       p = strchr(info1->dns_domain_name, '/');
+       if (p) {
+               p[0] = '\0';
+       }
+       
+       /* here we can use result and domain_res[0] */
        switch (format_desired) {
        case DRSUAPI_DS_NAME_FORMAT_FQDN_1779: {
-               info1->result_name      = ldb_dn_linearize(mem_ctx, result_res[0]->dn);
+               info1->result_name      = ldb_dn_alloc_linearized(mem_ctx, result->dn);
                W_ERROR_HAVE_NO_MEMORY(info1->result_name);
 
                info1->status           = DRSUAPI_DS_NAME_STATUS_OK;
                return WERR_OK;
        }
        case DRSUAPI_DS_NAME_FORMAT_CANONICAL: {
-               info1->result_name      = samdb_result_string(result_res[0], "canonicalName", NULL);
+               info1->result_name      = samdb_result_string(result, "canonicalName", NULL);
                info1->status           = DRSUAPI_DS_NAME_STATUS_OK;
                return WERR_OK;
        }
@@ -717,53 +926,104 @@ static WERROR DsCrackNameOneFilter(struct ldb_context *sam_ctx, TALLOC_CTX *mem_
                return DsCrackNameOneSyntactical(mem_ctx, 
                                                 DRSUAPI_DS_NAME_FORMAT_FQDN_1779, 
                                                 DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX,
-                                                result_res[0]->dn, name, info1);
+                                                result->dn, name, info1);
        }
        case DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT: {
-               const struct dom_sid *sid = samdb_result_dom_sid(mem_ctx, result_res[0], "objectSid");
+
+               const struct dom_sid *sid = samdb_result_dom_sid(mem_ctx, result, "objectSid");
                const char *_acc = "", *_dom = "";
                
-               if (!sid || (sid->num_auths < 4) || (sid->num_auths > 5)) {
-                       info1->status = DRSUAPI_DS_NAME_STATUS_NO_MAPPING;
-                       return WERR_OK;
-               }
+               if (samdb_find_attribute(sam_ctx, result, "objectClass", "domain")) {
 
-               if (sid->num_auths == 4) {
-                       ldb_ret = gendb_search(sam_ctx, mem_ctx, partitions_basedn, &domain_res, domain_attrs,
-                                              "(ncName=%s)", ldb_dn_linearize(mem_ctx, result_res[0]->dn));
-                       if (ldb_ret != 1) {
-                               info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
-                               return WERR_OK;
-                       }
-                       _dom = samdb_result_string(domain_res[0], "nETBIOSName", NULL);
-                       W_ERROR_HAVE_NO_MEMORY(_dom);
-               
-               } else if (sid->num_auths == 5) {
-                       const char *attrs[] = { NULL };
-                       struct ldb_message **domain_res2;
-                       struct dom_sid *dom_sid = dom_sid_dup(mem_ctx, sid);
-                       if (!dom_sid) {
+                       ldb_ret = ldb_search_exp_fmt(sam_ctx, mem_ctx, &domain_res, 
+                                                    partitions_basedn,
+                                                    LDB_SCOPE_ONELEVEL,
+                                                    domain_attrs,
+                                                    "(ncName=%s)", ldb_dn_get_linearized(result->dn));
+                       
+                       if (ldb_ret != LDB_SUCCESS) {
+                               DEBUG(2, ("DsCrackNameOneFilter domain ref search failed: %s", ldb_errstring(sam_ctx)));
+                               info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
                                return WERR_OK;
                        }
-                       dom_sid->num_auths--;
-                       ldb_ret = gendb_search(sam_ctx, mem_ctx, NULL, &domain_res, attrs,
-                                              "(&(objectSid=%s)(objectClass=domain))", ldap_encode_ndr_dom_sid(mem_ctx, dom_sid));
-                       if (ldb_ret != 1) {
+                       
+                       switch (domain_res->count) {
+                       case 1:
+                               break;
+                       case 0:
                                info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
                                return WERR_OK;
-                       }
-                       ldb_ret = gendb_search(sam_ctx, mem_ctx, partitions_basedn, &domain_res2, domain_attrs,
-                                              "(ncName=%s)", ldb_dn_linearize(mem_ctx, domain_res[0]->dn));
-                       if (ldb_ret != 1) {
-                               info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
+                       default:
+                               info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE;
                                return WERR_OK;
                        }
-                       
-                       _dom = samdb_result_string(domain_res2[0], "nETBIOSName", NULL);
+                       _dom = samdb_result_string(domain_res->msgs[0], "nETBIOSName", NULL);
                        W_ERROR_HAVE_NO_MEMORY(_dom);
-
-                       _acc = samdb_result_string(result_res[0], "sAMAccountName", NULL);
-                       W_ERROR_HAVE_NO_MEMORY(_acc);
+               } else {
+                       _acc = samdb_result_string(result, "sAMAccountName", NULL);
+                       if (!_acc) {
+                               info1->status = DRSUAPI_DS_NAME_STATUS_NO_MAPPING;
+                               return WERR_OK;
+                       }
+                       if (dom_sid_in_domain(dom_sid_parse_talloc(mem_ctx, SID_BUILTIN), sid)) {
+                               _dom = "BUILTIN";
+                       } else {
+                               const char *attrs[] = { NULL };
+                               struct ldb_result *domain_res2;
+                               struct dom_sid *dom_sid = dom_sid_dup(mem_ctx, sid);
+                               if (!dom_sid) {
+                                       return WERR_OK;
+                               }
+                               dom_sid->num_auths--;
+                               ldb_ret = ldb_search_exp_fmt(sam_ctx, mem_ctx, &domain_res, 
+                                                            NULL,
+                                                            LDB_SCOPE_BASE,
+                                                            attrs,
+                                                            "(&(objectSid=%s)(objectClass=domain))", 
+                                                            ldap_encode_ndr_dom_sid(mem_ctx, dom_sid));
+                               
+                               if (ldb_ret != LDB_SUCCESS) {
+                                       DEBUG(2, ("DsCrackNameOneFilter domain search failed: %s", ldb_errstring(sam_ctx)));
+                                       info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
+                                       return WERR_OK;
+                               }
+                               
+                               switch (domain_res->count) {
+                               case 1:
+                                       break;
+                               case 0:
+                                       info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
+                                       return WERR_OK;
+                               default:
+                                       info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE;
+                                       return WERR_OK;
+                               }
+
+                               ldb_ret = ldb_search_exp_fmt(sam_ctx, mem_ctx, &domain_res2, 
+                                                            partitions_basedn,
+                                                            LDB_SCOPE_ONELEVEL,
+                                                            domain_attrs,
+                                                            "(ncName=%s)", ldb_dn_get_linearized(domain_res->msgs[0]->dn));
+                               
+                               if (ldb_ret != LDB_SUCCESS) {
+                                       DEBUG(2, ("DsCrackNameOneFilter domain ref search failed: %s", ldb_errstring(sam_ctx)));
+                                       info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
+                                       return WERR_OK;
+                               }
+                               
+                               switch (domain_res2->count) {
+                               case 1:
+                                       break;
+                               case 0:
+                                       info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
+                                       return WERR_OK;
+                               default:
+                                       info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE;
+                                       return WERR_OK;
+                               }
+                               _dom = samdb_result_string(domain_res2->msgs[0], "nETBIOSName", NULL);
+                               W_ERROR_HAVE_NO_MEMORY(_dom);
+                       }
                }
 
                info1->result_name      = talloc_asprintf(mem_ctx, "%s\\%s", _dom, _acc);
@@ -775,7 +1035,7 @@ static WERROR DsCrackNameOneFilter(struct ldb_context *sam_ctx, TALLOC_CTX *mem_
        case DRSUAPI_DS_NAME_FORMAT_GUID: {
                struct GUID guid;
                
-               guid = samdb_result_guid(result_res[0], "objectGUID");
+               guid = samdb_result_guid(result, "objectGUID");
                
                info1->result_name      = GUID_string2(mem_ctx, &guid);
                W_ERROR_HAVE_NO_MEMORY(info1->result_name);
@@ -784,9 +1044,9 @@ static WERROR DsCrackNameOneFilter(struct ldb_context *sam_ctx, TALLOC_CTX *mem_
                return WERR_OK;
        }
        case DRSUAPI_DS_NAME_FORMAT_DISPLAY: {
-               info1->result_name      = samdb_result_string(result_res[0], "displayName", NULL);
+               info1->result_name      = samdb_result_string(result, "displayName", NULL);
                if (!info1->result_name) {
-                       info1->result_name      = samdb_result_string(result_res[0], "sAMAccountName", NULL);
+                       info1->result_name      = samdb_result_string(result, "sAMAccountName", NULL);
                } 
                if (!info1->result_name) {
                        info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
@@ -795,11 +1055,19 @@ static WERROR DsCrackNameOneFilter(struct ldb_context *sam_ctx, TALLOC_CTX *mem_
                }
                return WERR_OK;
        }
+       case DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL: {
+               info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE;
+               return WERR_OK;
+       }
+       case DRSUAPI_DS_NAME_FORMAT_DNS_DOMAIN: 
+       case DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY: {
+               info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
+               return WERR_OK;
+       }
        default:
+               info1->status = DRSUAPI_DS_NAME_STATUS_NO_MAPPING;
                return WERR_OK;
        }
-       
-       return WERR_INVALID_PARAM;
 }
 
 /* Given a user Principal Name (such as foo@bar.com),
@@ -928,9 +1196,11 @@ NTSTATUS crack_service_principal_name(struct ldb_context *sam_ctx,
        
 }
 
-NTSTATUS crack_dn_to_nt4_name(TALLOC_CTX *mem_ctx, 
-                             const char *dn, 
-                             const char **nt4_domain, const char **nt4_account)
+NTSTATUS crack_name_to_nt4_name(TALLOC_CTX *mem_ctx, 
+                               struct loadparm_context *lp_ctx,
+                               uint32_t format_offered,
+                               const char *name, 
+                               const char **nt4_domain, const char **nt4_account)
 {
        WERROR werr;
        struct drsuapi_DsNameInfo1 info1;
@@ -938,21 +1208,21 @@ NTSTATUS crack_dn_to_nt4_name(TALLOC_CTX *mem_ctx,
        char *p;
 
        /* Handle anonymous bind */
-       if (!dn || !*dn) {
+       if (!name || !*name) {
                *nt4_domain = "";
                *nt4_account = "";
                return NT_STATUS_OK;
        }
 
-       ldb = samdb_connect(mem_ctx, system_session(mem_ctx));
+       ldb = samdb_connect(mem_ctx, lp_ctx, system_session(mem_ctx, lp_ctx));
        if (ldb == NULL) {
                return NT_STATUS_INTERNAL_DB_CORRUPTION;
        }
 
        werr = DsCrackNameOneName(ldb, mem_ctx, 0,
-                                 DRSUAPI_DS_NAME_FORMAT_FQDN_1779
+                                 format_offered
                                  DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT,
-                                 dn,
+                                 name,
                                  &info1);
        if (!W_ERROR_IS_OK(werr)) {
                return werror_to_ntstatus(werr);
@@ -986,5 +1256,32 @@ NTSTATUS crack_dn_to_nt4_name(TALLOC_CTX *mem_ctx,
        }
 
        return NT_STATUS_OK;
-       
+}
+
+NTSTATUS crack_auto_name_to_nt4_name(TALLOC_CTX *mem_ctx,
+                                    struct loadparm_context *lp_ctx,
+                                    const char *name,
+                                    const char **nt4_domain,
+                                    const char **nt4_account)
+{
+       uint32_t format_offered = DRSUAPI_DS_NAME_FORMAT_UKNOWN;
+
+       /* Handle anonymous bind */
+       if (!name || !*name) {
+               *nt4_domain = "";
+               *nt4_account = "";
+               return NT_STATUS_OK;
+       }
+
+       if (strchr_m(name, '=')) {
+               format_offered = DRSUAPI_DS_NAME_FORMAT_FQDN_1779;
+       } else if (strchr_m(name, '@')) {
+               format_offered = DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL;
+       } else if (strchr_m(name, '\\')) {
+               format_offered = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT;
+       } else if (strchr_m(name, '/')) {
+               format_offered = DRSUAPI_DS_NAME_FORMAT_CANONICAL;
+       }
+
+       return crack_name_to_nt4_name(mem_ctx, lp_ctx, format_offered, name, nt4_domain, nt4_account);
 }