ldb: Build lmdb backend also in non-AD case
[samba.git] / source3 / passdb / lookup_sid.c
index a93145330b21b0b87215d986d035059f881974c2..7a29bb1abb5becf30dca26818e0abf94d9a76d37 100644 (file)
@@ -1,28 +1,70 @@
-/* 
+/*
    Unix SMB/CIFS implementation.
    uid/user handling
    Copyright (C) Andrew Tridgell         1992-1998
    Copyright (C) Gerald (Jerry) Carter   2003
    Copyright (C) Volker Lendecke        2005
-   
+
    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 3 of the License, or
    (at your option) any later version.
-   
+
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    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, see <http://www.gnu.org/licenses/>.
 */
 
 #include "includes.h"
+#include "passdb.h"
+#include "lib/util_unixsids.h"
 #include "../librpc/gen_ndr/ndr_security.h"
 #include "secrets.h"
-#include "memcache.h"
+#include "../lib/util/memcache.h"
+#include "idmap_cache.h"
+#include "../libcli/security/security.h"
+#include "lib/winbind_util.h"
+#include "../librpc/gen_ndr/idmap.h"
+#include "lib/util/bitmap.h"
+
+static bool lookup_unix_user_name(const char *name, struct dom_sid *sid)
+{
+       struct passwd *pwd;
+       bool ret;
+
+       pwd = Get_Pwnam_alloc(talloc_tos(), name);
+       if (pwd == NULL) {
+               return False;
+       }
+
+       /*
+        * For 64-bit uid's we have enough space in the whole SID,
+        * should they become necessary
+        */
+       ret = sid_compose(sid, &global_sid_Unix_Users, pwd->pw_uid);
+       TALLOC_FREE(pwd);
+       return ret;
+}
+
+static bool lookup_unix_group_name(const char *name, struct dom_sid *sid)
+{
+       struct group *grp;
+
+       grp = getgrnam(name);
+       if (grp == NULL) {
+               return False;
+       }
+
+       /*
+        * For 64-bit gid's we have enough space in the whole SID,
+        * should they become necessary
+        */
+       return sid_compose(sid, &global_sid_Unix_Groups, grp->gr_gid);
+}
 
 /*****************************************************************
  Dissect a user-provided name into domain, name, sid and type.
  If an explicit domain name was given in the form domain\user, it
  has to try that. If no explicit domain name was given, we have
  to do guesswork.
-*****************************************************************/  
+*****************************************************************/
 
-bool lookup_name(TALLOC_CTX *mem_ctx,
-                const char *full_name, int flags,
-                const char **ret_domain, const char **ret_name,
-                struct dom_sid *ret_sid, enum lsa_SidType *ret_type)
+static NTSTATUS lookup_name_internal(TALLOC_CTX *mem_ctx,
+                                    const char *full_name,
+                                    int flags,
+                                    const char **ret_domain,
+                                    const char **ret_name,
+                                    struct dom_sid *ret_sid,
+                                    enum lsa_SidType *ret_type)
 {
        char *p;
        const char *tmp;
        const char *domain = NULL;
        const char *name = NULL;
-       uint32 rid;
+       uint32_t rid;
        struct dom_sid sid;
        enum lsa_SidType type;
        TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+       NTSTATUS status;
 
        if (tmp_ctx == NULL) {
                DEBUG(0, ("talloc_new failed\n"));
-               return false;
+               return NT_STATUS_NO_MEMORY;
        }
 
        p = strchr_m(full_name, '\\');
@@ -58,31 +104,61 @@ bool lookup_name(TALLOC_CTX *mem_ctx,
                                        PTR_DIFF(p, full_name));
                name = talloc_strdup(tmp_ctx, p+1);
        } else {
-               domain = talloc_strdup(tmp_ctx, "");
-               name = talloc_strdup(tmp_ctx, full_name);
+               char *q = strchr_m(full_name, '@');
+
+               /* Set the domain for UPNs */
+               if (q != NULL) {
+                       name = talloc_strndup(tmp_ctx,
+                                             full_name,
+                                             PTR_DIFF(q, full_name));
+                       domain = talloc_strdup(tmp_ctx, q + 1);
+               } else {
+                       domain = talloc_strdup(tmp_ctx, "");
+                       name = talloc_strdup(tmp_ctx, full_name);
+               }
        }
 
        if ((domain == NULL) || (name == NULL)) {
                DEBUG(0, ("talloc failed\n"));
                TALLOC_FREE(tmp_ctx);
-               return false;
+               return NT_STATUS_NO_MEMORY;
        }
 
        DEBUG(10,("lookup_name: %s => domain=[%s], name=[%s]\n",
                full_name, domain, name));
        DEBUG(10, ("lookup_name: flags = 0x0%x\n", flags));
 
