drs-cracknames: When cracking NT4 names we should just look at netbios for the match
[metze/samba/wip.git] / source4 / dsdb / samdb / cracknames.c
index d4b122056b5ee344afb8bd5cebbe2fea0f47a41e..628546e959eb0deb063192d6546f98e19e886215 100644 (file)
@@ -1,11 +1,12 @@
 /* 
    Unix SMB/CIFS implementation.
 
-   endpoint server for the drsuapi pipe
+   crachnames implementation for the drsuapi pipe
    DsCrackNames()
 
    Copyright (C) Stefan Metzmacher 2004
    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
+   Copyright (C) Matthieu Patou <mat@matws.net> 2012
 
    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
 #include "includes.h"
 #include "librpc/gen_ndr/drsuapi.h"
 #include "lib/events/events.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 <ldb.h>
+#include <ldb_errors.h>
 #include "auth/kerberos/kerberos.h"
 #include "libcli/ldap/ldap_ndr.h"
 #include "libcli/security/security.h"
 #include "auth/auth.h"
+#include "../lib/util/util_ldb.h"
 #include "dsdb/samdb/samdb.h"
 #include "dsdb/common/util.h"
 #include "param/param.h"
@@ -42,7 +42,7 @@ static WERROR DsCrackNameOneFilter(struct ldb_context *sam_ctx, TALLOC_CTX *mem_
                                   enum drsuapi_DsNameFormat format_desired,
                                   struct ldb_dn *name_dn, const char *name, 
                                   const char *domain_filter, const char *result_filter, 
-                                  struct drsuapi_DsNameInfo1 *info1);
+                                  struct drsuapi_DsNameInfo1 *info1, int scope, struct ldb_dn *search_dn);
 static WERROR DsCrackNameOneSyntactical(TALLOC_CTX *mem_ctx,
                                        enum drsuapi_DsNameFormat format_offered,
                                        enum drsuapi_DsNameFormat format_desired,
@@ -65,7 +65,7 @@ static WERROR dns_domain_from_principal(TALLOC_CTX *mem_ctx, struct smb_krb5_con
        }
 
        /* This isn't an allocation assignemnt, so it is free'ed with the krb5_free_principal */
-       realm = krb5_principal_get_realm(smb_krb5_context->krb5_context, principal);
+       realm = smb_krb5_principal_get_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);
@@ -112,20 +112,20 @@ static enum drsuapi_DsNameStatus LDB_lookup_spn_alias(krb5_context context, stru
                         directory_attrs, "(objectClass=nTDSService)");
 
        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)));
+               DEBUG(1, ("ldb_search: dn: %s not found: %s\n", 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));
+               DEBUG(1, ("ldb_search: dn: %s not found\n", service_dn_str));
                return DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
        } else if (res->count != 1) {
                talloc_free(res);
-               DEBUG(1, ("ldb_search: dn: %s not found", service_dn_str));
+               DEBUG(1, ("ldb_search: dn: %s not found\n", service_dn_str));
                return DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
        }
 
        spnmappings = ldb_msg_find_element(res->msgs[0], "sPNMappings");
        if (!spnmappings || spnmappings->num_values == 0) {
-               DEBUG(1, ("ldb_search: dn: %s no sPNMappings attribute", service_dn_str));
+               DEBUG(1, ("ldb_search: dn: %s no sPNMappings attribute\n", service_dn_str));
                talloc_free(tmp_ctx);
                return DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
        }
