docs: fix a typo in history file
[bbaumbach/samba-autobuild/.git] / source3 / passdb / lookup_sid.c
index a5c2d503662da13ea85d4445f7d7a144be577844..426ea3f81bda52675af4ef59d63993d1c3e76bb1 100644 (file)
@@ -1,25 +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 "../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,
-                DOM_SID *ret_sid, enum lsa_SidType *ret_type)
+                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;
-       DOM_SID sid;
+       uint32_t rid;
+       struct dom_sid sid;
        enum lsa_SidType type;
        TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
 
@@ -55,8 +100,18 @@ 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)) {
@@ -65,31 +120,56 @@ bool lookup_name(TALLOC_CTX *mem_ctx,
                return false;
        }
 
-       DEBUG(10,("lookup_name: %s => %s (domain), %s (name)\n",
+       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_copy(&sid, get_global_sam_sid());
-                       sid_append_rid(&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);
+                       return false;
                }
-               TALLOC_FREE(tmp_ctx);
-               return false;
        }
 
        if ((flags & LOOKUP_NAME_BUILTIN) &&
            strequal(domain, builtin_domain_name()))
        {
+               if (strlen(name) == 0) {
+                       /* Swap domain and name */
+                       tmp = name; name = domain; domain = tmp;
+                       sid_copy(&sid, &global_sid_Builtin);
+                       type = SID_NAME_DOMAIN;
+                       goto ok;
+               }
+
                /* Explicit request for a name in BUILTIN */
                if (lookup_builtin_name(name, &rid)) {
-                       sid_copy(&sid, &global_sid_Builtin);
-                       sid_append_rid(&sid, rid);
+                       sid_compose(&sid, &global_sid_Builtin, rid);
                        type = SID_NAME_ALIAS;
                        goto ok;
                }
@@ -106,7 +186,8 @@ bool lookup_name(TALLOC_CTX *mem_ctx,
                        goto ok;
        }
 
-       if (!(flags & LOOKUP_NAME_EXPLICIT) && strequal(domain, unix_users_domain_name())) {
+       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;
@@ -115,7 +196,8 @@ bool lookup_name(TALLOC_CTX *mem_ctx,
                return false;
        }
 
-       if (!(flags & LOOKUP_NAME_EXPLICIT) && strequal(domain, unix_groups_domain_name())) {
+       if (((flags & LOOKUP_NAME_NO_NSS) == 0)
+           && strequal(domain, unix_groups_domain_name())) {
                if (lookup_unix_group_name(name, &sid)) {
                        type = SID_NAME_DOM_GRP;
                        goto ok;
@@ -124,7 +206,31 @@ bool lookup_name(TALLOC_CTX *mem_ctx,
                return false;
        }
 
-       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;
+       }
+
+       /*
+        * No domain names beyond this point
+        */
+       if (domain[0] != '\0') {
                TALLOC_FREE(tmp_ctx);
                return false;
        }
@@ -136,6 +242,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))
        {
@@ -199,14 +310,13 @@ 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))
        {
                domain = talloc_strdup(tmp_ctx, builtin_domain_name());
-               sid_copy(&sid, &global_sid_Builtin);
-               sid_append_rid(&sid, rid);
+               sid_compose(&sid, &global_sid_Builtin, rid);
                type = SID_NAME_ALIAS;
                goto ok;
        }
@@ -220,8 +330,7 @@ bool lookup_name(TALLOC_CTX *mem_ctx,
            lookup_global_sam_name(name, flags, &rid, &type))
        {
                domain = talloc_strdup(tmp_ctx, get_global_sam_name());
-               sid_copy(&sid, get_global_sam_sid());
-               sid_append_rid(&sid, rid);
+               sid_compose(&sid, get_global_sam_sid(), rid);
                goto ok;
        }
 
@@ -247,10 +356,9 @@ bool lookup_name(TALLOC_CTX *mem_ctx,
         * that (yet), but give it a chance. */
 
        if (IS_DC && winbind_lookup_name("", name, &sid, &type)) {
-               DOM_SID dom_sid;
-               uint32 tmp_rid;
+               struct dom_sid dom_sid;
                enum lsa_SidType domain_type;
-               
+
                if (type == SID_NAME_DOMAIN) {
                        /* Swap name and type */
                        tmp = name; name = domain; domain = tmp;
@@ -262,7 +370,7 @@ bool lookup_name(TALLOC_CTX *mem_ctx,
                 * domain it figured out itself. Maybe fix that later... */
 
                sid_copy(&dom_sid, &sid);
-               sid_split_rid(&dom_sid, &tmp_rid);
+               sid_split_rid(&dom_sid, NULL);
 
                if (!winbind_lookup_sid(tmp_ctx, &dom_sid, &domain, NULL,
                                        &domain_type) ||
@@ -280,13 +388,15 @@ 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_EXPLICIT) && lookup_unix_user_name(name, &sid)) {
+       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;
                goto ok;
        }
 
-       if (!(flags & LOOKUP_NAME_EXPLICIT) && lookup_unix_group_name(name, &sid)) {
+       if (((flags & LOOKUP_NAME_NO_NSS) == 0)
+           && lookup_unix_group_name(name, &sid)) {
                domain = talloc_strdup(tmp_ctx, unix_groups_domain_name());
                type = SID_NAME_DOM_GRP;
                goto ok;
@@ -324,7 +434,10 @@ bool lookup_name(TALLOC_CTX *mem_ctx,
                        TALLOC_FREE(tmp_ctx);
                        return false;
                }
-               strupper_m(tmp_dom);
+               if (!strupper_m(tmp_dom)) {
+                       TALLOC_FREE(tmp_ctx);
+                       return false;
+               }
                *ret_domain = tmp_dom;
        }
 
@@ -349,27 +462,26 @@ bool lookup_name(TALLOC_CTX *mem_ctx,
 bool lookup_name_smbconf(TALLOC_CTX *mem_ctx,
                 const char *full_name, int flags,
                 const char **ret_domain, const char **ret_name,
-                DOM_SID *ret_sid, enum lsa_SidType *ret_type)
+                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;
 
-       /* 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) {
+                       qualified_name = talloc_strdup(mem_ctx, full_name);
+                       if (qualified_name == NULL) {
                                return false;
                        }
-                       tmp[p - full_name] = '\\';
-                       full_name = tmp;
+                       qualified_name[p - full_name] = '\\';
+                       full_name = qualified_name;
                }
 
                return lookup_name(mem_ctx, full_name, flags,
@@ -377,6 +489,30 @@ bool lookup_name_smbconf(TALLOC_CTX *mem_ctx,
                                ret_sid, ret_type);
        }
 
+       /* Try with winbind default domain name. */
+       if (lp_winbind_use_default_domain()) {
+               bool ok;
+
+               qualified_name = talloc_asprintf(mem_ctx,
+                                                "%s\\%s",
+                                                lp_workgroup(),
+                                                full_name);
+               if (qualified_name == NULL) {
+                       return false;
+               }
+
+               ok = lookup_name(mem_ctx,
+                                qualified_name,
+                                flags,
+                                ret_domain,
+                                ret_name,
+                                ret_sid,
+                                ret_type);
+               if (ok) {
+                       return true;
+               }
+       }
+
        /* Try with our own SAM name. */
        qualified_name = talloc_asprintf(mem_ctx, "%s\\%s",
                                get_global_sam_name(),
@@ -390,7 +526,7 @@ bool lookup_name_smbconf(TALLOC_CTX *mem_ctx,
                                ret_sid, ret_type)) {
                return true;
        }
-       
+
        /* Finally try with "Unix Users" or "Unix Group" */
        qualified_name = talloc_asprintf(mem_ctx, "%s\\%s",
                                flags & LOOKUP_NAME_GROUP ?
@@ -407,8 +543,8 @@ bool lookup_name_smbconf(TALLOC_CTX *mem_ctx,
 }
 
 static bool wb_lookup_rids(TALLOC_CTX *mem_ctx,
-                          const DOM_SID *domain_sid,
-                          int num_rids, uint32 *rids,
+                          const struct dom_sid *domain_sid,
+                          int num_rids, uint32_t *rids,
                           const char **domain_name,
                           const char **names, enum lsa_SidType *types)
 {
@@ -457,29 +593,33 @@ static bool wb_lookup_rids(TALLOC_CTX *mem_ctx,
        return true;
 }
 
-static bool lookup_rids(TALLOC_CTX *mem_ctx, const DOM_SID *domain_sid,
+static bool lookup_rids(TALLOC_CTX *mem_ctx, const struct dom_sid *domain_sid,
                        int num_rids, uint32_t *rids,
                        const char **domain_name,
                        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_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;
                }
+
+               for (i = 0; i < num_rids; i++)
+                       (*types)[i] = SID_NAME_UNKNOWN;
        } else {
                *names = NULL;
                *types = NULL;
        }
 
-       if (sid_check_is_domain(domain_sid)) {
+       if (sid_check_is_our_sam(domain_sid)) {
                NTSTATUS result;
 
                if (*domain_name == NULL) {
@@ -528,9 +668,8 @@ static bool lookup_rids(TALLOC_CTX *mem_ctx, const DOM_SID *domain_sid,
 
        if (sid_check_is_wellknown_domain(domain_sid, NULL)) {
                for (i=0; i<num_rids; i++) {
-                       DOM_SID sid;
-                       sid_copy(&sid, domain_sid);
-                       sid_append_rid(&sid, rids[i]);
+                       struct dom_sid sid;
+                       sid_compose(&sid, domain_sid, rids[i]);
                        if (lookup_wellknown_sid(mem_ctx, &sid,
                                                 domain_name, &(*names)[i])) {
                                if ((*names)[i] == NULL) {
@@ -590,13 +729,13 @@ static bool lookup_rids(TALLOC_CTX *mem_ctx, const DOM_SID *domain_sid,
  * Is the SID a domain as such? If yes, lookup its name.
  */
 
-static bool lookup_as_domain(const DOM_SID *sid, TALLOC_CTX *mem_ctx,
+static bool lookup_as_domain(const struct dom_sid *sid, TALLOC_CTX *mem_ctx,
                             const char **name)
 {
        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;
        }
@@ -627,7 +766,7 @@ static bool lookup_as_domain(const 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
@@ -641,7 +780,7 @@ static bool lookup_as_domain(const 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;
@@ -678,8 +817,9 @@ static bool lookup_as_domain(const DOM_SID *sid, TALLOC_CTX *mem_ctx,
  * Level 6: Like 4
  */
 
-static bool check_dom_sid_to_level(const DOM_SID *sid, int level)
+static bool check_dom_sid_to_level(const struct dom_sid *sid, int level)
 {
+       struct dom_sid_buf buf;
        int ret = false;
 
        switch(level) {
@@ -693,7 +833,7 @@ static bool check_dom_sid_to_level(const 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;
@@ -702,7 +842,8 @@ static bool check_dom_sid_to_level(const 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;
 }
 
@@ -713,18 +854,16 @@ static bool check_dom_sid_to_level(const 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,
-                    const DOM_SID **sids, int level,
+                    const struct dom_sid **sids, int level,
                     struct lsa_dom_info **ret_domains,
                     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;
 
@@ -736,7 +875,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;
@@ -745,7 +884,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;
@@ -753,7 +892,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.
@@ -767,8 +906,8 @@ NTSTATUS lookup_sids(TALLOC_CTX *mem_ctx, int num_sids,
         */
 
        for (i=0; i<num_sids; i++) {
-               DOM_SID sid;
-               uint32 rid;
+               struct dom_sid sid;
+               uint32_t rid = 0;
                const char *domain_name = NULL;
 
                sid_copy(&sid, sids[i]);
@@ -786,7 +925,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;
@@ -820,7 +959,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;
                        }
                }
@@ -881,13 +1020,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++) {
@@ -905,7 +1049,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];
@@ -936,18 +1080,20 @@ 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 DOM_SID *sid,
+bool lookup_sid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid,
                const char **ret_domain, const char **ret_name,
                enum lsa_SidType *ret_type)
 {
        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"));
@@ -968,7 +1114,7 @@ bool lookup_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
                goto done;
        }
 
-       if ((ret_name != NULL) && 
+       if ((ret_name != NULL) &&
            !(*ret_name = talloc_strdup(mem_ctx, name->name))) {
                goto done;
        }
@@ -981,428 +1127,362 @@ bool lookup_sid(TALLOC_CTX *mem_ctx, const 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(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, NULL, 0));
-
-       return true;
-}
-
-/*****************************************************************
-  Find a uid given a SID.
-*****************************************************************/
+       bool ret;
 
-static bool fetch_uid_from_cache( uid_t *puid, const 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, NULL, 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 DOM_SID *psid, uid_t uid)
+static bool legacy_sid_to_gid(const struct dom_sid *psid, gid_t *pgid)
 {
-       memcache_add(NULL, SID_UID_CACHE,
-                    data_blob_const(psid, ndr_size_dom_sid(psid, NULL, 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, NULL, 0)));
-}
-
-/*****************************************************************
-  Find a SID given a gid.
-*****************************************************************/
-
-static bool fetch_sid_from_gid_cache(DOM_SID *psid, gid_t gid)
-{
-       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, NULL, 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 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_UID_CACHE,
-                            data_blob_const(psid, ndr_size_dom_sid(psid, NULL, 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 DOM_SID *psid, gid_t gid)
-{
-       memcache_add(NULL, SID_GID_CACHE,
-                    data_blob_const(psid, ndr_size_dom_sid(psid, NULL, 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, NULL, 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(DOM_SID *psid, uid_t uid)
+void xid_to_sid(struct dom_sid *psid, const struct unixid *xid)
 {
-       uint32 rid;
+       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_rid(uid, &rid);
-       unbecome_root();
+       *psid = (struct dom_sid) {0};
 
-       if (ret) {
-               /* This is a mapped user */
-               sid_copy(psid, get_global_sam_sid());
-               sid_append_rid(psid, rid);
+       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(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 DOM_SID *psid, uid_t *puid)
-{
-       enum lsa_SidType type;
-       uint32 rid;
-
-       if (sid_peek_check_rid(get_global_sam_sid(), psid, &rid)) {
-               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 DOM_SID *psid, gid_t *pgid)
+void uid_to_sid(struct dom_sid *psid, uid_t uid)
 {
-       uint32 rid;
-       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_peek_check_rid(get_global_sam_sid(), psid, &rid)) {
-               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);
-
-       return true;
-}
+       /*
+        * For the ones that we couldn't map in the loop above, query winbindd
+        * via wbcSidsToUnixIds().
+        */
 
-/*****************************************************************
- *THE CANONICAL* convert uid_t to SID function.
-*****************************************************************/  
+       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)));
+       }
 
-void uid_to_sid(DOM_SID *psid, uid_t uid)
-{
-       bool expired = true;
-       bool ret;
-       ZERO_STRUCTP(psid);
+       /*
+        * 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.
+        */
 
-       if (fetch_sid_from_uid_cache(psid, uid))
-               return;
+       num_not_cached = 0;
 
-       /* Check the winbindd cache directly. */
-       ret = idmap_cache_find_uid2sid(uid, psid, &expired);
+       for (i=0; i<num_sids; i++) {
+               if (bitmap_query(found, i)) {
+                       continue;
+               }
 
-       if (ret && !expired && is_null_sid(psid)) {
-               /*
-                * Negative cache entry, we already asked.
-                * do legacy.
-                */
-               legacy_uid_to_sid(psid, uid);
-               return;
-       }
+               SMB_ASSERT(num_not_cached < wbc_ids_size);
 
-       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", 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(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", 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 DOM_SID *psid, uid_t *puid)
+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 */
@@ -1411,11 +1491,18 @@ bool sid_to_uid(const 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);
 
@@ -1430,43 +1517,34 @@ bool sid_to_uid(const DOM_SID *psid, uid_t *puid)
        if (!ret || expired) {
                /* Not in cache. Ask winbindd. */
                if (!winbind_sid_to_uid(puid, psid)) {
-                       if (!winbind_ping()) {
-                               return legacy_sid_to_uid(psid, puid);
-                       }
-
                        DEBUG(5, ("winbind failed to find a uid for sid %s\n",
-                                 sid_string_dbg(psid)));
-                       return false;
+                                 dom_sid_str_buf(psid, &buf)));
+                       /* winbind failed. do legacy */
+                       return legacy_sid_to_uid(psid, 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 DOM_SID *psid, gid_t *pgid)
+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 */
@@ -1475,11 +1553,18 @@ bool sid_to_gid(const 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);
 
@@ -1497,19 +1582,150 @@ bool sid_to_gid(const DOM_SID *psid, gid_t *pgid)
                 * (Idmap will check it is a valid SID and of the right type) */
 
                if ( !winbind_sid_to_gid(pgid, psid) ) {
-                       if (!winbind_ping()) {
-                               return legacy_sid_to_gid(psid, pgid);
-                       }
 
                        DEBUG(10,("winbind failed to find a gid for sid %s\n",
-                                 sid_string_dbg(psid)));
-                       return false;
+                                 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;
 }
+
+/**
+ * @brief This function gets the primary group SID mapping the primary
+ *        GID of the user as obtained by an actual getpwnam() call.
+ *        This is necessary to avoid issues with arbitrary group SIDs
+ *        stored in passdb. We try as hard as we can to get the SID
+ *        corresponding to the GID, including trying group mapping.
+ *        If nothing else works, we will force "Domain Users" as the
+ *        primary group.
+ *        This is needed because we must always be able to lookup the
+ *        primary group SID, so we cannot settle for an arbitrary SID.
+ *
+ *        This call can be expensive. Use with moderation.
+ *        If you have a "samu" struct around use pdb_get_group_sid()
+ *        instead as it does properly cache results.
+ *
+ * @param mem_ctx[in]     The memory context iused to allocate the result.
+ * @param username[in]    The user's name
+ * @param _pwd[in|out]    If available, pass in user's passwd struct.
+ *                        It will contain a tallocated passwd if NULL was
+ *                        passed in.
+ * @param _group_sid[out] The user's Primary Group SID
+ *
+ * @return NTSTATUS error code.
+ */
+NTSTATUS get_primary_group_sid(TALLOC_CTX *mem_ctx,
+                               const char *username,
+                               struct passwd **_pwd,
+                               struct dom_sid **_group_sid)
+{
+       TALLOC_CTX *tmp_ctx;
+       bool need_lookup_sid = false;
+       struct dom_sid *group_sid;
+       struct passwd *pwd = *_pwd;
+
+       tmp_ctx = talloc_new(mem_ctx);
+       if (!tmp_ctx) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       if (!pwd) {
+               pwd = Get_Pwnam_alloc(mem_ctx, username);
+               if (!pwd) {
+                       DEBUG(0, ("Failed to find a Unix account for %s\n",
+                                 username));
+                       TALLOC_FREE(tmp_ctx);
+                       return NT_STATUS_NO_SUCH_USER;
+               }
+       }
+
+       group_sid = talloc_zero(mem_ctx, struct dom_sid);
+       if (!group_sid) {
+               TALLOC_FREE(tmp_ctx);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       gid_to_sid(group_sid, pwd->pw_gid);
+       if (!is_null_sid(group_sid)) {
+               struct dom_sid domain_sid;
+               uint32_t rid;
+
+               /* We need a sid within our domain */
+               sid_copy(&domain_sid, group_sid);
+               sid_split_rid(&domain_sid, &rid);
+               if (dom_sid_equal(&domain_sid, get_global_sam_sid())) {
+                       /*
+                        * As shortcut for the expensive lookup_sid call
+                        * compare the domain sid part
+                        */
+                       switch (rid) {
+                       case DOMAIN_RID_ADMINS:
+                       case DOMAIN_RID_USERS:
+                               goto done;
+                       default:
+                               need_lookup_sid = true;
+                               break;
+                       }
+               } else {
+                       /* Try group mapping */
+                       struct unixid id;
+
+                       id.id = pwd->pw_gid;
+                       id.type = ID_TYPE_GID;
+
+                       ZERO_STRUCTP(group_sid);
+                       if (pdb_id_to_sid(&id, group_sid)) {
+                               need_lookup_sid = true;
+                       }
+               }
+       }
+
+       /* We must verify that this is a valid SID that resolves to a
+        * group of the correct type */
+       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",
+                          dom_sid_str_buf(group_sid, &buf),
+                          username));
+
+               /* Now check that it's actually a domain group and
+                * not something else */
+               lookup_ret = lookup_sid(tmp_ctx, group_sid,
+                                       NULL, NULL, &type);
+
+               if (lookup_ret && (type == SID_NAME_DOM_GRP)) {
+                       goto done;
+               }
+
+               DEBUG(3, ("Primary group %s for user %s is"
+                         " a %s and not a domain group\n",
+                         dom_sid_str_buf(group_sid, &buf),
+                         username,
+                         sid_type_lookup(type)));
+       }
+
+       /* Everything else, failed.
+        * Just set it to the 'Domain Users' RID of 513 which will
+          always resolve to a name */
+       DEBUG(3, ("Forcing Primary Group to 'Domain Users' for %s\n",
+                 username));
+
+       sid_compose(group_sid, get_global_sam_sid(), DOMAIN_RID_USERS);
+
+done:
+       *_pwd = talloc_move(mem_ctx, &pwd);
+       *_group_sid = talloc_move(mem_ctx, &group_sid);
+       TALLOC_FREE(tmp_ctx);
+       return NT_STATUS_OK;
+}
+