-       if ((flags & LOOKUP_NAME_DOMAIN) &&
-           strequal(domain, get_global_sam_name()))
-       {
+       if ((flags & LOOKUP_NAME_DOMAIN) || (flags == 0)) {
+               bool check_global_sam = false;
 
-               /* It's our own domain, lookup the name in passdb */
-               if (lookup_global_sam_name(name, flags, &rid, &type)) {
-                       sid_compose(&sid, get_global_sam_sid(), rid);
-                       goto ok;
+               check_global_sam = strequal(domain, get_global_sam_name());
+
+               /* If we are running on a DC that has PASSDB module with domain
+                * information, check if DNS forest name is matching the domain
+                * name. This is the case of IPA domain controller when
+                * trusted AD DC looks up users found in a Global Catalog of
+                * the forest root domain. */
+               if (!check_global_sam && (IS_DC)) {
+                       struct pdb_domain_info *dom_info = NULL;
+                       dom_info = pdb_get_domain_info(tmp_ctx);
+
+                       if ((dom_info != NULL) && (dom_info->dns_forest != NULL)) {
+                               check_global_sam = strequal(domain, dom_info->dns_forest);
+                       }
+
+                       TALLOC_FREE(dom_info);
+               }
+
+               if (check_global_sam) {
+                       /* It's our own domain, lookup the name in passdb */
+                       if (lookup_global_sam_name(name, flags, &rid, &type)) {
+                               sid_compose(&sid, get_global_sam_sid(), rid);
+                               goto ok;
+                       }
+                       TALLOC_FREE(tmp_ctx);
+                       *ret_type = SID_NAME_UNKNOWN;
+                       return NT_STATUS_OK;
                }
-               TALLOC_FREE(tmp_ctx);
-               return false;
        }
 
        if ((flags & LOOKUP_NAME_BUILTIN) &&
@@ -103,26 +179,34 @@ bool lookup_name(TALLOC_CTX *mem_ctx,
                        goto ok;
                }
                TALLOC_FREE(tmp_ctx);
-               return false;
+               *ret_type = SID_NAME_UNKNOWN;
+               return NT_STATUS_OK;
        }
 
        /* Try the explicit winbind lookup first, don't let it guess the
         * domain yet at this point yet. This comes later. */
 
        if ((domain[0] != '\0') &&
-           (flags & ~(LOOKUP_NAME_DOMAIN|LOOKUP_NAME_ISOLATED)) &&
-           (winbind_lookup_name(domain, name, &sid, &type))) {
+           (flags & ~(LOOKUP_NAME_DOMAIN|LOOKUP_NAME_ISOLATED)))
+       {
+               status = winbind_lookup_name_ex(domain, name, &sid, &type);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
+               if (type != SID_NAME_UNKNOWN) {
                        goto ok;
+               }
        }
 
-       if (((flags & LOOKUP_NAME_NO_NSS) == 0)
+       if (((flags & (LOOKUP_NAME_NO_NSS|LOOKUP_NAME_GROUP)) == 0)
            && strequal(domain, unix_users_domain_name())) {
                if (lookup_unix_user_name(name, &sid)) {
                        type = SID_NAME_USER;
                        goto ok;
                }
                TALLOC_FREE(tmp_ctx);
-               return false;
+               *ret_type = SID_NAME_UNKNOWN;
+               return NT_STATUS_OK;
        }
 
        if (((flags & LOOKUP_NAME_NO_NSS) == 0)
@@ -132,12 +216,39 @@ bool lookup_name(TALLOC_CTX *mem_ctx,
                        goto ok;
                }
                TALLOC_FREE(tmp_ctx);
-               return false;
+               *ret_type = SID_NAME_UNKNOWN;
+               return NT_STATUS_OK;
        }
 
-       if ((domain[0] == '\0') && (!(flags & LOOKUP_NAME_ISOLATED))) {
+       /*
+        * Finally check for a well known domain name ("NT Authority"),
+        * this is being taken care of in lookup_wellknown_name().
+        */
+       if ((domain[0] != '\0') &&
+           (flags & LOOKUP_NAME_WKN) &&
+           lookup_wellknown_name(tmp_ctx, name, &sid, &domain))
+       {
+               type = SID_NAME_WKN_GRP;
+               goto ok;
+       }
+
+       /*
+        * If we're told not to look up 'isolated' names then we're
+        * done.
+        */
+       if (!(flags & LOOKUP_NAME_ISOLATED)) {
                TALLOC_FREE(tmp_ctx);
-               return false;
+               *ret_type = SID_NAME_UNKNOWN;
+               return NT_STATUS_OK;
+       }
+
+       /*
+        * No domain names beyond this point
+        */
+       if (domain[0] != '\0') {
+               TALLOC_FREE(tmp_ctx);
+               *ret_type = SID_NAME_UNKNOWN;
+               return NT_STATUS_OK;
        }
 
        /* Now the guesswork begins, we haven't been given an explicit
@@ -147,6 +258,11 @@ bool lookup_name(TALLOC_CTX *mem_ctx,
 
        /* 1. well-known names */
 
+       /*
+        * Check for well known names without a domain name.
+        * e.g. \Creator Owner.
+        */
+
        if ((flags & LOOKUP_NAME_WKN) &&
            lookup_wellknown_name(tmp_ctx, name, &sid, &domain))
        {
@@ -174,7 +290,7 @@ bool lookup_name(TALLOC_CTX *mem_ctx,
                if (!secrets_fetch_domain_sid(name, &sid)) {
                        DEBUG(3, ("Could not fetch my SID\n"));
                        TALLOC_FREE(tmp_ctx);
-                       return false;
+                       return NT_STATUS_INTERNAL_DB_CORRUPTION;
                }
                /* Swap domain and name */
                tmp = name; name = domain; domain = tmp;
@@ -190,7 +306,7 @@ bool lookup_name(TALLOC_CTX *mem_ctx,
                if (!secrets_fetch_domain_sid(name, &sid)) {
                        DEBUG(3, ("Could not fetch the domain SID\n"));
                        TALLOC_FREE(tmp_ctx);
-                       return false;
+                       return NT_STATUS_INTERNAL_DB_CORRUPTION;
                }
                /* Swap domain and name */
                tmp = name; name = domain; domain = tmp;
@@ -210,7 +326,7 @@ bool lookup_name(TALLOC_CTX *mem_ctx,
                goto ok;
        }
 
-       /* 6. Builtin aliases */        
+       /* 6. Builtin aliases */
 
        if ((flags & LOOKUP_NAME_BUILTIN) &&
            lookup_builtin_name(name, &rid))
@@ -238,16 +354,21 @@ bool lookup_name(TALLOC_CTX *mem_ctx,
 
        if (!(flags & LOOKUP_NAME_REMOTE)) {
                TALLOC_FREE(tmp_ctx);
-               return false;
+               *ret_type = SID_NAME_UNKNOWN;
+               return NT_STATUS_OK;
        }
 
        /* If we are not a DC, we have to ask in our primary domain. Let
         * winbind do that. */
 
-       if (!IS_DC &&
-           (winbind_lookup_name(lp_workgroup(), name, &sid, &type))) {
-               domain = talloc_strdup(tmp_ctx, lp_workgroup());
-               goto ok;
+       if (!IS_DC) {
+               status = winbind_lookup_name_ex(lp_workgroup(), name, &sid, &type);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
+               if (type != SID_NAME_UNKNOWN) {
+                       goto ok;
+               }
        }
 
        /* 9. Trusted domains */
@@ -255,33 +376,43 @@ bool lookup_name(TALLOC_CTX *mem_ctx,
        /* If we're a DC we have to ask all trusted DC's. Winbind does not do
         * that (yet), but give it a chance. */
 
-       if (IS_DC && winbind_lookup_name("", name, &sid, &type)) {
+       if (IS_DC) {
                struct dom_sid dom_sid;
-               uint32 tmp_rid;
                enum lsa_SidType domain_type;
-               
-               if (type == SID_NAME_DOMAIN) {
-                       /* Swap name and type */
-                       tmp = name; name = domain; domain = tmp;
-                       goto ok;
-               }
-
-               /* Here we have to cope with a little deficiency in the
-                * winbind API: We have to ask it again for the name of the
-                * domain it figured out itself. Maybe fix that later... */
 
-               sid_copy(&dom_sid, &sid);
-               sid_split_rid(&dom_sid, &tmp_rid);
+               status = winbind_lookup_name_ex("", name, &sid, &type);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
+               if (type != SID_NAME_UNKNOWN) {
+                       if (type == SID_NAME_DOMAIN) {
+                               /* Swap name and type */
+                               tmp = name; name = domain; domain = tmp;
+                               goto ok;
+                       }
 
-               if (!winbind_lookup_sid(tmp_ctx, &dom_sid, &domain, NULL,
-                                       &domain_type) ||
-                   (domain_type != SID_NAME_DOMAIN)) {
-                       DEBUG(2, ("winbind could not find the domain's name "
-                                 "it just looked up for us\n"));
-                       TALLOC_FREE(tmp_ctx);
-                       return false;
+                       /* Here we have to cope with a little deficiency
+                        * in the winbind API: We have to ask it again
+                        * for the name of the domain it figured out
+                        * itself. Maybe fix that later... */
+
+                       sid_copy(&dom_sid, &sid);
+                       sid_split_rid(&dom_sid, NULL);
+
+                       if (!winbind_lookup_sid(tmp_ctx, &dom_sid,
+                                               &domain, NULL,
+                                               &domain_type) ||
+                           (domain_type != SID_NAME_DOMAIN))
+                       {
+                               DBG_INFO("winbind could not find the "
+                                        "domain's name it just looked "
+                                        "up for us\n");
+                               TALLOC_FREE(tmp_ctx);
+                               *ret_type = SID_NAME_UNKNOWN;
+                               return NT_STATUS_OK;
+                       }
+                       goto ok;
                }
-               goto ok;
        }
 
        /* 10. Don't translate */
@@ -289,7 +420,7 @@ bool lookup_name(TALLOC_CTX *mem_ctx,
        /* 11. Ok, windows would end here. Samba has two more options:
                Unmapped users and unmapped groups */
 
-       if (((flags & LOOKUP_NAME_NO_NSS) == 0)
+       if (((flags & (LOOKUP_NAME_NO_NSS|LOOKUP_NAME_GROUP)) == 0)
            && lookup_unix_user_name(name, &sid)) {
                domain = talloc_strdup(tmp_ctx, unix_users_domain_name());
                type = SID_NAME_USER;
@@ -308,13 +439,14 @@ bool lookup_name(TALLOC_CTX *mem_ctx,
         */
 
        TALLOC_FREE(tmp_ctx);
-       return false;
+       *ret_type = SID_NAME_UNKNOWN;
+       return NT_STATUS_OK;
 
  ok:
        if ((domain == NULL) || (name == NULL)) {
                DEBUG(0, ("talloc failed\n"));
                TALLOC_FREE(tmp_ctx);
-               return false;
+               return NT_STATUS_NO_MEMORY;
        }
 
        /*
@@ -325,7 +457,7 @@ bool lookup_name(TALLOC_CTX *mem_ctx,
            !(*ret_name = talloc_strdup(mem_ctx, name))) {
                DEBUG(0, ("talloc failed\n"));
                TALLOC_FREE(tmp_ctx);
-               return false;
+               return NT_STATUS_NO_MEMORY;
        }
 
        if (ret_domain != NULL) {
@@ -333,9 +465,12 @@ bool lookup_name(TALLOC_CTX *mem_ctx,
                if (!(tmp_dom = talloc_strdup(mem_ctx, domain))) {
                        DEBUG(0, ("talloc failed\n"));
                        TALLOC_FREE(tmp_ctx);
-                       return false;
+                       return NT_STATUS_NO_MEMORY;
+               }
+               if (!strupper_m(tmp_dom)) {
+                       TALLOC_FREE(tmp_ctx);
+                       return NT_STATUS_INTERNAL_ERROR;
                }
-               strupper_m(tmp_dom);
                *ret_domain = tmp_dom;
        }
 
@@ -343,11 +478,39 @@ bool lookup_name(TALLOC_CTX *mem_ctx,
                sid_copy(ret_sid, &sid);
        }
 
+       *ret_type = type;
+
+       TALLOC_FREE(tmp_ctx);
+       return NT_STATUS_OK;
+}
+
+bool lookup_name(TALLOC_CTX *mem_ctx,
+                const char *full_name,
+                int flags,
+                const char **ret_domain,
+                const char **ret_name,
+                struct dom_sid *ret_sid,
+                enum lsa_SidType *ret_type)
+{
+       enum lsa_SidType type;
+       NTSTATUS status;
+
+       status = lookup_name_internal(mem_ctx,
+                                     full_name,
+                                     flags,
+                                     ret_domain,
+                                     ret_name,
+                                     ret_sid,
+                                     &type);
+       if (!NT_STATUS_IS_OK(status)) {
+               return false;
+       }
        if (ret_type != NULL) {
                *ret_type = type;
        }
-
-       TALLOC_FREE(tmp_ctx);
+       if (type == SID_NAME_UNKNOWN) {
+               return false;
+       }
        return true;
 }
 
@@ -357,35 +520,66 @@ bool lookup_name(TALLOC_CTX *mem_ctx,
  and then "Unix Users"\foo (or "Unix Groups"\foo).
 ************************************************************************/
 
-bool lookup_name_smbconf(TALLOC_CTX *mem_ctx,
-                const char *full_name, int flags,
-                const char **ret_domain, const char **ret_name,
-                struct dom_sid *ret_sid, enum lsa_SidType *ret_type)
+NTSTATUS lookup_name_smbconf_ex(TALLOC_CTX *mem_ctx,
+                               const char *full_name,
+                               int flags,
+                               const char **ret_domain,
+                               const char **ret_name,
+                               struct dom_sid *ret_sid,
+                               enum lsa_SidType *ret_type)
 {
-       char *qualified_name;
-       const char *p;
+       char *qualified_name = NULL;
+       const char *p = strchr_m(full_name, *lp_winbind_separator());
+       bool is_qualified = p != NULL || strchr_m(full_name, '@') != NULL;
+       NTSTATUS status;
 
-       /* NB. No winbindd_separator here as lookup_name needs \\' */
-       if ((p = strchr_m(full_name, *lp_winbind_separator())) != NULL) {
+       /* For DOMAIN\user or user@REALM directly call lookup_name(). */
+       if (is_qualified) {
 
                /* The name is already qualified with a domain. */
 
-               if (*lp_winbind_separator() != '\\') {
-                       char *tmp;
-
+               if (p != NULL && *lp_winbind_separator() != '\\') {
                        /* lookup_name() needs '\\' as a separator */
 
-                       tmp = talloc_strdup(mem_ctx, full_name);
-                       if (!tmp) {
-                               return false;
+                       qualified_name = talloc_strdup(mem_ctx, full_name);
+                       if (qualified_name == NULL) {
+                               return NT_STATUS_NO_MEMORY;
                        }
-                       tmp[p - full_name] = '\\';
-                       full_name = tmp;
+                       qualified_name[p - full_name] = '\\';
+                       full_name = qualified_name;
                }
 
-               return lookup_name(mem_ctx, full_name, flags,
-                               ret_domain, ret_name,
-                               ret_sid, ret_type);
+               return lookup_name_internal(mem_ctx,
+                                           full_name,
+                                           flags,
+                                           ret_domain,
+                                           ret_name,
+                                           ret_sid,
+                                           ret_type);
+       }
+
+       /* Try with winbind default domain name. */
+       if (lp_winbind_use_default_domain()) {
+               qualified_name = talloc_asprintf(mem_ctx,
+                                                "%s\\%s",
+                                                lp_workgroup(),
+                                                full_name);
+               if (qualified_name == NULL) {
+                       return NT_STATUS_NO_MEMORY;
+               }
+
+               status= lookup_name_internal(mem_ctx,
+                                            qualified_name,
+                                            flags,
+                                            ret_domain,
+                                            ret_name,
+                                            ret_sid,
+                                            ret_type);
+               if (NT_STATUS_IS_OK(status) &&
+                   *ret_type != SID_NAME_UNKNOWN)
+               {
+                       return NT_STATUS_OK;
+               }
        }
 
        /* Try with our own SAM name. */
@@ -393,15 +587,22 @@ bool lookup_name_smbconf(TALLOC_CTX *mem_ctx,
                                get_global_sam_name(),
                                full_name );
        if (!qualified_name) {
-               return false;
+               return NT_STATUS_NO_MEMORY;
        }
 
-       if (lookup_name(mem_ctx, qualified_name, flags,
-                               ret_domain, ret_name,
-                               ret_sid, ret_type)) {
-               return true;
+       status = lookup_name_internal(mem_ctx,
+                                     qualified_name,
+                                     flags,
+                                     ret_domain,
+                                     ret_name,
+                                     ret_sid,
+                                     ret_type);
+       if (NT_STATUS_IS_OK(status) &&
+           *ret_type != SID_NAME_UNKNOWN)
+       {
+               return NT_STATUS_OK;
        }
-       
+
        /* Finally try with "Unix Users" or "Unix Group" */
        qualified_name = talloc_asprintf(mem_ctx, "%s\\%s",
                                flags & LOOKUP_NAME_GROUP ?
@@ -409,17 +610,48 @@ bool lookup_name_smbconf(TALLOC_CTX *mem_ctx,
                                        unix_users_domain_name(),
                                full_name );
        if (!qualified_name) {
-               return false;
+               return NT_STATUS_NO_MEMORY;
        }
 
-       return lookup_name(mem_ctx, qualified_name, flags,
-                               ret_domain, ret_name,
-                               ret_sid, ret_type);
+       return lookup_name_internal(mem_ctx,
+                                   qualified_name,
+                                   flags,
+                                   ret_domain,
+                                   ret_name,
+                                   ret_sid,
+                                   ret_type);
+}
+
+bool lookup_name_smbconf(TALLOC_CTX *mem_ctx,
+                const char *full_name, int flags,
+                const char **ret_domain, const char **ret_name,
+                struct dom_sid *ret_sid, enum lsa_SidType *ret_type)
+{
+       enum lsa_SidType type;
+       NTSTATUS status;
+
+       status = lookup_name_smbconf_ex(mem_ctx,
+                                       full_name,
+                                       flags,
+                                       ret_domain,
+                                       ret_name,
+                                       ret_sid,
+                                       &type);
+       if (!NT_STATUS_IS_OK(status)) {
+               return false;
+       }
+       if (ret_type != NULL) {
+               *ret_type = type;
+       }
+       if (type == SID_NAME_UNKNOWN) {
+               return false;
+       }
+       return true;
 }
 
 static bool wb_lookup_rids(TALLOC_CTX *mem_ctx,
                           const struct dom_sid *domain_sid,
-                          int num_rids, uint32 *rids,
+                          int num_rids, uint32_t *rids,
                           const char **domain_name,
                           const char **names, enum lsa_SidType *types)
 {
@@ -474,13 +706,14 @@ static bool lookup_rids(TALLOC_CTX *mem_ctx, const struct dom_sid *domain_sid,
                        const char ***names, enum lsa_SidType **types)
 {
        int i;
+       struct dom_sid_buf buf;
 
        DEBUG(10, ("lookup_rids called for domain sid '%s'\n",
-                  sid_string_dbg(domain_sid)));
+                  dom_sid_str_buf(domain_sid, &buf)));
 
        if (num_rids) {
-               *names = TALLOC_ZERO_ARRAY(mem_ctx, const char *, num_rids);
-               *types = TALLOC_ARRAY(mem_ctx, enum lsa_SidType, num_rids);
+               *names = talloc_zero_array(mem_ctx, const char *, num_rids);
+               *types = talloc_array(mem_ctx, enum lsa_SidType, num_rids);
 
                if ((*names == NULL) || (*types == NULL)) {
                        return false;
@@ -493,7 +726,7 @@ static bool lookup_rids(TALLOC_CTX *mem_ctx, const struct dom_sid *domain_sid,
                *types = NULL;
        }
 
-       if (sid_check_is_domain(domain_sid)) {
+       if (sid_check_is_our_sam(domain_sid)) {
                NTSTATUS result;
 
                if (*domain_name == NULL) {
@@ -609,7 +842,7 @@ static bool lookup_as_domain(const struct dom_sid *sid, TALLOC_CTX *mem_ctx,
        const char *tmp;
        enum lsa_SidType type;
 
-       if (sid_check_is_domain(sid)) {
+       if (sid_check_is_our_sam(sid)) {
                *name = talloc_strdup(mem_ctx, get_global_sam_name());
                return true;
        }
@@ -640,7 +873,7 @@ static bool lookup_as_domain(const struct dom_sid *sid, TALLOC_CTX *mem_ctx,
        }
 
        if (IS_DC) {
-               uint32 i, num_domains;
+               uint32_t i, num_domains;
                struct trustdom_info **domains;
 
                /* This is relatively expensive, but it happens only on DCs
@@ -654,7 +887,7 @@ static bool lookup_as_domain(const struct dom_sid *sid, TALLOC_CTX *mem_ctx,
                }
 
                for (i=0; i<num_domains; i++) {
-                       if (sid_equal(sid, &domains[i]->sid)) {
+                       if (dom_sid_equal(sid, &domains[i]->sid)) {
                                *name = talloc_strdup(mem_ctx,
                                                      domains[i]->name);
                                return true;
@@ -693,6 +926,7 @@ static bool lookup_as_domain(const struct dom_sid *sid, TALLOC_CTX *mem_ctx,
 
 static bool check_dom_sid_to_level(const struct dom_sid *sid, int level)
 {
+       struct dom_sid_buf buf;
        int ret = false;
 
        switch(level) {
@@ -706,7 +940,7 @@ static bool check_dom_sid_to_level(const struct dom_sid *sid, int level)
        case 3:
        case 4:
        case 6:
-               ret = sid_check_is_domain(sid);
+               ret = sid_check_is_our_sam(sid);
                break;
        case 5:
                ret = false;
@@ -715,7 +949,8 @@ static bool check_dom_sid_to_level(const struct dom_sid *sid, int level)
 
        DEBUG(10, ("%s SID %s in level %d\n",
                   ret ? "Accepting" : "Rejecting",
-                  sid_string_dbg(sid), level));
+                  dom_sid_str_buf(sid, &buf),
+                  level));
        return ret;
 }
 
@@ -726,9 +961,7 @@ static bool check_dom_sid_to_level(const struct dom_sid *sid, int level)
  * This attempts to be as efficient as possible: It collects all SIDs
  * belonging to a domain and hands them in bulk to the appropriate lookup
  * function. In particular pdb_lookup_rids with ldapsam_trusted benefits
- * *hugely* from this. Winbind is going to be extended with a lookup_rids
- * interface as well, so on a DC we can do a bulk lsa_lookuprids to the
- * appropriate DC.
+ * *hugely* from this.
  */
 
 NTSTATUS lookup_sids(TALLOC_CTX *mem_ctx, int num_sids,
@@ -737,7 +970,7 @@ NTSTATUS lookup_sids(TALLOC_CTX *mem_ctx, int num_sids,
                     struct lsa_name_info **ret_names)
 {
        TALLOC_CTX *tmp_ctx;
-       NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+       NTSTATUS result;
        struct lsa_name_info *name_infos;
        struct lsa_dom_info *dom_infos = NULL;
 
@@ -749,7 +982,7 @@ NTSTATUS lookup_sids(TALLOC_CTX *mem_ctx, int num_sids,
        }
 
        if (num_sids) {
-               name_infos = TALLOC_ARRAY(mem_ctx, struct lsa_name_info, num_sids);
+               name_infos = talloc_array(mem_ctx, struct lsa_name_info, num_sids);
                if (name_infos == NULL) {
                        result = NT_STATUS_NO_MEMORY;
                        goto fail;
@@ -758,7 +991,7 @@ NTSTATUS lookup_sids(TALLOC_CTX *mem_ctx, int num_sids,
                name_infos = NULL;
        }
 
-       dom_infos = TALLOC_ZERO_ARRAY(mem_ctx, struct lsa_dom_info,
+       dom_infos = talloc_zero_array(mem_ctx, struct lsa_dom_info,
                                      LSA_REF_DOMAIN_LIST_MULTIPLIER);
        if (dom_infos == NULL) {
                result = NT_STATUS_NO_MEMORY;
@@ -766,7 +999,7 @@ NTSTATUS lookup_sids(TALLOC_CTX *mem_ctx, int num_sids,
        }
 
        /* First build up the data structures:
-        * 
+        *
         * dom_infos is a list of domains referenced in the list of
         * SIDs. Later we will walk the list of domains and look up the RIDs
         * in bulk.
@@ -799,7 +1032,7 @@ NTSTATUS lookup_sids(TALLOC_CTX *mem_ctx, int num_sids,
                                result = NT_STATUS_NO_MEMORY;
                                goto fail;
                        }
-                               
+
                        name_infos[i].rid = 0;
                        name_infos[i].type = SID_NAME_DOMAIN;
                        name_infos[i].name = NULL;
@@ -833,7 +1066,7 @@ NTSTATUS lookup_sids(TALLOC_CTX *mem_ctx, int num_sids,
                        if (!dom_infos[j].valid) {
                                break;
                        }
-                       if (sid_equal(&sid, &dom_infos[j].sid)) {
+                       if (dom_sid_equal(&sid, &dom_infos[j].sid)) {
                                break;
                        }
                }
@@ -894,13 +1127,18 @@ NTSTATUS lookup_sids(TALLOC_CTX *mem_ctx, int num_sids,
                        break;
                }
 
-               if (dom->num_idxs) {
-                       if (!(rids = TALLOC_ARRAY(tmp_ctx, uint32, dom->num_idxs))) {
-                               result = NT_STATUS_NO_MEMORY;
-                               goto fail;
-                       }
-               } else {
-                       rids = NULL;
+               if (dom->num_idxs == 0) {
+                       /*
+                        * This happens only if the only sid related to
+                        * this domain is the domain sid itself, which
+                        * is mapped to SID_NAME_DOMAIN above.
+                        */
+                       continue;
+               }
+
+               if (!(rids = talloc_array(tmp_ctx, uint32_t, dom->num_idxs))) {
+                       result = NT_STATUS_NO_MEMORY;
+                       goto fail;
                }
 
                for (j=0; j<dom->num_idxs; j++) {
@@ -918,7 +1156,7 @@ NTSTATUS lookup_sids(TALLOC_CTX *mem_ctx, int num_sids,
                        result = NT_STATUS_NO_MEMORY;
                        goto fail;
                }
-                       
+
                for (j=0; j<dom->num_idxs; j++) {
                        int idx = dom->idxs[j];
                        name_infos[idx].type = types[j];
@@ -949,7 +1187,7 @@ NTSTATUS lookup_sids(TALLOC_CTX *mem_ctx, int num_sids,
 
 /*****************************************************************
  *THE CANONICAL* convert SID to name function.
-*****************************************************************/  
+*****************************************************************/
 
 bool lookup_sid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid,
                const char **ret_domain, const char **ret_name,
@@ -957,10 +1195,12 @@ bool lookup_sid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid,
 {
        struct lsa_dom_info *domain;
        struct lsa_name_info *name;
+       struct dom_sid_buf buf;
        TALLOC_CTX *tmp_ctx;
        bool ret = false;
 
-       DEBUG(10, ("lookup_sid called for SID '%s'\n", sid_string_dbg(sid)));
+       DEBUG(10, ("lookup_sid called for SID '%s'\n",
+                  dom_sid_str_buf(sid, &buf)));
 
        if (!(tmp_ctx = talloc_new(mem_ctx))) {
                DEBUG(0, ("talloc_new failed\n"));
@@ -981,7 +1221,7 @@ bool lookup_sid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid,
                goto done;
        }
 
-       if ((ret_name != NULL) && 
+       if ((ret_name != NULL) &&
            !(*ret_name = talloc_strdup(mem_ctx, name->name))) {
                goto done;
        }
@@ -994,423 +1234,362 @@ bool lookup_sid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid,
 
  done:
        if (ret) {
-               DEBUG(10, ("Sid %s -> %s\\%s(%d)\n", sid_string_dbg(sid),
+               DEBUG(10, ("Sid %s -> %s\\%s(%d)\n",
+                          dom_sid_str_buf(sid, &buf),
                           domain->name, name->name, name->type));
        } else {
-               DEBUG(10, ("failed to lookup sid %s\n", sid_string_dbg(sid)));
+               DEBUG(10, ("failed to lookup sid %s\n",
+                          dom_sid_str_buf(sid, &buf)));
        }
        TALLOC_FREE(tmp_ctx);
        return ret;
 }
 
 /*****************************************************************
- Id mapping cache.  This is to avoid Winbind mappings already
- seen by smbd to be queried too frequently, keeping winbindd
- busy, and blocking smbd while winbindd is busy with other
- stuff. Written by Michael Steffens <michael.steffens@hp.com>,
- modified to use linked lists by jra.
-*****************************************************************/  
-
-/*****************************************************************
-  Find a SID given a uid.
+ *THE LEGACY* convert SID to id function.
 *****************************************************************/
 
-static bool fetch_sid_from_uid_cache(struct dom_sid *psid, uid_t uid)
+static bool legacy_sid_to_unixid(const struct dom_sid *psid, struct unixid *id)
 {
-       DATA_BLOB cache_value;
-
-       if (!memcache_lookup(NULL, UID_SID_CACHE,
-                            data_blob_const(&uid, sizeof(uid)),
-                            &cache_value)) {
-               return false;
-       }
-
-       memcpy(psid, cache_value.data, MIN(sizeof(*psid), cache_value.length));
-       SMB_ASSERT(cache_value.length >= offsetof(struct dom_sid, id_auth));
-       SMB_ASSERT(cache_value.length == ndr_size_dom_sid(psid, 0));
-
-       return true;
-}
-
-/*****************************************************************
-  Find a uid given a SID.
-*****************************************************************/
+       bool ret;
 
-static bool fetch_uid_from_cache( uid_t *puid, const struct dom_sid *psid )
-{
-       DATA_BLOB cache_value;
+       become_root();
+       ret = pdb_sid_to_id(psid, id);
+       unbecome_root();
 
-       if (!memcache_lookup(NULL, SID_UID_CACHE,
-                            data_blob_const(psid, ndr_size_dom_sid(psid, 0)),
-                            &cache_value)) {
+       if (!ret) {
+               struct dom_sid_buf buf;
+               DEBUG(10,("LEGACY: mapping failed for sid %s\n",
+                         dom_sid_str_buf(psid, &buf)));
                return false;
        }
 
-       SMB_ASSERT(cache_value.length == sizeof(*puid));
-       memcpy(puid, cache_value.data, sizeof(*puid));
-
        return true;
 }
 
-/*****************************************************************
- Store uid to SID mapping in cache.
-*****************************************************************/
-
-void store_uid_sid_cache(const struct dom_sid *psid, uid_t uid)
-{
-       memcache_add(NULL, SID_UID_CACHE,
-                    data_blob_const(psid, ndr_size_dom_sid(psid, 0)),
-                    data_blob_const(&uid, sizeof(uid)));
-       memcache_add(NULL, UID_SID_CACHE,
-                    data_blob_const(&uid, sizeof(uid)),
-                    data_blob_const(psid, ndr_size_dom_sid(psid, 0)));
-}
-
-/*****************************************************************
-  Find a SID given a gid.
-*****************************************************************/
-
-static bool fetch_sid_from_gid_cache(struct dom_sid *psid, gid_t gid)
+static bool legacy_sid_to_gid(const struct dom_sid *psid, gid_t *pgid)
 {
-       DATA_BLOB cache_value;
-
-       if (!memcache_lookup(NULL, GID_SID_CACHE,
-                            data_blob_const(&gid, sizeof(gid)),
-                            &cache_value)) {
+       struct unixid id;
+       if (!legacy_sid_to_unixid(psid, &id)) {
                return false;
        }
-
-       memcpy(psid, cache_value.data, MIN(sizeof(*psid), cache_value.length));
-       SMB_ASSERT(cache_value.length >= offsetof(struct dom_sid, id_auth));
-       SMB_ASSERT(cache_value.length == ndr_size_dom_sid(psid, 0));
-
-       return true;
+       if (id.type == ID_TYPE_GID || id.type == ID_TYPE_BOTH) {
+               *pgid = id.id;
+               return true;
+       }
+       return false;
 }
 
-/*****************************************************************
-  Find a gid given a SID.
-*****************************************************************/
-
-static bool fetch_gid_from_cache(gid_t *pgid, const struct dom_sid *psid)
+static bool legacy_sid_to_uid(const struct dom_sid *psid, uid_t *puid)
 {
-       DATA_BLOB cache_value;
-
-       if (!memcache_lookup(NULL, SID_GID_CACHE,
-                            data_blob_const(psid, ndr_size_dom_sid(psid, 0)),
-                            &cache_value)) {
+       struct unixid id;
+       if (!legacy_sid_to_unixid(psid, &id)) {
                return false;
        }
-
-       SMB_ASSERT(cache_value.length == sizeof(*pgid));
-       memcpy(pgid, cache_value.data, sizeof(*pgid));
-
-       return true;
-}
-
-/*****************************************************************
- Store gid to SID mapping in cache.
-*****************************************************************/
-
-void store_gid_sid_cache(const struct dom_sid *psid, gid_t gid)
-{
-       memcache_add(NULL, SID_GID_CACHE,
-                    data_blob_const(psid, ndr_size_dom_sid(psid, 0)),
-                    data_blob_const(&gid, sizeof(gid)));
-       memcache_add(NULL, GID_SID_CACHE,
-                    data_blob_const(&gid, sizeof(gid)),
-                    data_blob_const(psid, ndr_size_dom_sid(psid, 0)));
+       if (id.type == ID_TYPE_UID || id.type == ID_TYPE_BOTH) {
+               *puid = id.id;
+               return true;
+       }
+       return false;
 }
 
-/*****************************************************************
- *THE LEGACY* convert uid_t to SID function.
-*****************************************************************/  
-
-static void legacy_uid_to_sid(struct dom_sid *psid, uid_t uid)
+void xid_to_sid(struct dom_sid *psid, const struct unixid *xid)
 {
+       bool expired = true;
        bool ret;
+       struct dom_sid_buf buf;
 
-       ZERO_STRUCTP(psid);
+       SMB_ASSERT(xid->type == ID_TYPE_UID || xid->type == ID_TYPE_GID);
 
-       become_root();
-       ret = pdb_uid_to_sid(uid, psid);
-       unbecome_root();
+       *psid = (struct dom_sid) {0};
 
-       if (ret) {
-               /* This is a mapped user */
+       ret = idmap_cache_find_xid2sid(xid, psid, &expired);
+       if (ret && !expired) {
+               DBG_DEBUG("%cID %"PRIu32" -> %s from cache\n",
+                         xid->type == ID_TYPE_UID ? 'U' : 'G',
+                         xid->id,
+                         dom_sid_str_buf(psid, &buf));
                goto done;
        }
 
-       /* This is an unmapped user */
-
-       uid_to_unix_users_sid(uid, psid);
-
- done:
-       DEBUG(10,("LEGACY: uid %u -> sid %s\n", (unsigned int)uid,
-                 sid_string_dbg(psid)));
-
-       store_uid_sid_cache(psid, uid);
-       return;
-}
-
-/*****************************************************************
- *THE LEGACY* convert gid_t to SID function.
-*****************************************************************/  
-
-static void legacy_gid_to_sid(struct dom_sid *psid, gid_t gid)
-{
-       bool ret;
-
-       ZERO_STRUCTP(psid);
-
-       become_root();
-       ret = pdb_gid_to_sid(gid, psid);
-       unbecome_root();
-
+       ret = winbind_xid_to_sid(psid, xid);
        if (ret) {
-               /* This is a mapped group */
+               /*
+                * winbind can return an explicit negative mapping
+                * here. It's up to winbind to prime the cache either
+                * positively or negatively, don't mess with the cache
+                * here.
+                */
+               DBG_DEBUG("%cID %"PRIu32" -> %s from cache\n",
+                         xid->type == ID_TYPE_UID ? 'U' : 'G',
+                         xid->id,
+                         dom_sid_str_buf(psid, &buf));
                goto done;
        }
-       
-       /* This is an unmapped group */
-
-       gid_to_unix_groups_sid(gid, psid);
 
- done:
-       DEBUG(10,("LEGACY: gid %u -> sid %s\n", (unsigned int)gid,
-                 sid_string_dbg(psid)));
-
-       store_gid_sid_cache(psid, gid);
-       return;
-}
-
-/*****************************************************************
- *THE LEGACY* convert SID to uid function.
-*****************************************************************/  
-
-static bool legacy_sid_to_uid(const struct dom_sid *psid, uid_t *puid)
-{
-       enum lsa_SidType type;
-
-       if (sid_check_is_in_our_domain(psid)) {
-               union unid_t id;
-               bool ret;
+       {
+               /*
+                * Make a copy, pdb_id_to_sid might want to turn
+                * xid->type into ID_TYPE_BOTH, which we ignore here.
+                */
+               struct unixid rw_xid = *xid;
 
                become_root();
-               ret = pdb_sid_to_id(psid, &id, &type);
+               ret = pdb_id_to_sid(&rw_xid, psid);
                unbecome_root();
-
-               if (ret) {
-                       if (type != SID_NAME_USER) {
-                               DEBUG(5, ("sid %s is a %s, expected a user\n",
-                                         sid_string_dbg(psid),
-                                         sid_type_lookup(type)));
-                               return false;
-                       }
-                       *puid = id.uid;
-                       goto done;
-               }
-
-               /* This was ours, but it was not mapped.  Fail */
        }
 
-       DEBUG(10,("LEGACY: mapping failed for sid %s\n",
-                 sid_string_dbg(psid)));
-       return false;
+       if (ret) {
+               DBG_DEBUG("%cID %"PRIu32" -> %s from passdb\n",
+                         xid->type == ID_TYPE_UID ? 'U' : 'G',
+                         xid->id,
+                         dom_sid_str_buf(psid, &buf));
+               goto done;
+       }
 
 done:
-       DEBUG(10,("LEGACY: sid %s -> uid %u\n", sid_string_dbg(psid),
-                 (unsigned int)*puid ));
+       if (is_null_sid(psid)) {
+               /*
+                * Nobody found anything: Return S-1-22-xx-yy. Don't
+                * store that in caches, this is up to the layers
+                * beneath us.
+                */
+               if (xid->type == ID_TYPE_UID) {
+                       uid_to_unix_users_sid(xid->id, psid);
+               } else {
+                       gid_to_unix_groups_sid(xid->id, psid);
+               }
 
-       store_uid_sid_cache(psid, *puid);
-       return true;
+               DBG_DEBUG("%cID %"PRIu32" -> %s fallback\n",
+                         xid->type == ID_TYPE_UID ? 'U' : 'G',
+                         xid->id,
+                         dom_sid_str_buf(psid, &buf));
+       }
 }
 
-/*****************************************************************
- *THE LEGACY* convert SID to gid function.
- Group mapping is used for gids that maps to Wellknown SIDs
-*****************************************************************/  
-
-static bool legacy_sid_to_gid(const struct dom_sid *psid, gid_t *pgid)
+void uid_to_sid(struct dom_sid *psid, uid_t uid)
 {
-       GROUP_MAP map;
-       union unid_t id;
-       enum lsa_SidType type;
+       struct unixid xid = { .type = ID_TYPE_UID, .id = uid};
+       xid_to_sid(psid, &xid);
+}
 
-       if ((sid_check_is_in_builtin(psid) ||
-            sid_check_is_in_wellknown_domain(psid))) {
-               bool ret;
+void gid_to_sid(struct dom_sid *psid, gid_t gid)
+{
+       struct unixid xid = { .type = ID_TYPE_GID, .id = gid};
+       xid_to_sid(psid, &xid);
+}
 
-               become_root();
-               ret = pdb_getgrsid(&map, *psid);
-               unbecome_root();
+bool sids_to_unixids(const struct dom_sid *sids, uint32_t num_sids,
+                    struct unixid *ids)
+{
+       struct wbcDomainSid *wbc_sids = NULL;
+       struct wbcUnixId *wbc_ids = NULL;
+       struct bitmap *found = NULL;
+       uint32_t i, num_not_cached;
+       uint32_t wbc_ids_size = 0;
+       wbcErr err;
+       bool ret = false;
 
-               if (ret) {
-                       *pgid = map.gid;
-                       goto done;
-               }
-               DEBUG(10,("LEGACY: mapping failed for sid %s\n",
-                         sid_string_dbg(psid)));
+       wbc_sids = talloc_array(talloc_tos(), struct wbcDomainSid, num_sids);
+       if (wbc_sids == NULL) {
                return false;
        }
+       found = bitmap_talloc(wbc_sids, num_sids);
+       if (found == NULL) {
+               goto fail;
+       }
 
-       if (sid_check_is_in_our_domain(psid)) {
-               bool ret;
+       /*
+        * We go through the requested SID array three times.
+        * First time to look for global_sid_Unix_Users
+        * and global_sid_Unix_Groups SIDS, and to look
+        * for mappings cached in the idmap_cache.
+        *
+        * Use bitmap_set() to mark an ids[] array entry as
+        * being mapped.
+        */
 
-               become_root();
-               ret = pdb_sid_to_id(psid, &id, &type);
-               unbecome_root();
+       num_not_cached = 0;
 
-               if (ret) {
-                       if ((type != SID_NAME_DOM_GRP) &&
-                           (type != SID_NAME_ALIAS)) {
-                               DEBUG(5, ("LEGACY: sid %s is a %s, expected "
-                                         "a group\n", sid_string_dbg(psid),
-                                         sid_type_lookup(type)));
-                               return false;
-                       }
-                       *pgid = id.gid;
-                       goto done;
+       for (i=0; i<num_sids; i++) {
+               bool expired;
+               uint32_t rid;
+
+               if (sid_peek_check_rid(&global_sid_Unix_Users,
+                                      &sids[i], &rid)) {
+                       ids[i].type = ID_TYPE_UID;
+                       ids[i].id = rid;
+                       bitmap_set(found, i);
+                       continue;
                }
-       
-               /* This was ours, but it was not mapped.  Fail */
+               if (sid_peek_check_rid(&global_sid_Unix_Groups,
+                                      &sids[i], &rid)) {
+                       ids[i].type = ID_TYPE_GID;
+                       ids[i].id = rid;
+                       bitmap_set(found, i);
+                       continue;
+               }
+               if (idmap_cache_find_sid2unixid(&sids[i], &ids[i], &expired)
+                   && !expired)
+               {
+                       bitmap_set(found, i);
+                       continue;
+               }
+               ids[i].type = ID_TYPE_NOT_SPECIFIED;
+               memcpy(&wbc_sids[num_not_cached], &sids[i],
+                      ndr_size_dom_sid(&sids[i], 0));
+               num_not_cached += 1;
+       }
+       if (num_not_cached == 0) {
+               goto done;
        }
 
-       DEBUG(10,("LEGACY: mapping failed for sid %s\n",
-                 sid_string_dbg(psid)));
-       return false;
-       
- done:
-       DEBUG(10,("LEGACY: sid %s -> gid %u\n", sid_string_dbg(psid),
-                 (unsigned int)*pgid ));
-
-       store_gid_sid_cache(psid, *pgid);
+       /*
+        * For the ones that we couldn't map in the loop above, query winbindd
+        * via wbcSidsToUnixIds().
+        */
 
-       return true;
-}
+       wbc_ids_size = num_not_cached;
+       wbc_ids = talloc_array(talloc_tos(), struct wbcUnixId, wbc_ids_size);
+       if (wbc_ids == NULL) {
+               goto fail;
+       }
+       for (i=0; i<wbc_ids_size; i++) {
+               wbc_ids[i].type = WBC_ID_TYPE_NOT_SPECIFIED;
+               wbc_ids[i].id.gid = (uint32_t)-1;
+       }
+       err = wbcSidsToUnixIds(wbc_sids, wbc_ids_size, wbc_ids);
+       if (!WBC_ERROR_IS_OK(err)) {
+               DEBUG(10, ("wbcSidsToUnixIds returned %s\n",
+                          wbcErrorString(err)));
+       }
 
-/*****************************************************************
- *THE CANONICAL* convert uid_t to SID function.
-*****************************************************************/  
+       /*
+        * Second time through the SID array, replace
+        * the ids[] entries that wbcSidsToUnixIds() was able to
+        * map.
+        *
+        * Use bitmap_set() to mark an ids[] array entry as
+        * being mapped.
+        */
 
-void uid_to_sid(struct dom_sid *psid, uid_t uid)
-{
-       bool expired = true;
-       bool ret;
-       ZERO_STRUCTP(psid);
+       num_not_cached = 0;
 
-       if (fetch_sid_from_uid_cache(psid, uid))
-               return;
+       for (i=0; i<num_sids; i++) {
+               if (bitmap_query(found, i)) {
+                       continue;
+               }
 
-       /* Check the winbindd cache directly. */
-       ret = idmap_cache_find_uid2sid(uid, psid, &expired);
+               SMB_ASSERT(num_not_cached < wbc_ids_size);
 
-       if (ret && !expired && is_null_sid(psid)) {
-               /*
-                * Negative cache entry, we already asked.
-                * do legacy.
-                */
-               legacy_uid_to_sid(psid, uid);
-               return;
-       }
-
-       if (!ret || expired) {
-               /* Not in cache. Ask winbindd. */
-               if (!winbind_uid_to_sid(psid, uid)) {
+               switch (wbc_ids[num_not_cached].type) {
+               case WBC_ID_TYPE_UID:
+                       ids[i].type = ID_TYPE_UID;
+                       ids[i].id = wbc_ids[num_not_cached].id.uid;
+                       bitmap_set(found, i);
+                       break;
+               case WBC_ID_TYPE_GID:
+                       ids[i].type = ID_TYPE_GID;
+                       ids[i].id = wbc_ids[num_not_cached].id.gid;
+                       bitmap_set(found, i);
+                       break;
+               case WBC_ID_TYPE_BOTH:
+                       ids[i].type = ID_TYPE_BOTH;
+                       ids[i].id = wbc_ids[num_not_cached].id.uid;
+                       bitmap_set(found, i);
+                       break;
+               case WBC_ID_TYPE_NOT_SPECIFIED:
                        /*
-                        * We shouldn't return the NULL SID
-                        * here if winbind was running and
-                        * couldn't map, as winbind will have
-                        * added a negative entry that will
-                        * cause us to go though the
-                        * legacy_uid_to_sid()
-                        * function anyway in the case above
-                        * the next time we ask.
+                        * wbcSidsToUnixIds() wasn't able to map this
+                        * so we still need to check legacy_sid_to_XXX()
+                        * below. Don't mark the bitmap entry
+                        * as being found so the final loop knows
+                        * to try and map this entry.
                         */
-                       DEBUG(5, ("uid_to_sid: winbind failed to find a sid "
-                                 "for uid %u\n", (unsigned int)uid));
-
-                       legacy_uid_to_sid(psid, uid);
-                       return;
+                       ids[i].type = ID_TYPE_NOT_SPECIFIED;
+                       ids[i].id = (uint32_t)-1;
+                       break;
+               default:
+                       /*
+                        * A successful return from wbcSidsToUnixIds()
+                        * cannot return anything other than the values
+                        * checked for above. Ensure this is so.
+                        */
+                       smb_panic(__location__);
+                       break;
                }
+               num_not_cached += 1;
        }
 
-       DEBUG(10,("uid %u -> sid %s\n", (unsigned int)uid,
-                 sid_string_dbg(psid)));
-
-       store_uid_sid_cache(psid, uid);
-       return;
-}
-
-/*****************************************************************
- *THE CANONICAL* convert gid_t to SID function.
-*****************************************************************/  
-
-void gid_to_sid(struct dom_sid *psid, gid_t gid)
-{
-       bool expired = true;
-       bool ret;
-       ZERO_STRUCTP(psid);
-
-       if (fetch_sid_from_gid_cache(psid, gid))
-               return;
-
-       /* Check the winbindd cache directly. */
-       ret = idmap_cache_find_gid2sid(gid, psid, &expired);
+       /*
+        * Third and final time through the SID array,
+        * try legacy_sid_to_gid()/legacy_sid_to_uid()
+        * for entries we haven't already been able to
+        * map.
+        *
+        * Use bitmap_set() to mark an ids[] array entry as
+        * being mapped.
+        */
 
-       if (ret && !expired && is_null_sid(psid)) {
-               /*
-                * Negative cache entry, we already asked.
-                * do legacy.
-                */
-               legacy_gid_to_sid(psid, gid);
-               return;
+       for (i=0; i<num_sids; i++) {
+               if (bitmap_query(found, i)) {
+                       continue;
+               }
+               if (legacy_sid_to_gid(&sids[i], &ids[i].id)) {
+                       ids[i].type = ID_TYPE_GID;
+                       bitmap_set(found, i);
+                       continue;
+               }
+               if (legacy_sid_to_uid(&sids[i], &ids[i].id)) {
+                       ids[i].type = ID_TYPE_UID;
+                       bitmap_set(found, i);
+                       continue;
+               }
        }
-
-       if (!ret || expired) {
-               /* Not in cache. Ask winbindd. */
-               if (!winbind_gid_to_sid(psid, gid)) {
+done:
+       /*
+        * Pass through the return array for consistency.
+        * Any ids[].id mapped to (uint32_t)-1 must be returned
+        * as ID_TYPE_NOT_SPECIFIED.
+        */
+       for (i=0; i<num_sids; i++) {
+               switch(ids[i].type) {
+               case ID_TYPE_GID:
+               case ID_TYPE_UID:
+               case ID_TYPE_BOTH:
+                       if (ids[i].id == (uint32_t)-1) {
+                               ids[i].type = ID_TYPE_NOT_SPECIFIED;
+                       }
+                       break;
+               case ID_TYPE_NOT_SPECIFIED:
+                       break;
+               case ID_TYPE_WB_REQUIRE_TYPE:
                        /*
-                        * We shouldn't return the NULL SID
-                        * here if winbind was running and
-                        * couldn't map, as winbind will have
-                        * added a negative entry that will
-                        * cause us to go though the
-                        * legacy_gid_to_sid()
-                        * function anyway in the case above
-                        * the next time we ask.
+                        * these are internal between winbindd
+                        * parent and child.
                         */
-                       DEBUG(5, ("gid_to_sid: winbind failed to find a sid "
-                                 "for gid %u\n", (unsigned int)gid));
-
-                       legacy_gid_to_sid(psid, gid);
-                       return;
+                       smb_panic(__location__);
+                       break;
                }
        }
 
-       DEBUG(10,("gid %u -> sid %s\n", (unsigned int)gid,
-                 sid_string_dbg(psid)));
-
-       store_gid_sid_cache(psid, gid);
-       return;
+       ret = true;
+fail:
+       TALLOC_FREE(wbc_ids);
+       TALLOC_FREE(wbc_sids);
+       return ret;
 }
 
 /*****************************************************************
  *THE CANONICAL* convert SID to uid function.
-*****************************************************************/  
+*****************************************************************/
 
 bool sid_to_uid(const struct dom_sid *psid, uid_t *puid)
 {
        bool expired = true;
        bool ret;
-       uint32 rid;
-       gid_t gid;
-
-       if (fetch_uid_from_cache(puid, psid))
-               return true;
-
-       if (fetch_gid_from_cache(&gid, psid)) {
-               return false;
-       }
+       uint32_t rid;
+       struct dom_sid_buf buf;
 
        /* Optimize for the Unix Users Domain
         * as the conversion is straightforward */
@@ -1419,11 +1598,18 @@ bool sid_to_uid(const struct dom_sid *psid, uid_t *puid)
                *puid = uid;
 
                /* return here, don't cache */
-               DEBUG(10,("sid %s -> uid %u\n", sid_string_dbg(psid),
-                       (unsigned int)*puid ));
+               DEBUG(10,("sid %s -> uid %u\n",
+                         dom_sid_str_buf(psid, &buf),
+                         (unsigned int)*puid ));
                return true;
        }
 
+       if (sid_check_is_in_unix_groups(psid)) {
+               DBG_DEBUG("SID %s is a group, failing\n",
+                         dom_sid_str_buf(psid, &buf));
+               return false;
+       }
+
        /* Check the winbindd cache directly. */
        ret = idmap_cache_find_sid2uid(psid, puid, &expired);
 
@@ -1439,7 +1625,7 @@ bool sid_to_uid(const struct dom_sid *psid, uid_t *puid)
                /* Not in cache. Ask winbindd. */
                if (!winbind_sid_to_uid(puid, psid)) {
                        DEBUG(5, ("winbind failed to find a uid for sid %s\n",
-                                 sid_string_dbg(psid)));
+                                 dom_sid_str_buf(psid, &buf)));
                        /* winbind failed. do legacy */
                        return legacy_sid_to_uid(psid, puid);
                }
@@ -1448,30 +1634,24 @@ bool sid_to_uid(const struct dom_sid *psid, uid_t *puid)
        /* TODO: Here would be the place to allocate both a gid and a uid for
         * the SID in question */
 
-       DEBUG(10,("sid %s -> uid %u\n", sid_string_dbg(psid),
+       DEBUG(10,("sid %s -> uid %u\n",
+                 dom_sid_str_buf(psid, &buf),
                (unsigned int)*puid ));
 
-       store_uid_sid_cache(psid, *puid);
        return true;
 }
 
 /*****************************************************************
  *THE CANONICAL* convert SID to gid function.
  Group mapping is used for gids that maps to Wellknown SIDs
-*****************************************************************/  
+*****************************************************************/
 
 bool sid_to_gid(const struct dom_sid *psid, gid_t *pgid)
 {
        bool expired = true;
        bool ret;
-       uint32 rid;
-       uid_t uid;
-
-       if (fetch_gid_from_cache(pgid, psid))
-               return true;
-
-       if (fetch_uid_from_cache(&uid, psid))
-               return false;
+       uint32_t rid;
+       struct dom_sid_buf buf;
 
        /* Optimize for the Unix Groups Domain
         * as the conversion is straightforward */
@@ -1480,11 +1660,18 @@ bool sid_to_gid(const struct dom_sid *psid, gid_t *pgid)
                *pgid = gid;
 
                /* return here, don't cache */
-               DEBUG(10,("sid %s -> gid %u\n", sid_string_dbg(psid),
+               DEBUG(10,("sid %s -> gid %u\n",
+                         dom_sid_str_buf(psid, &buf),
                        (unsigned int)*pgid ));
                return true;
        }
 
+       if (sid_check_is_in_unix_users(psid)) {
+               DBG_DEBUG("SID %s is a user, failing\n",
+                         dom_sid_str_buf(psid, &buf));
+               return false;
+       }
+
        /* Check the winbindd cache directly. */
        ret = idmap_cache_find_sid2gid(psid, pgid, &expired);
 
@@ -1504,16 +1691,16 @@ bool sid_to_gid(const struct dom_sid *psid, gid_t *pgid)
                if ( !winbind_sid_to_gid(pgid, psid) ) {
 
                        DEBUG(10,("winbind failed to find a gid for sid %s\n",
-                                 sid_string_dbg(psid)));
+                                 dom_sid_str_buf(psid, &buf)));
                        /* winbind failed. do legacy */
                        return legacy_sid_to_gid(psid, pgid);
                }
        }
 
-       DEBUG(10,("sid %s -> gid %u\n", sid_string_dbg(psid),
+       DEBUG(10,("sid %s -> gid %u\n",
+                 dom_sid_str_buf(psid, &buf),
                  (unsigned int)*pgid ));
 
-       store_gid_sid_cache(psid, *pgid);
        return true;
 }
 
@@ -1559,7 +1746,7 @@ NTSTATUS get_primary_group_sid(TALLOC_CTX *mem_ctx,
        if (!pwd) {
                pwd = Get_Pwnam_alloc(mem_ctx, username);
                if (!pwd) {
-                       DEBUG(0, ("Failed to find a Unix account for %s",
+                       DEBUG(0, ("Failed to find a Unix account for %s\n",
                                  username));
                        TALLOC_FREE(tmp_ctx);
                        return NT_STATUS_NO_SUCH_USER;
@@ -1580,7 +1767,7 @@ NTSTATUS get_primary_group_sid(TALLOC_CTX *mem_ctx,
                /* We need a sid within our domain */
                sid_copy(&domain_sid, group_sid);
                sid_split_rid(&domain_sid, &rid);
-               if (sid_equal(&domain_sid, get_global_sam_sid())) {
+               if (dom_sid_equal(&domain_sid, get_global_sam_sid())) {
                        /*
                         * As shortcut for the expensive lookup_sid call
                         * compare the domain sid part
@@ -1595,8 +1782,13 @@ NTSTATUS get_primary_group_sid(TALLOC_CTX *mem_ctx,
                        }
                } else {
                        /* Try group mapping */
+                       struct unixid id;
+
+                       id.id = pwd->pw_gid;
+                       id.type = ID_TYPE_GID;
+
                        ZERO_STRUCTP(group_sid);
-                       if (pdb_gid_to_sid(pwd->pw_gid, group_sid)) {
+                       if (pdb_id_to_sid(&id, group_sid)) {
                                need_lookup_sid = true;
                        }
                }
@@ -1607,9 +1799,11 @@ NTSTATUS get_primary_group_sid(TALLOC_CTX *mem_ctx,
        if (need_lookup_sid) {
                enum lsa_SidType type = SID_NAME_UNKNOWN;
                bool lookup_ret;
+               struct dom_sid_buf buf;
 
                DEBUG(10, ("do lookup_sid(%s) for group of user %s\n",
-                          sid_string_dbg(group_sid), username));
+                          dom_sid_str_buf(group_sid, &buf),
+                          username));
 
                /* Now check that it's actually a domain group and
                 * not something else */
@@ -1622,7 +1816,8 @@ NTSTATUS get_primary_group_sid(TALLOC_CTX *mem_ctx,
 
                DEBUG(3, ("Primary group %s for user %s is"
                          " a %s and not a domain group\n",
-                         sid_string_dbg(group_sid), username,
+                         dom_sid_str_buf(group_sid, &buf),
+                         username,
                          sid_type_lookup(type)));
        }
 
@@ -1640,3 +1835,4 @@ done:
        TALLOC_FREE(tmp_ctx);
        return NT_STATUS_OK;
 }
+