@@ -185,6 +185,7 @@ static WERROR DsCrackNameSPNAlias(struct ldb_context *sam_ctx, TALLOC_CTX *mem_c
        WERROR wret;
        krb5_error_code ret;
        krb5_principal principal;
+       const krb5_data *component;
        const char *service, *dns_name;
        char *new_service;
        char *new_princ;
@@ -194,7 +195,7 @@ static WERROR DsCrackNameSPNAlias(struct ldb_context *sam_ctx, TALLOC_CTX *mem_c
        ret = krb5_parse_name_flags(smb_krb5_context->krb5_context, 
                                    name, KRB5_PRINCIPAL_PARSE_NO_REALM, &principal);
        if (ret) {
-               DEBUG(2, ("Could not parse principal: %s: %s",
+               DEBUG(2, ("Could not parse principal: %s: %s\n",
                          name, smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
                                                           ret, mem_ctx)));
                return WERR_NOMEM;
@@ -203,12 +204,17 @@ static WERROR DsCrackNameSPNAlias(struct ldb_context *sam_ctx, TALLOC_CTX *mem_c
        /* grab cifs/, http/ etc */
 
        /* This is checked for in callers, but be safe */
-       if (principal->name.name_string.len < 2) {
+       if (krb5_princ_size(smb_krb5_context->krb5_context, principal) < 2) {
                info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
+               krb5_free_principal(smb_krb5_context->krb5_context, principal);
                return WERR_OK;
        }
-       service = principal->name.name_string.val[0];
-       dns_name = principal->name.name_string.val[1];
+       component = krb5_princ_component(smb_krb5_context->krb5_context,
+                                        principal, 0);
+       service = (const char *)component->data;
+       component = krb5_princ_component(smb_krb5_context->krb5_context,
+                                        principal, 1);
+       dns_name = (const char *)component->data;
 
        /* MAP it */
        namestatus = LDB_lookup_spn_alias(smb_krb5_context->krb5_context, 
@@ -216,39 +222,30 @@ static WERROR DsCrackNameSPNAlias(struct ldb_context *sam_ctx, TALLOC_CTX *mem_c
                                          service, &new_service);
 
        if (namestatus == DRSUAPI_DS_NAME_STATUS_NOT_FOUND) {
+               wret = WERR_OK;
                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;
+                       wret = WERR_NOMEM;
                }
-               return WERR_OK;
+               krb5_free_principal(smb_krb5_context->krb5_context, principal);
+               return wret;
        } else if (namestatus != DRSUAPI_DS_NAME_STATUS_OK) {
                info1->status = namestatus;
                krb5_free_principal(smb_krb5_context->krb5_context, principal);
                return WERR_OK;
        }
 
-       /* ooh, very nasty playing around in the Principal... */
-       free(principal->name.name_string.val[0]);
-       principal->name.name_string.val[0] = strdup(new_service);
-       if (!principal->name.name_string.val[0]) {
-               krb5_free_principal(smb_krb5_context->krb5_context, principal);
-               return WERR_NOMEM;
-       }
-
        /* reform principal */
-       ret = krb5_unparse_name_flags(smb_krb5_context->krb5_context, principal, 
-                                     KRB5_PRINCIPAL_UNPARSE_NO_REALM, &new_princ);
-
-       if (ret) {
+       new_princ = talloc_asprintf(mem_ctx, "%s/%s", new_service, dns_name);
+       if (!new_princ) {
                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);
+       talloc_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);
@@ -292,19 +289,23 @@ static WERROR DsCrackNameUPN(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
                return WERR_OK;
        }
 
-       realm = krb5_principal_get_realm(smb_krb5_context->krb5_context, principal);
+       realm = smb_krb5_principal_get_realm(smb_krb5_context->krb5_context,
+                                            principal);
 
        ldb_ret = ldb_search(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));
+                            samdb_partitions_dn(sam_ctx, mem_ctx),
+                            LDB_SCOPE_ONELEVEL,
+                            domain_attrs,
+                            "(&(objectClass=crossRef)(|(dnsRoot=%s)(netbiosName=%s))(systemFlags:%s:=%u))",
+                            ldb_binary_encode_string(mem_ctx, realm),
+                            ldb_binary_encode_string(mem_ctx, realm),
+                            LDB_OID_COMPARATOR_AND,
+                            SYSTEM_FLAG_CR_NTDS_DOMAIN);
 
        if (ldb_ret != LDB_SUCCESS) {
-               DEBUG(2, ("DsCrackNameUPN domain ref search failed: %s", ldb_errstring(sam_ctx)));
+               DEBUG(2, ("DsCrackNameUPN domain ref search failed: %s\n", ldb_errstring(sam_ctx)));
                info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
+               krb5_free_principal(smb_krb5_context->krb5_context, principal);
                return WERR_OK;
        }
 
@@ -312,10 +313,12 @@ static WERROR DsCrackNameUPN(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
        case 1:
                break;
        case 0:
+               krb5_free_principal(smb_krb5_context->krb5_context, principal);
                return dns_domain_from_principal(mem_ctx, smb_krb5_context, 
                                                 name, info1);
        default:
                info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE;
+               krb5_free_principal(smb_krb5_context->krb5_context, principal);
                return WERR_OK;
        }
 
@@ -342,12 +345,102 @@ static WERROR DsCrackNameUPN(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
                                      smb_krb5_context, 
                                      format_flags, format_offered, format_desired, 
                                      NULL, unparsed_name_short, domain_filter, result_filter, 
-                                     info1);
+                                     info1, LDB_SCOPE_SUBTREE, NULL);
        free(unparsed_name_short);
 
        return status;
 }
 
+/*
+ * This function will workout the filtering parameter in order to be able to do
+ * the adapted search when the incomming format is format_functional.
+ * This boils down to defining the search_dn (passed as pointer to ldb_dn *) and the
+ * ldap filter request.
+ * Main input parameters are:
+ * * name, which is the portion of the functional name after the
+ * first '/'.
+ * * domain_filter, which is a ldap search filter used to find the NC DN given the
+ * function name to crack.
+ */
+static WERROR get_format_functional_filtering_param(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
+                       char *name, struct drsuapi_DsNameInfo1 *info1,
+                       struct ldb_dn **psearch_dn, const char *domain_filter, const char **presult_filter)
+{
+       struct ldb_result *domain_res = NULL;
+       const char * const domain_attrs[] = {"ncName", NULL};
+       struct ldb_dn *partitions_basedn = samdb_partitions_dn(sam_ctx, mem_ctx);
+       int ldb_ret;
+       char *account,  *s, *result_filter = NULL;
+       struct ldb_dn *search_dn = NULL;
+
+       *psearch_dn = NULL;
+       *presult_filter = NULL;
+
+       ldb_ret = ldb_search(sam_ctx, mem_ctx, &domain_res,
+                               partitions_basedn,
+                               LDB_SCOPE_ONELEVEL,
+                               domain_attrs,
+                               "%s", domain_filter);
+
+       if (ldb_ret != LDB_SUCCESS) {
+               DEBUG(2, ("DsCrackNameOne domain ref search failed: %s\n", ldb_errstring(sam_ctx)));
+               info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
+               return WERR_FOOBAR;
+       }
+
+       if (domain_res->count == 1) {
+               struct ldb_dn *tmp_dn = samdb_result_dn(sam_ctx, mem_ctx, domain_res->msgs[0], "ncName", NULL);
+               const char * const name_attrs[] = {"name", NULL};
+
+               account = name;
+               s = strchr(account, '/');
+               talloc_free(domain_res);
+               while(s) {
+                       s[0] = '\0';
+                       s++;
+
+                       ldb_ret = ldb_search(sam_ctx, mem_ctx, &domain_res,
+                                               tmp_dn,
+                                               LDB_SCOPE_ONELEVEL,
+                                               name_attrs,
+                                               "name=%s", account);
+
+                       if (ldb_ret != LDB_SUCCESS) {
+                               DEBUG(2, ("DsCrackNameOne domain ref search failed: %s\n", ldb_errstring(sam_ctx)));
+                               info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
+                               return WERR_OK;
+                       }
+                       talloc_free(tmp_dn);
+                       switch (domain_res->count) {
+                       case 1:
+                               break;
+                       case 0:
+                               talloc_free(domain_res);
+                               info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
+                               return WERR_OK;
+                       default:
+                               talloc_free(domain_res);
+                               info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE;
+                               return WERR_OK;
+                       }
+
+                       tmp_dn = talloc_steal(mem_ctx, domain_res->msgs[0]->dn);
+                       talloc_free(domain_res);
+                       search_dn = tmp_dn;
+                       account = s;
+                       s = strchr(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);
+       }
+       *psearch_dn = search_dn;
+       *presult_filter = result_filter;
+       return WERR_OK;
+}
+
 /* Crack a single 'name', from format_offered into format_desired, returning the result in info1 */
 
 WERROR DsCrackNameOneName(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
@@ -359,8 +452,10 @@ WERROR DsCrackNameOneName(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
        const char *domain_filter = NULL;
        const char *result_filter = NULL;
        struct ldb_dn *name_dn = NULL;
+       struct ldb_dn *search_dn = NULL;
 
        struct smb_krb5_context *smb_krb5_context = NULL;
+       int scope = LDB_SCOPE_SUBTREE;
 
        info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
        info1->dns_domain_name = NULL;
@@ -393,7 +488,10 @@ WERROR DsCrackNameOneName(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
                        if (!W_ERROR_IS_OK(werr)) {
                                return werr;
                        }
-                       if (info1->status != DRSUAPI_DS_NAME_STATUS_NOT_FOUND) {
+                       if (info1->status != DRSUAPI_DS_NAME_STATUS_NOT_FOUND &&
+                           (formats[i] != DRSUAPI_DS_NAME_FORMAT_CANONICAL ||
+                            info1->status != DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR))
+                       {
                                return werr;
                        }
                }
@@ -404,6 +502,7 @@ WERROR DsCrackNameOneName(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
        case DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX:
        {
                char *str, *s, *account;
+               scope = LDB_SCOPE_ONELEVEL;
 
                if (strlen(name) == 0) {
                        info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
@@ -433,31 +532,33 @@ WERROR DsCrackNameOneName(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
                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)));
+               domain_filter = talloc_asprintf(mem_ctx, "(&(objectClass=crossRef)(dnsRoot=%s)(systemFlags:%s:=%u))",
+                                               ldb_binary_encode_string(mem_ctx, str),
+                                               LDB_OID_COMPARATOR_AND,
+                                               SYSTEM_FLAG_CR_NTDS_DOMAIN);
                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 = s;
+               if (account && *account) {
+                       WERROR werr = get_format_functional_filtering_param(sam_ctx,
+                                                                               mem_ctx,
+                                                                               account,
+                                                                               info1,
+                                                                               &search_dn,
+                                                                               domain_filter,
+                                                                               &result_filter);
+                       if (!W_ERROR_IS_OK(werr)) {
+                               return werr;
                        }
-                       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);
+                       if (info1->status != DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR)
+                               return WERR_OK;
                }
                break;
        }
        case DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT: {
                char *p;
                char *domain;
-               struct ldb_dn *dn_domain;
                const char *account = NULL;
 
                domain = talloc_strdup(mem_ctx, name);
@@ -475,14 +576,11 @@ WERROR DsCrackNameOneName(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
                        account = &p[1];
                }
 
-               /* it could be in DNS domain form */
-               dn_domain = samdb_dns_domain_to_dn(sam_ctx, mem_ctx, domain);
-               W_ERROR_HAVE_NO_MEMORY(dn_domain);
-
                domain_filter = talloc_asprintf(mem_ctx, 
-                                               "(&(&(|(nETBIOSName=%s)(nCName=%s))(objectclass=crossRef))(ncName=*))",
+                                               "(&(objectClass=crossRef)(netbiosName=%s)(systemFlags:%s:=%u))",
                                                ldb_binary_encode_string(mem_ctx, domain),
-                                               ldb_dn_get_linearized(dn_domain));
+                                               LDB_OID_COMPARATOR_AND,
+                                               SYSTEM_FLAG_CR_NTDS_DOMAIN);
                W_ERROR_HAVE_NO_MEMORY(domain_filter);
                if (account) {
                        result_filter = talloc_asprintf(mem_ctx, "(sAMAccountName=%s)",
@@ -544,6 +642,7 @@ WERROR DsCrackNameOneName(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
 
                domain_filter = NULL;
                if (!sid) {
+                       info1->dns_domain_name = NULL;
                        info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
                        return WERR_OK;
                }
@@ -599,6 +698,7 @@ WERROR DsCrackNameOneName(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
        case DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL: {
                krb5_principal principal;
                char *unparsed_name_short;
+               const krb5_data *component;
                char *service;
 
                ret = smb_krb5_init_context(mem_ctx, 
@@ -611,16 +711,18 @@ WERROR DsCrackNameOneName(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
                }
 
                ret = krb5_parse_name(smb_krb5_context->krb5_context, name, &principal);
-               if (ret == 0 && principal->name.name_string.len < 2) {
+               if (ret == 0 &&
+                   krb5_princ_size(smb_krb5_context->krb5_context,
+                                                       principal) < 2) {
                        info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
                        krb5_free_principal(smb_krb5_context->krb5_context, principal);
                        return WERR_OK;
+               } else if (ret == 0) {
+                       krb5_free_principal(smb_krb5_context->krb5_context, principal);
                }
                ret = krb5_parse_name_flags(smb_krb5_context->krb5_context, name, 
                                            KRB5_PRINCIPAL_PARSE_NO_REALM, &principal);
                if (ret) {
-                       krb5_free_principal(smb_krb5_context->krb5_context, principal);
-
                        return dns_domain_from_principal(mem_ctx, smb_krb5_context,
                                                         name, info1);
                }
@@ -634,13 +736,22 @@ WERROR DsCrackNameOneName(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
                        return WERR_NOMEM;
                }
 
-               service = principal->name.name_string.val[0];
-               if ((principal->name.name_string.len == 2) && (strcasecmp(service, "host") == 0)) {
+               component = krb5_princ_component(smb_krb5_context->krb5_context,
+                                                principal, 0);
+               service = (char *)component->data;
+               if ((krb5_princ_size(smb_krb5_context->krb5_context,
+                                                       principal) == 2) &&
+                       (strcasecmp(service, "host") == 0)) {
                        /* the 'cn' attribute is just the leading part of the name */
                        char *computer_name;
-                       computer_name = talloc_strndup(mem_ctx, principal->name.name_string.val[1], 
-                                                     strcspn(principal->name.name_string.val[1], "."));
+                       component = krb5_princ_component(
+                                               smb_krb5_context->krb5_context,
+                                               principal, 1);
+                       computer_name = talloc_strndup(mem_ctx, (char *)component->data,
+                                                       strcspn((char *)component->data, "."));
                        if (computer_name == NULL) {
+                               krb5_free_principal(smb_krb5_context->krb5_context, principal);
+                               free(unparsed_name_short);
                                return WERR_NOMEM;
                        }
 
@@ -661,7 +772,6 @@ WERROR DsCrackNameOneName(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
                info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
                return WERR_OK;
        }
-
        }
 
        if (format_flags & DRSUAPI_DS_NAME_FLAG_SYNTACTICAL_ONLY) {
@@ -674,7 +784,7 @@ WERROR DsCrackNameOneName(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
                                    format_flags, format_offered, format_desired, 
                                    name_dn, name, 
                                    domain_filter, result_filter, 
-                                   info1);
+                                   info1, scope, search_dn);
 }
 
 /* Subcase of CrackNames.  It is possible to translate a LDAP-style DN
@@ -726,7 +836,8 @@ static WERROR DsCrackNameOneFilter(struct ldb_context *sam_ctx, TALLOC_CTX *mem_
                                   enum drsuapi_DsNameFormat format_desired,
                                   struct ldb_dn *name_dn, const char *name, 
                                   const char *domain_filter, const char *result_filter, 
-                                  struct drsuapi_DsNameInfo1 *info1)
+                                  struct drsuapi_DsNameInfo1 *info1,
+                                  int scope, struct ldb_dn *search_dn)
 {
        int ldb_ret;
        struct ldb_result *domain_res = NULL;
@@ -794,7 +905,7 @@ static WERROR DsCrackNameOneFilter(struct ldb_context *sam_ctx, TALLOC_CTX *mem_
                                             "%s", domain_filter);
 
                if (ldb_ret != LDB_SUCCESS) {
-                       DEBUG(2, ("DsCrackNameOneFilter domain ref search failed: %s", ldb_errstring(sam_ctx)));
+                       DEBUG(2, ("DsCrackNameOneFilter domain ref search failed: %s\n", ldb_errstring(sam_ctx)));
                        info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
                        return WERR_OK;
                }
@@ -822,25 +933,32 @@ static WERROR DsCrackNameOneFilter(struct ldb_context *sam_ctx, TALLOC_CTX *mem_
                int ret;
                struct ldb_result *res;
                uint32_t dsdb_flags = 0;
-               struct ldb_dn *search_dn;
+               struct ldb_dn *real_search_dn;
 
                if (domain_res) {
-                       dsdb_flags = 0;
-                       search_dn = samdb_result_dn(sam_ctx, mem_ctx, domain_res->msgs[0], "ncName", NULL);
+                       if (!search_dn) {
+                               struct ldb_dn *tmp_dn = samdb_result_dn(sam_ctx, mem_ctx, domain_res->msgs[0], "ncName", NULL);
+                               real_search_dn = tmp_dn;
+                       } else {
+                               real_search_dn = search_dn;
+                       }
                } else {
                        dsdb_flags = DSDB_SEARCH_SEARCH_ALL_PARTITIONS;
-                       search_dn = ldb_get_root_basedn(sam_ctx);
+                       real_search_dn = NULL;
+               }
+               if (format_desired == DRSUAPI_DS_NAME_FORMAT_GUID){
+                        dsdb_flags |= DSDB_SEARCH_SHOW_RECYCLED;
                }
 
                /* search with the 'phantom root' flag */
                ret = dsdb_search(sam_ctx, mem_ctx, &res,
-                                 search_dn,
-                                 LDB_SCOPE_SUBTREE,
+                                 real_search_dn,
+                                 scope,
                                  result_attrs,
-                                 DSDB_SEARCH_SEARCH_ALL_PARTITIONS,
+                                 dsdb_flags,
                                  "%s", result_filter);
                if (ret != LDB_SUCCESS) {
-                       DEBUG(2, ("DsCrackNameOneFilter phantom root search failed: %s",
+                       DEBUG(2, ("DsCrackNameOneFilter phantom root search failed: %s\n",
                                  ldb_errstring(sam_ctx)));
                        info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
                        return WERR_OK;
@@ -857,7 +975,7 @@ static WERROR DsCrackNameOneFilter(struct ldb_context *sam_ctx, TALLOC_CTX *mem_
                                          result_attrs);
        } else {
                /* Can't happen */
-               DEBUG(0, ("LOGIC ERROR: DsCrackNameOneFilter domain ref search not available: This can't happen..."));
+               DEBUG(0, ("LOGIC ERROR: DsCrackNameOneFilter domain ref search not available: This can't happen...\n"));
                info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
                return WERR_OK;
        }
@@ -884,7 +1002,7 @@ 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)));
+               DEBUG(2, ("DsCrackNameOneFilter result search failed: %s\n", ldb_errstring(sam_ctx)));
                info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
                return WERR_OK;
        default:
@@ -915,6 +1033,7 @@ static WERROR DsCrackNameOneFilter(struct ldb_context *sam_ctx, TALLOC_CTX *mem_
                                return WERR_OK;
                        }
                }
+               /* FALL TROUGH */
                default:
                        info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE;
                        return WERR_OK;
@@ -953,9 +1072,16 @@ static WERROR DsCrackNameOneFilter(struct ldb_context *sam_ctx, TALLOC_CTX *mem_
 
                const struct dom_sid *sid = samdb_result_dom_sid(mem_ctx, result, "objectSid");
                const char *_acc = "", *_dom = "";
+               if (sid == NULL) {
+                       info1->status = DRSUAPI_DS_NAME_STATUS_NO_MAPPING;
+                       return WERR_OK;
+               }
 
-               if (samdb_find_attribute(sam_ctx, result, "objectClass", "domain")) {
-
+               if (samdb_find_attribute(sam_ctx, result, "objectClass",
+                                        "domain")) {
+                       /* This can also find a DomainDNSZones entry,
+                        * but it won't have the SID we just
+                        * checked.  */
                        ldb_ret = ldb_search(sam_ctx, mem_ctx, &domain_res,
                                                     partitions_basedn,
                                                     LDB_SCOPE_ONELEVEL,
@@ -963,7 +1089,7 @@ static WERROR DsCrackNameOneFilter(struct ldb_context *sam_ctx, TALLOC_CTX *mem_
                                                     "(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)));
+                               DEBUG(2, ("DsCrackNameOneFilter domain ref search failed: %s\n", ldb_errstring(sam_ctx)));
                                info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
                                return WERR_OK;
                        }
@@ -1004,7 +1130,7 @@ static WERROR DsCrackNameOneFilter(struct ldb_context *sam_ctx, TALLOC_CTX *mem_
                                                             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)));
+                                       DEBUG(2, ("DsCrackNameOneFilter domain search failed: %s\n", ldb_errstring(sam_ctx)));
                                        info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
                                        return WERR_OK;
                                }
@@ -1027,7 +1153,7 @@ static WERROR DsCrackNameOneFilter(struct ldb_context *sam_ctx, TALLOC_CTX *mem_
                                                             "(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)));
+                                       DEBUG(2, ("DsCrackNameOneFilter domain ref search failed: %s\n", ldb_errstring(sam_ctx)));
                                        info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
                                        return WERR_OK;
                                }
@@ -1082,6 +1208,7 @@ static WERROR DsCrackNameOneFilter(struct ldb_context *sam_ctx, TALLOC_CTX *mem_
        }
        case DRSUAPI_DS_NAME_FORMAT_DNS_DOMAIN: 
        case DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY: {
+               info1->dns_domain_name = NULL;
                info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
                return WERR_OK;
        }
@@ -1337,7 +1464,7 @@ WERROR dcesrv_drsuapi_ListRoles(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx
                names[i].dns_domain_name = samdb_dn_to_dnshostname(sam_ctx, mem_ctx,
                                                                   server_dn);
                if(!names[i].dns_domain_name) {
-                       DEBUG(4, ("list_roles: Failed to find dNSHostName for server %s",
+                       DEBUG(4, ("list_roles: Failed to find dNSHostName for server %s\n",
                                  ldb_dn_get_linearized(server_dn)));
                }
                names[i].result_name = talloc_strdup(mem_ctx, ldb_dn_get_linearized(role_owner_dn));
@@ -1357,7 +1484,7 @@ WERROR dcesrv_drsuapi_CrackNamesByNameFormat(struct ldb_context *sam_ctx, TALLOC
        uint32_t i, count;
        WERROR status;
 
-       *ctr1 = talloc(mem_ctx, struct drsuapi_DsNameCtr1);
+       *ctr1 = talloc_zero(mem_ctx, struct drsuapi_DsNameCtr1);
        W_ERROR_HAVE_NO_MEMORY(*ctr1);
 
        count = req1->count;
@@ -1381,3 +1508,110 @@ WERROR dcesrv_drsuapi_CrackNamesByNameFormat(struct ldb_context *sam_ctx, TALLOC
 
        return WERR_OK;
 }
+
+WERROR dcesrv_drsuapi_ListInfoServer(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
+                                    const struct drsuapi_DsNameRequest1 *req1,
+                                    struct drsuapi_DsNameCtr1 **_ctr1)
+{
+       struct drsuapi_DsNameInfo1 *names;
+       struct ldb_result *res;
+       struct ldb_dn *server_dn, *dn;
+       struct drsuapi_DsNameCtr1 *ctr1;
+       int ret, i;
+       const char *str;
+       const char *attrs[] = {
+               "dn",
+               "dNSHostName",
+               "serverReference",
+               NULL
+       };
+
+       *_ctr1 = NULL;
+
+       ctr1 = talloc_zero(mem_ctx, struct drsuapi_DsNameCtr1);
+       W_ERROR_HAVE_NO_MEMORY(ctr1);
+
+       /*
+        * No magic value here, we have to return 3 entries according to the
+        * MS-DRSR.pdf
+        */
+       ctr1->count = 3;
+       names = talloc_zero_array(ctr1, struct drsuapi_DsNameInfo1,
+                                 ctr1->count);
+       W_ERROR_HAVE_NO_MEMORY(names);
+       ctr1->array = names;
+
+       for (i=0; i < ctr1->count; i++) {
+               names[i].status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
+       }
+       *_ctr1 = ctr1;
+
+       if (req1->count != 1) {
+               DEBUG(1, ("Expected a count of 1 for the ListInfoServer crackname \n"));
+               return WERR_OK;
+       }
+
+       if (req1->names[0].str == NULL) {
+               return WERR_OK;
+       }
+
+       server_dn = ldb_dn_new(mem_ctx, sam_ctx, req1->names[0].str);
+       W_ERROR_HAVE_NO_MEMORY(server_dn);
+
+       ret = ldb_search(sam_ctx, mem_ctx, &res, server_dn, LDB_SCOPE_ONELEVEL,
+                        NULL, "(objectClass=nTDSDSA)");
+
+       if (ret != LDB_SUCCESS) {
+               DEBUG(1, ("Search for objectClass=nTDSDSA "
+                         "returned less than 1 objects\n"));
+               return WERR_OK;
+       }
+
+       if (res->count != 1) {
+               DEBUG(1, ("Search for objectClass=nTDSDSA "
+                         "returned less than 1 objects\n"));
+               return WERR_OK;
+       }
+
+       if (res->msgs[0]->dn) {
+               names[0].result_name = ldb_dn_alloc_linearized(names, res->msgs[0]->dn);
+               W_ERROR_HAVE_NO_MEMORY(names[0].result_name);
+               names[0].status = DRSUAPI_DS_NAME_STATUS_OK;
+       }
+
+       talloc_free(res);
+
+       ret = ldb_search(sam_ctx, mem_ctx, &res, server_dn, LDB_SCOPE_BASE,
+                        attrs, "(objectClass=*)");
+       if (ret != LDB_SUCCESS) {
+               DEBUG(1, ("Search for objectClass=* on dn %s"
+                         "returned %s\n", req1->names[0].str,
+                         ldb_strerror(ret)));
+               return WERR_OK;
+       }
+
+       if (res->count != 1) {
+               DEBUG(1, ("Search for objectClass=* on dn %s"
+                         "returned less than 1 objects\n", req1->names[0].str));
+               return WERR_OK;
+       }
+
+       str = ldb_msg_find_attr_as_string(res->msgs[0], "dNSHostName", NULL);
+       if (str != NULL) {
+               names[1].result_name = talloc_strdup(names, str);
+               W_ERROR_HAVE_NO_MEMORY(names[1].result_name);
+               names[1].status = DRSUAPI_DS_NAME_STATUS_OK;
+       }
+
+       dn = ldb_msg_find_attr_as_dn(sam_ctx, mem_ctx, res->msgs[0], "serverReference");
+       if (dn != NULL) {
+               names[2].result_name = ldb_dn_alloc_linearized(names, dn);
+               W_ERROR_HAVE_NO_MEMORY(names[2].result_name);
+               names[2].status = DRSUAPI_DS_NAME_STATUS_OK;
+       }
+
+       talloc_free(dn);
+       talloc_free(res);
+
+       return WERR_OK;
+}