libcli/security Provide a common, top level libcli/security/security.h
[sfrench/samba-autobuild/.git] / source3 / passdb / lookup_sid.c
index b339754c6df0f29986ef5d8475c32391dc349ee2..5c6118f8ce81459a117611a9a140f0013628fdaf 100644 (file)
@@ -7,7 +7,7 @@
    
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 #include "includes.h"
+#include "../librpc/gen_ndr/ndr_security.h"
+#include "secrets.h"
+#include "memcache.h"
+#include "idmap_cache.h"
+#include "../libcli/security/security.h"
 
 /*****************************************************************
  Dissect a user-provided name into domain, name, sid and type.
  to do guesswork.
 *****************************************************************/  
 
-BOOL lookup_name(TALLOC_CTX *mem_ctx,
+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;
+       struct dom_sid sid;
        enum lsa_SidType type;
        TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
 
        if (tmp_ctx == NULL) {
                DEBUG(0, ("talloc_new failed\n"));
-               return False;
+               return false;
        }
 
        p = strchr_m(full_name, '\\');
@@ -60,69 +64,82 @@ BOOL lookup_name(TALLOC_CTX *mem_ctx,
                name = talloc_strdup(tmp_ctx, full_name);
        }
 
-       DEBUG(10,("lookup_name: %s => %s (domain), %s (name)\n", 
-               full_name, domain, name));
-
        if ((domain == NULL) || (name == NULL)) {
                DEBUG(0, ("talloc failed\n"));
                TALLOC_FREE(tmp_ctx);
-               return False;
+               return false;
        }
 
-       if (strequal(domain, get_global_sam_name())) {
+       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()))
+       {
 
                /* 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);
+                       sid_compose(&sid, get_global_sam_sid(), rid);
                        goto ok;
                }
                TALLOC_FREE(tmp_ctx);
-               return False;
+               return false;
        }
 
-       if (strequal(domain, builtin_domain_name())) {
+       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;
                }
                TALLOC_FREE(tmp_ctx);
-               return False;
+               return false;
        }
 
        /* 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))) {
                        goto ok;
        }
 
-       if (strequal(domain, unix_users_domain_name())) {
+       if (((flags & LOOKUP_NAME_NO_NSS) == 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;
+               return false;
        }
 
-       if (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;
                }
                TALLOC_FREE(tmp_ctx);
-               return False;
+               return false;
        }
 
        if ((domain[0] == '\0') && (!(flags & LOOKUP_NAME_ISOLATED))) {
                TALLOC_FREE(tmp_ctx);
-               return False;
+               return false;
        }
 
        /* Now the guesswork begins, we haven't been given an explicit
@@ -132,14 +149,18 @@ BOOL lookup_name(TALLOC_CTX *mem_ctx,
 
        /* 1. well-known names */
 
-       if (lookup_wellknown_name(tmp_ctx, name, &sid, &domain)) {
+       if ((flags & LOOKUP_NAME_WKN) &&
+           lookup_wellknown_name(tmp_ctx, name, &sid, &domain))
+       {
                type = SID_NAME_WKN_GRP;
                goto ok;
        }
 
        /* 2. Builtin domain as such */
 
-       if (strequal(name, builtin_domain_name())) {
+       if ((flags & (LOOKUP_NAME_BUILTIN|LOOKUP_NAME_REMOTE)) &&
+           strequal(name, builtin_domain_name()))
+       {
                /* Swap domain and name */
                tmp = name; name = domain; domain = tmp;
                sid_copy(&sid, &global_sid_Builtin);
@@ -149,11 +170,13 @@ BOOL lookup_name(TALLOC_CTX *mem_ctx,
 
        /* 3. Account domain */
 
-       if (strequal(name, get_global_sam_name())) {
+       if ((flags & LOOKUP_NAME_DOMAIN) &&
+           strequal(name, get_global_sam_name()))
+       {
                if (!secrets_fetch_domain_sid(name, &sid)) {
                        DEBUG(3, ("Could not fetch my SID\n"));
                        TALLOC_FREE(tmp_ctx);
-                       return False;
+                       return false;
                }
                /* Swap domain and name */
                tmp = name; name = domain; domain = tmp;
@@ -163,11 +186,13 @@ BOOL lookup_name(TALLOC_CTX *mem_ctx,
 
        /* 4. Primary domain */
 
-       if (!IS_DC && strequal(name, lp_workgroup())) {
+       if ((flags & LOOKUP_NAME_DOMAIN) && !IS_DC &&
+           strequal(name, lp_workgroup()))
+       {
                if (!secrets_fetch_domain_sid(name, &sid)) {
                        DEBUG(3, ("Could not fetch the domain SID\n"));
                        TALLOC_FREE(tmp_ctx);
-                       return False;
+                       return false;
                }
                /* Swap domain and name */
                tmp = name; name = domain; domain = tmp;
@@ -178,8 +203,9 @@ BOOL lookup_name(TALLOC_CTX *mem_ctx,
        /* 5. Trusted domains as such, to me it looks as if members don't do
               this, tested an XP workstation in a NT domain -- vl */
 
-       if (IS_DC && (secrets_fetch_trusted_domain_password(name, NULL,
-                                                           &sid, NULL))) {
+       if ((flags & LOOKUP_NAME_REMOTE) && IS_DC &&
+           (pdb_get_trusteddom_pw(name, NULL, &sid, NULL)))
+       {
                /* Swap domain and name */
                tmp = name; name = domain; domain = tmp;
                type = SID_NAME_DOMAIN;
@@ -188,10 +214,11 @@ BOOL lookup_name(TALLOC_CTX *mem_ctx,
 
        /* 6. Builtin aliases */        
 
-       if (lookup_builtin_name(name, &rid)) {
+       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;
        }
@@ -201,10 +228,11 @@ BOOL lookup_name(TALLOC_CTX *mem_ctx,
 
        /* Both cases are done by looking at our passdb */
 
-       if (lookup_global_sam_name(name, flags, &rid, &type)) {
+       if ((flags & LOOKUP_NAME_DOMAIN) &&
+           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;
        }
 
@@ -212,7 +240,7 @@ BOOL lookup_name(TALLOC_CTX *mem_ctx,
 
        if (!(flags & LOOKUP_NAME_REMOTE)) {
                TALLOC_FREE(tmp_ctx);
-               return False;
+               return false;
        }
 
        /* If we are not a DC, we have to ask in our primary domain. Let
@@ -230,7 +258,7 @@ 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;
+               struct dom_sid dom_sid;
                uint32 tmp_rid;
                enum lsa_SidType domain_type;
                
@@ -253,7 +281,7 @@ BOOL lookup_name(TALLOC_CTX *mem_ctx,
                        DEBUG(2, ("winbind could not find the domain's name "
                                  "it just looked up for us\n"));
                        TALLOC_FREE(tmp_ctx);
-                       return False;
+                       return false;
                }
                goto ok;
        }
@@ -263,13 +291,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 (lookup_unix_user_name(name, &sid)) {
+       if (((flags & LOOKUP_NAME_NO_NSS) == 0)
+           && lookup_unix_user_name(name, &sid)) {
                domain = talloc_strdup(tmp_ctx, unix_users_domain_name());
                type = SID_NAME_USER;
                goto ok;
        }
 
-       if (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;
@@ -280,13 +310,13 @@ BOOL lookup_name(TALLOC_CTX *mem_ctx,
         */
 
        TALLOC_FREE(tmp_ctx);
-       return False;
+       return false;
 
  ok:
        if ((domain == NULL) || (name == NULL)) {
                DEBUG(0, ("talloc failed\n"));
                TALLOC_FREE(tmp_ctx);
-               return False;
+               return false;
        }
 
        /*
@@ -297,7 +327,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 false;
        }
 
        if (ret_domain != NULL) {
@@ -305,7 +335,7 @@ 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 false;
                }
                strupper_m(tmp_dom);
                *ret_domain = tmp_dom;
@@ -320,7 +350,7 @@ BOOL lookup_name(TALLOC_CTX *mem_ctx,
        }
 
        TALLOC_FREE(tmp_ctx);
-       return True;
+       return true;
 }
 
 /************************************************************************
@@ -329,10 +359,10 @@ BOOL lookup_name(TALLOC_CTX *mem_ctx,
  and then "Unix Users"\foo (or "Unix Groups"\foo).
 ************************************************************************/
 
-BOOL lookup_name_smbconf(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;
@@ -349,7 +379,7 @@ BOOL lookup_name_smbconf(TALLOC_CTX *mem_ctx,
 
                        tmp = talloc_strdup(mem_ctx, full_name);
                        if (!tmp) {
-                               return False;
+                               return false;
                        }
                        tmp[p - full_name] = '\\';
                        full_name = tmp;
@@ -365,13 +395,13 @@ BOOL lookup_name_smbconf(TALLOC_CTX *mem_ctx,
                                get_global_sam_name(),
                                full_name );
        if (!qualified_name) {
-               return False;
+               return false;
        }
 
        if (lookup_name(mem_ctx, qualified_name, flags,
                                ret_domain, ret_name,
                                ret_sid, ret_type)) {
-               return True;
+               return true;
        }
        
        /* Finally try with "Unix Users" or "Unix Group" */
@@ -381,7 +411,7 @@ BOOL lookup_name_smbconf(TALLOC_CTX *mem_ctx,
                                        unix_users_domain_name(),
                                full_name );
        if (!qualified_name) {
-               return False;
+               return false;
        }
 
        return lookup_name(mem_ctx, qualified_name, flags,
@@ -389,8 +419,8 @@ BOOL lookup_name_smbconf(TALLOC_CTX *mem_ctx,
                                ret_sid, ret_type);
 }
 
-static BOOL wb_lookup_rids(TALLOC_CTX *mem_ctx,
-                          const DOM_SID *domain_sid,
+static bool wb_lookup_rids(TALLOC_CTX *mem_ctx,
+                          const struct dom_sid *domain_sid,
                           int num_rids, uint32 *rids,
                           const char **domain_name,
                           const char **names, enum lsa_SidType *types)
@@ -401,15 +431,23 @@ static BOOL wb_lookup_rids(TALLOC_CTX *mem_ctx,
        TALLOC_CTX *tmp_ctx;
 
        if (!(tmp_ctx = talloc_init("wb_lookup_rids"))) {
-               return False;
+               return false;
        }
 
        if (!winbind_lookup_rids(tmp_ctx, domain_sid, num_rids, rids,
                                 domain_name, &my_names, &my_types)) {
+               *domain_name = "";
                for (i=0; i<num_rids; i++) {
+                       names[i] = "";
                        types[i] = SID_NAME_UNKNOWN;
                }
-               return True;
+               TALLOC_FREE(tmp_ctx);
+               return true;
+       }
+
+       if (!(*domain_name = talloc_strdup(mem_ctx, *domain_name))) {
+               TALLOC_FREE(tmp_ctx);
+               return false;
        }
 
        /*
@@ -420,30 +458,41 @@ static BOOL wb_lookup_rids(TALLOC_CTX *mem_ctx,
        for (i=0; i<num_rids; i++) {
                if (my_names[i] == NULL) {
                        TALLOC_FREE(tmp_ctx);
-                       return False;
+                       return false;
                }
                if (!(names[i] = talloc_strdup(names, my_names[i]))) {
                        TALLOC_FREE(tmp_ctx);
-                       return False;
+                       return false;
                }
                types[i] = my_types[i];
        }
        TALLOC_FREE(tmp_ctx);
-       return True;
+       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;
 
-       *names = TALLOC_ARRAY(mem_ctx, const char *, num_rids);
-       *types = TALLOC_ARRAY(mem_ctx, enum lsa_SidType, num_rids);
+       DEBUG(10, ("lookup_rids called for domain sid '%s'\n",
+                  sid_string_dbg(domain_sid)));
 
-       if ((*names == NULL) || (*types == NULL)) {
-               return False;
+       if (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)) {
@@ -455,7 +504,7 @@ static BOOL lookup_rids(TALLOC_CTX *mem_ctx, const DOM_SID *domain_sid,
                }
 
                if (*domain_name == NULL) {
-                       return False;
+                       return false;
                }
 
                become_root();
@@ -476,65 +525,76 @@ static BOOL lookup_rids(TALLOC_CTX *mem_ctx, const DOM_SID *domain_sid,
                }
 
                if (*domain_name == NULL) {
-                       return False;
+                       return false;
                }
 
                for (i=0; i<num_rids; i++) {
                        if (lookup_builtin_rid(*names, rids[i],
                                               &(*names)[i])) {
                                if ((*names)[i] == NULL) {
-                                       return False;
+                                       return false;
                                }
                                (*types)[i] = SID_NAME_ALIAS;
                        } else {
                                (*types)[i] = SID_NAME_UNKNOWN;
                        }
                }
-               return True;
+               return true;
        }
 
        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) {
-                                       return False;
+                                       return false;
                                }
                                (*types)[i] = SID_NAME_WKN_GRP;
                        } else {
                                (*types)[i] = SID_NAME_UNKNOWN;
                        }
                }
-               return True;
+               return true;
        }
 
        if (sid_check_is_unix_users(domain_sid)) {
                if (*domain_name == NULL) {
                        *domain_name = talloc_strdup(
                                mem_ctx, unix_users_domain_name());
+                       if (*domain_name == NULL) {
+                               return false;
+                       }
                }
                for (i=0; i<num_rids; i++) {
                        (*names)[i] = talloc_strdup(
                                (*names), uidtoname(rids[i]));
+                       if ((*names)[i] == NULL) {
+                               return false;
+                       }
                        (*types)[i] = SID_NAME_USER;
                }
-               return True;
+               return true;
        }
 
        if (sid_check_is_unix_groups(domain_sid)) {
                if (*domain_name == NULL) {
                        *domain_name = talloc_strdup(
                                mem_ctx, unix_groups_domain_name());
+                       if (*domain_name == NULL) {
+                               return false;
+                       }
                }
                for (i=0; i<num_rids; i++) {
                        (*names)[i] = talloc_strdup(
                                (*names), gidtoname(rids[i]));
+                       if ((*names)[i] == NULL) {
+                               return false;
+                       }
                        (*types)[i] = SID_NAME_DOM_GRP;
                }
-               return True;
+               return true;
        }
 
        return wb_lookup_rids(mem_ctx, domain_sid, num_rids, rids,
@@ -545,7 +605,7 @@ 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;
@@ -553,22 +613,32 @@ static BOOL lookup_as_domain(const DOM_SID *sid, TALLOC_CTX *mem_ctx,
 
        if (sid_check_is_domain(sid)) {
                *name = talloc_strdup(mem_ctx, get_global_sam_name());
-               return True;
+               return true;
        }
 
        if (sid_check_is_builtin(sid)) {
                *name = talloc_strdup(mem_ctx, builtin_domain_name());
-               return True;
+               return true;
        }
 
        if (sid_check_is_wellknown_domain(sid, &tmp)) {
                *name = talloc_strdup(mem_ctx, tmp);
-               return True;
+               return true;
+       }
+
+       if (sid_check_is_unix_users(sid)) {
+               *name = talloc_strdup(mem_ctx, unix_users_domain_name());
+               return true;
+       }
+
+       if (sid_check_is_unix_groups(sid)) {
+               *name = talloc_strdup(mem_ctx, unix_groups_domain_name());
+               return true;
        }
 
        if (sid->num_auths != 4) {
                /* This can't be a domain */
-               return False;
+               return false;
        }
 
        if (IS_DC) {
@@ -579,29 +649,29 @@ static BOOL lookup_as_domain(const DOM_SID *sid, TALLOC_CTX *mem_ctx,
                 * and for SIDs that have 4 sub-authorities and thus look like
                 * domains */
 
-               if (!NT_STATUS_IS_OK(secrets_trusted_domains(mem_ctx,
-                                                            &num_domains,
-                                                            &domains))) {
-                       return False;
+               if (!NT_STATUS_IS_OK(pdb_enum_trusteddoms(mem_ctx,
+                                                         &num_domains,
+                                                         &domains))) {
+                       return false;
                }
 
                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;
+                               return true;
                        }
                }
-               return False;
+               return false;
        }
 
        if (winbind_lookup_sid(mem_ctx, sid, &tmp, NULL, &type) &&
            (type == SID_NAME_DOMAIN)) {
                *name = tmp;
-               return True;
+               return true;
        }
 
-       return False;
+       return false;
 }
 
 /*
@@ -619,17 +689,17 @@ static BOOL lookup_as_domain(const DOM_SID *sid, TALLOC_CTX *mem_ctx,
  * Level 2: Ask domain and trusted domains, no builtin and wkn
  * Level 3: Only ask domain
  * Level 4: W2k3ad: Only ask AD trusts
- * Level 5: Don't lookup anything
+ * Level 5: Only ask transitive forest trusts
  * 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)
 {
-       int ret = False;
+       int ret = false;
 
        switch(level) {
        case 1:
-               ret = True;
+               ret = true;
                break;
        case 2:
                ret = (!sid_check_is_builtin(sid) &&
@@ -641,13 +711,13 @@ static BOOL check_dom_sid_to_level(const DOM_SID *sid, int level)
                ret = sid_check_is_domain(sid);
                break;
        case 5:
-               ret = False;
+               ret = false;
                break;
        }
 
        DEBUG(10, ("%s SID %s in level %d\n",
                   ret ? "Accepting" : "Rejecting",
-                  sid_string_static(sid), level));
+                  sid_string_dbg(sid), level));
        return ret;
 }
 
@@ -664,14 +734,14 @@ static BOOL check_dom_sid_to_level(const DOM_SID *sid, int level)
  */
 
 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;
        struct lsa_name_info *name_infos;
-       struct lsa_dom_info *dom_infos;
+       struct lsa_dom_info *dom_infos = NULL;
 
        int i, j;
 
@@ -680,10 +750,19 @@ NTSTATUS lookup_sids(TALLOC_CTX *mem_ctx, int num_sids,
                return NT_STATUS_NO_MEMORY;
        }
 
-       name_infos = TALLOC_ARRAY(mem_ctx, struct lsa_name_info, num_sids);
+       if (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;
+               }
+       } else {
+               name_infos = NULL;
+       }
+
        dom_infos = TALLOC_ZERO_ARRAY(mem_ctx, struct lsa_dom_info,
-                                     MAX_REF_DOMAINS);
-       if ((name_infos == NULL) || (dom_infos == NULL)) {
+                                     LSA_REF_DOMAIN_LIST_MULTIPLIER);
+       if (dom_infos == NULL) {
                result = NT_STATUS_NO_MEMORY;
                goto fail;
        }
@@ -703,8 +782,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]);
@@ -740,7 +819,7 @@ NTSTATUS lookup_sids(TALLOC_CTX *mem_ctx, int num_sids,
                } else {
                        /* This is a normal SID with rid component */
                        if (!sid_split_rid(&sid, &rid)) {
-                               result = NT_STATUS_INVALID_PARAMETER;
+                               result = NT_STATUS_INVALID_SID;
                                goto fail;
                        }
                }
@@ -752,16 +831,16 @@ NTSTATUS lookup_sids(TALLOC_CTX *mem_ctx, int num_sids,
                        continue;
                }
 
-               for (j=0; j<MAX_REF_DOMAINS; j++) {
+               for (j=0; j<LSA_REF_DOMAIN_LIST_MULTIPLIER; j++) {
                        if (!dom_infos[j].valid) {
                                break;
                        }
-                       if (sid_equal(&sid, &dom_infos[j].sid)) {
+                       if (dom_sid_equal(&sid, &dom_infos[j].sid)) {
                                break;
                        }
                }
 
-               if (j == MAX_REF_DOMAINS) {
+               if (j == LSA_REF_DOMAIN_LIST_MULTIPLIER) {
                        /* TODO: What's the right error message here? */
                        result = NT_STATUS_NONE_MAPPED;
                        goto fail;
@@ -770,7 +849,7 @@ NTSTATUS lookup_sids(TALLOC_CTX *mem_ctx, int num_sids,
                if (!dom_infos[j].valid) {
                        /* We found a domain not yet referenced, create a new
                         * ref. */
-                       dom_infos[j].valid = True;
+                       dom_infos[j].valid = true;
                        sid_copy(&dom_infos[j].sid, &sid);
 
                        if (domain_name != NULL) {
@@ -805,7 +884,7 @@ NTSTATUS lookup_sids(TALLOC_CTX *mem_ctx, int num_sids,
 
        /* Iterate over the domains found */
 
-       for (i=0; i<MAX_REF_DOMAINS; i++) {
+       for (i=0; i<LSA_REF_DOMAIN_LIST_MULTIPLIER; i++) {
                uint32_t *rids;
                const char *domain_name = NULL;
                const char **names;
@@ -817,9 +896,13 @@ NTSTATUS lookup_sids(TALLOC_CTX *mem_ctx, int num_sids,
                        break;
                }
 
-               if (!(rids = TALLOC_ARRAY(tmp_ctx, uint32, dom->num_idxs))) {
-                       result = NT_STATUS_NO_MEMORY;
-                       goto fail;
+               if (dom->num_idxs) {
+                       if (!(rids = TALLOC_ARRAY(tmp_ctx, uint32, dom->num_idxs))) {
+                               result = NT_STATUS_NO_MEMORY;
+                               goto fail;
+                       }
+               } else {
+                       rids = NULL;
                }
 
                for (j=0; j<dom->num_idxs; j++) {
@@ -856,6 +939,7 @@ NTSTATUS lookup_sids(TALLOC_CTX *mem_ctx, int num_sids,
 
        *ret_domains = dom_infos;
        *ret_names = name_infos;
+       TALLOC_FREE(tmp_ctx);
        return NT_STATUS_OK;
 
  fail:
@@ -869,18 +953,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;
        TALLOC_CTX *tmp_ctx;
-       BOOL ret = False;
+       bool ret = false;
+
+       DEBUG(10, ("lookup_sid called for SID '%s'\n", sid_string_dbg(sid)));
 
        if (!(tmp_ctx = talloc_new(mem_ctx))) {
                DEBUG(0, ("talloc_new failed\n"));
-               return False;
+               return false;
        }
 
        if (!NT_STATUS_IS_OK(lookup_sids(tmp_ctx, 1, &sid, 1,
@@ -906,16 +992,14 @@ BOOL lookup_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
                *ret_type = name->type;
        }
 
-       ret = True;
+       ret = true;
 
  done:
        if (ret) {
-               DEBUG(10, ("Sid %s -> %s\\%s(%d)\n",
-                          sid_string_static(sid), domain->name,
-                          name->name, name->type));
+               DEBUG(10, ("Sid %s -> %s\\%s(%d)\n", sid_string_dbg(sid),
+                          domain->name, name->name, name->type));
        } else {
-               DEBUG(10, ("failed to lookup sid %s\n",
-                          sid_string_static(sid)));
+               DEBUG(10, ("failed to lookup sid %s\n", sid_string_dbg(sid)));
        }
        TALLOC_FREE(tmp_ctx);
        return ret;
@@ -929,222 +1013,331 @@ BOOL lookup_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
  modified to use linked lists by jra.
 *****************************************************************/  
 
-#define MAX_UID_SID_CACHE_SIZE 100
-#define TURNOVER_UID_SID_CACHE_SIZE 10
-#define MAX_GID_SID_CACHE_SIZE 100
-#define TURNOVER_GID_SID_CACHE_SIZE 10
+/*****************************************************************
+  Find a SID given a uid.
+*****************************************************************/
 
-static size_t n_uid_sid_cache = 0;
-static size_t n_gid_sid_cache = 0;
+static bool fetch_sid_from_uid_cache(struct dom_sid *psid, uid_t uid)
+{
+       DATA_BLOB cache_value;
 
-static struct uid_sid_cache {
-       struct uid_sid_cache *next, *prev;
-       uid_t uid;
-       DOM_SID sid;
-       enum lsa_SidType sidtype;
-} *uid_sid_cache_head;
+       if (!memcache_lookup(NULL, UID_SID_CACHE,
+                            data_blob_const(&uid, sizeof(uid)),
+                            &cache_value)) {
+               return false;
+       }
 
-static struct gid_sid_cache {
-       struct gid_sid_cache *next, *prev;
-       gid_t gid;
-       DOM_SID sid;
-       enum lsa_SidType sidtype;
-} *gid_sid_cache_head;
+       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 SID given a uid.
-*****************************************************************/  
+  Find a uid given a SID.
+*****************************************************************/
 
-static BOOL fetch_sid_from_uid_cache(DOM_SID *psid, uid_t uid)
+static bool fetch_uid_from_cache( uid_t *puid, const struct dom_sid *psid )
 {
-       struct uid_sid_cache *pc;
-
-       for (pc = uid_sid_cache_head; pc; pc = pc->next) {
-               if (pc->uid == uid) {
-                       *psid = pc->sid;
-                       DEBUG(3,("fetch sid from uid cache %u -> %s\n",
-                                (unsigned int)uid, sid_string_static(psid)));
-                       DLIST_PROMOTE(uid_sid_cache_head, pc);
-                       return True;
-               }
+       DATA_BLOB cache_value;
+
+       if (!memcache_lookup(NULL, SID_UID_CACHE,
+                            data_blob_const(psid, ndr_size_dom_sid(psid, 0)),
+                            &cache_value)) {
+               return false;
        }
-       return False;
+
+       SMB_ASSERT(cache_value.length == sizeof(*puid));
+       memcpy(puid, cache_value.data, sizeof(*puid));
+
+       return true;
 }
 
 /*****************************************************************
 Find a uid given a SID.
-*****************************************************************/  
Store uid to SID mapping in cache.
+*****************************************************************/
 
-static BOOL fetch_uid_from_cache( uid_t *puid, const DOM_SID *psid )
+void store_uid_sid_cache(const struct dom_sid *psid, uid_t uid)
 {
-       struct uid_sid_cache *pc;
-
-       for (pc = uid_sid_cache_head; pc; pc = pc->next) {
-               if (sid_compare(&pc->sid, psid) == 0) {
-                       *puid = pc->uid;
-                       DEBUG(3,("fetch uid from cache %u -> %s\n",
-                                (unsigned int)*puid, sid_string_static(psid)));
-                       DLIST_PROMOTE(uid_sid_cache_head, pc);
-                       return True;
-               }
+       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)
+{
+       DATA_BLOB cache_value;
+
+       if (!memcache_lookup(NULL, GID_SID_CACHE,
+                            data_blob_const(&gid, sizeof(gid)),
+                            &cache_value)) {
+               return false;
        }
-       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;
 }
 
 /*****************************************************************
- Store uid to SID mapping in cache.
+  Find a gid given a SID.
+*****************************************************************/
+
+static bool fetch_gid_from_cache(gid_t *pgid, const struct dom_sid *psid)
+{
+       DATA_BLOB cache_value;
+
+       if (!memcache_lookup(NULL, SID_GID_CACHE,
+                            data_blob_const(psid, ndr_size_dom_sid(psid, 0)),
+                            &cache_value)) {
+               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)));
+}
+
+/*****************************************************************
+ *THE LEGACY* convert uid_t to SID function.
 *****************************************************************/  
 
-void store_uid_sid_cache(const DOM_SID *psid, uid_t uid)
+static void legacy_uid_to_sid(struct dom_sid *psid, uid_t uid)
 {
-       struct uid_sid_cache *pc;
+       bool ret;
 
-       /* do not store SIDs in the "Unix Group" domain */
-       
-       if ( sid_check_is_in_unix_users( psid ) )
-               return;
+       ZERO_STRUCTP(psid);
 
-       if (n_uid_sid_cache >= MAX_UID_SID_CACHE_SIZE && n_uid_sid_cache > TURNOVER_UID_SID_CACHE_SIZE) {
-               /* Delete the last TURNOVER_UID_SID_CACHE_SIZE entries. */
-               struct uid_sid_cache *pc_next;
-               size_t i;
-
-               for (i = 0, pc = uid_sid_cache_head; i < (n_uid_sid_cache - TURNOVER_UID_SID_CACHE_SIZE); i++, pc = pc->next)
-                       ;
-               for(; pc; pc = pc_next) {
-                       pc_next = pc->next;
-                       DLIST_REMOVE(uid_sid_cache_head,pc);
-                       SAFE_FREE(pc);
-                       n_uid_sid_cache--;
-               }
+       become_root();
+       ret = pdb_uid_to_sid(uid, psid);
+       unbecome_root();
+
+       if (ret) {
+               /* This is a mapped user */
+               goto done;
        }
 
-       pc = SMB_MALLOC_P(struct uid_sid_cache);
-       if (!pc)
-               return;
-       pc->uid = uid;
-       sid_copy(&pc->sid, psid);
-       DLIST_ADD(uid_sid_cache_head, pc);
-       n_uid_sid_cache++;
+       /* 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;
 }
 
 /*****************************************************************
 Find a SID given a gid.
*THE LEGACY* convert gid_t to SID function.
 *****************************************************************/  
 
-static BOOL fetch_sid_from_gid_cache(DOM_SID *psid, gid_t gid)
+static void legacy_gid_to_sid(struct dom_sid *psid, gid_t gid)
 {
-       struct gid_sid_cache *pc;
-
-       for (pc = gid_sid_cache_head; pc; pc = pc->next) {
-               if (pc->gid == gid) {
-                       *psid = pc->sid;
-                       DEBUG(3,("fetch sid from gid cache %u -> %s\n",
-                                (unsigned int)gid, sid_string_static(psid)));
-                       DLIST_PROMOTE(gid_sid_cache_head, pc);
-                       return True;
-               }
+       bool ret;
+
+       ZERO_STRUCTP(psid);
+
+       become_root();
+       ret = pdb_gid_to_sid(gid, psid);
+       unbecome_root();
+
+       if (ret) {
+               /* This is a mapped group */
+               goto done;
        }
-       return False;
+       
+       /* 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;
 }
 
 /*****************************************************************
 Find a gid given a SID.
*THE LEGACY* convert SID to uid function.
 *****************************************************************/  
 
-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)
 {
-       struct gid_sid_cache *pc;
-
-       for (pc = gid_sid_cache_head; pc; pc = pc->next) {
-               if (sid_compare(&pc->sid, psid) == 0) {
-                       *pgid = pc->gid;
-                       DEBUG(3,("fetch gid from cache %u -> %s\n",
-                                (unsigned int)*pgid, sid_string_static(psid)));
-                       DLIST_PROMOTE(gid_sid_cache_head, pc);
-                       return True;
+       enum lsa_SidType type;
+
+       if (sid_check_is_in_our_domain(psid)) {
+               union unid_t id;
+               bool ret;
+
+               become_root();
+               ret = pdb_sid_to_id(psid, &id, &type);
+               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 */
        }
-       return False;
+
+       DEBUG(10,("LEGACY: mapping failed for sid %s\n",
+                 sid_string_dbg(psid)));
+       return false;
+
+done:
+       DEBUG(10,("LEGACY: sid %s -> uid %u\n", sid_string_dbg(psid),
+                 (unsigned int)*puid ));
+
+       store_uid_sid_cache(psid, *puid);
+       return true;
 }
 
 /*****************************************************************
- Store gid to SID mapping in cache.
+ *THE LEGACY* convert SID to gid function.
+ Group mapping is used for gids that maps to Wellknown SIDs
 *****************************************************************/  
 
-void store_gid_sid_cache(const DOM_SID *psid, gid_t gid)
+static bool legacy_sid_to_gid(const struct dom_sid *psid, gid_t *pgid)
 {
-       struct gid_sid_cache *pc;
-       
-       /* do not store SIDs in the "Unix Group" domain */
-       
-       if ( sid_check_is_in_unix_groups( psid ) )
-               return;
+       GROUP_MAP map;
+       union unid_t id;
+       enum lsa_SidType type;
+
+       if ((sid_check_is_in_builtin(psid) ||
+            sid_check_is_in_wellknown_domain(psid))) {
+               bool ret;
+
+               become_root();
+               ret = pdb_getgrsid(&map, *psid);
+               unbecome_root();
 
-       if (n_gid_sid_cache >= MAX_GID_SID_CACHE_SIZE && n_gid_sid_cache > TURNOVER_GID_SID_CACHE_SIZE) {
-               /* Delete the last TURNOVER_GID_SID_CACHE_SIZE entries. */
-               struct gid_sid_cache *pc_next;
-               size_t i;
-
-               for (i = 0, pc = gid_sid_cache_head; i < (n_gid_sid_cache - TURNOVER_GID_SID_CACHE_SIZE); i++, pc = pc->next)
-                       ;
-               for(; pc; pc = pc_next) {
-                       pc_next = pc->next;
-                       DLIST_REMOVE(gid_sid_cache_head,pc);
-                       SAFE_FREE(pc);
-                       n_gid_sid_cache--;
+               if (ret) {
+                       *pgid = map.gid;
+                       goto done;
                }
+               DEBUG(10,("LEGACY: mapping failed for sid %s\n",
+                         sid_string_dbg(psid)));
+               return false;
        }
 
-       pc = SMB_MALLOC_P(struct gid_sid_cache);
-       if (!pc)
-               return;
-       pc->gid = gid;
-       sid_copy(&pc->sid, psid);
-       DLIST_ADD(gid_sid_cache_head, pc);
+       if (sid_check_is_in_our_domain(psid)) {
+               bool ret;
+
+               become_root();
+               ret = pdb_sid_to_id(psid, &id, &type);
+               unbecome_root();
+
+               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;
+               }
+       
+               /* This was ours, but it was not mapped.  Fail */
+       }
 
-       DEBUG(3,("store_gid_sid_cache: gid %u in cache -> %s\n", (unsigned int)gid,
-               sid_string_static(psid)));
+       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 ));
 
-       n_gid_sid_cache++;
+       store_gid_sid_cache(psid, *pgid);
+
+       return true;
 }
 
 /*****************************************************************
  *THE CANONICAL* convert uid_t to SID function.
 *****************************************************************/  
 
-void uid_to_sid(DOM_SID *psid, uid_t uid)
+void uid_to_sid(struct dom_sid *psid, uid_t uid)
 {
-       uid_t low, high;
-       uint32 rid;
-
+       bool expired = true;
+       bool ret;
        ZERO_STRUCTP(psid);
 
        if (fetch_sid_from_uid_cache(psid, uid))
                return;
 
-       if (lp_idmap_uid(&low, &high) && (uid >= low) && (uid <= high) &&
-           winbind_uid_to_sid(psid, uid)) {
-
-               DEBUG(10,("uid_to_sid: winbindd %u -> %s\n",
-                         (unsigned int)uid, sid_string_static(psid)));
-               goto done;
-       }
+       /* Check the winbindd cache directly. */
+       ret = idmap_cache_find_uid2sid(uid, psid, &expired);
 
-       if (pdb_uid_to_rid(uid, &rid)) {
-               /* This is a mapped user */
-               sid_copy(psid, get_global_sam_sid());
-               sid_append_rid(psid, rid);
-               goto done;
+       if (ret && !expired && is_null_sid(psid)) {
+               /*
+                * Negative cache entry, we already asked.
+                * do legacy.
+                */
+               legacy_uid_to_sid(psid, uid);
+               return;
        }
 
-       /* This is an unmapped user */
+       if (!ret || expired) {
+               /* Not in cache. Ask winbindd. */
+               if (!winbind_uid_to_sid(psid, uid)) {
+                       /*
+                        * 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.
+                        */
+                       DEBUG(5, ("uid_to_sid: winbind failed to find a sid "
+                                 "for uid %u\n", (unsigned int)uid));
 
-       uid_to_unix_users_sid(uid, psid);
+                       legacy_uid_to_sid(psid, uid);
+                       return;
+               }
+       }
 
- done:
-       DEBUG(10,("uid_to_sid: local %u -> %s\n", (unsigned int)uid,
-                 sid_string_static(psid)));
+       DEBUG(10,("uid %u -> sid %s\n", (unsigned int)uid,
+                 sid_string_dbg(psid)));
 
        store_uid_sid_cache(psid, uid);
        return;
@@ -1154,35 +1347,50 @@ void uid_to_sid(DOM_SID *psid, uid_t uid)
  *THE CANONICAL* convert gid_t to SID function.
 *****************************************************************/  
 
-void gid_to_sid(DOM_SID *psid, gid_t gid)
+void gid_to_sid(struct dom_sid *psid, gid_t gid)
 {
-       gid_t low, high;
-
+       bool expired = true;
+       bool ret;
        ZERO_STRUCTP(psid);
 
        if (fetch_sid_from_gid_cache(psid, gid))
                return;
 
-       if (lp_idmap_gid(&low, &high) && (gid >= low) && (gid <= high) &&
-           winbind_gid_to_sid(psid, gid)) {
+       /* Check the winbindd cache directly. */
+       ret = idmap_cache_find_gid2sid(gid, psid, &expired);
 
-               DEBUG(10,("gid_to_sid: winbindd %u -> %s\n",
-                         (unsigned int)gid, sid_string_static(psid)));
-               goto done;
+       if (ret && !expired && is_null_sid(psid)) {
+               /*
+                * Negative cache entry, we already asked.
+                * do legacy.
+                */
+               legacy_gid_to_sid(psid, gid);
+               return;
        }
 
-       if (pdb_gid_to_sid(gid, psid)) {
-               /* This is a mapped group */
-               goto done;
-       }
-       
-       /* This is an unmapped group */
+       if (!ret || expired) {
+               /* Not in cache. Ask winbindd. */
+               if (!winbind_gid_to_sid(psid, gid)) {
+                       /*
+                        * 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.
+                        */
+                       DEBUG(5, ("gid_to_sid: winbind failed to find a sid "
+                                 "for gid %u\n", (unsigned int)gid));
 
-       gid_to_unix_groups_sid(gid, psid);
+                       legacy_gid_to_sid(psid, gid);
+                       return;
+               }
+       }
 
- done:
-       DEBUG(10,("gid_to_sid: local %u -> %s\n", (unsigned int)gid,
-                 sid_string_static(psid)));
+       DEBUG(10,("gid %u -> sid %s\n", (unsigned int)gid,
+                 sid_string_dbg(psid)));
 
        store_gid_sid_cache(psid, gid);
        return;
@@ -1192,73 +1400,61 @@ void gid_to_sid(DOM_SID *psid, gid_t gid)
  *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)
 {
-       enum lsa_SidType type;
+       bool expired = true;
+       bool ret;
        uint32 rid;
        gid_t gid;
 
        if (fetch_uid_from_cache(puid, psid))
-               return True;
+               return true;
 
        if (fetch_gid_from_cache(&gid, psid)) {
-               return False;
+               return false;
        }
 
+       /* Optimize for the Unix Users Domain
+        * as the conversion is straightforward */
        if (sid_peek_check_rid(&global_sid_Unix_Users, psid, &rid)) {
                uid_t uid = rid;
                *puid = uid;
-               goto done;
-       }
 
-       if (sid_peek_check_rid(get_global_sam_sid(), psid, &rid)) {
-               union unid_t id;
-
-               if (pdb_sid_to_id(psid, &id, &type)) {
-                       if (type != SID_NAME_USER) {
-                               DEBUG(5, ("sid %s is a %s, expected a user\n",
-                                         sid_string_static(psid),
-                                         sid_type_lookup(type)));
-                               return False;
-                       }
-                       *puid = id.uid;
-                       goto done;
-               }
-
-               /* This was ours, but it was not mapped.  Fail */
-
-               return False;
+               /* return here, don't cache */
+               DEBUG(10,("sid %s -> uid %u\n", sid_string_dbg(psid),
+                       (unsigned int)*puid ));
+               return true;
        }
 
-       if (winbind_lookup_sid(NULL, psid, NULL, NULL, &type)) {
+       /* Check the winbindd cache directly. */
+       ret = idmap_cache_find_sid2uid(psid, puid, &expired);
 
-               if (type != SID_NAME_USER) {
-                       DEBUG(10, ("sid_to_uid: sid %s is a %s\n",
-                                  sid_string_static(psid),
-                                  sid_type_lookup(type)));
-                       return False;
-               }
+       if (ret && !expired && (*puid == (uid_t)-1)) {
+               /*
+                * Negative cache entry, we already asked.
+                * do legacy.
+                */
+               return legacy_sid_to_uid(psid, puid);
+       }
 
+       if (!ret || expired) {
+               /* Not in cache. Ask winbindd. */
                if (!winbind_sid_to_uid(puid, psid)) {
-                       DEBUG(5, ("sid_to_uid: winbind failed to allocate a "
-                                 "new uid for sid %s\n",
-                                 sid_string_static(psid)));
-                       return False;
+                       DEBUG(5, ("winbind failed to find a uid for sid %s\n",
+                                 sid_string_dbg(psid)));
+                       /* winbind failed. do legacy */
+                       return legacy_sid_to_uid(psid, puid);
                }
-               goto done;
        }
 
        /* TODO: Here would be the place to allocate both a gid and a uid for
         * the SID in question */
 
-       return False;
-
- done:
-       DEBUG(10,("sid_to_uid: %s -> %u\n", sid_string_static(psid),
+       DEBUG(10,("sid %s -> uid %u\n", sid_string_dbg(psid),
                (unsigned int)*puid ));
 
        store_uid_sid_cache(psid, *puid);
-       return True;
+       return true;
 }
 
 /*****************************************************************
@@ -1266,84 +1462,183 @@ BOOL sid_to_uid(const DOM_SID *psid, uid_t *puid)
  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;
-       GROUP_MAP map;
-       union unid_t id;
-       enum lsa_SidType type;
        uid_t uid;
 
        if (fetch_gid_from_cache(pgid, psid))
-               return True;
+               return true;
 
        if (fetch_uid_from_cache(&uid, psid))
-               return False;
+               return false;
 
+       /* Optimize for the Unix Groups Domain
+        * as the conversion is straightforward */
        if (sid_peek_check_rid(&global_sid_Unix_Groups, psid, &rid)) {
                gid_t gid = rid;
                *pgid = gid;
-               goto done;
+
+               /* return here, don't cache */
+               DEBUG(10,("sid %s -> gid %u\n", sid_string_dbg(psid),
+                       (unsigned int)*pgid ));
+               return true;
        }
 
-       if ((sid_check_is_in_builtin(psid) ||
-            sid_check_is_in_wellknown_domain(psid))) {
-               if (pdb_getgrsid(&map, *psid)) {
-                       *pgid = map.gid;
-                       goto done;
-               }
-               return False;
+       /* Check the winbindd cache directly. */
+       ret = idmap_cache_find_sid2gid(psid, pgid, &expired);
+
+       if (ret && !expired && (*pgid == (gid_t)-1)) {
+               /*
+                * Negative cache entry, we already asked.
+                * do legacy.
+                */
+               return legacy_sid_to_gid(psid, pgid);
        }
 
-       if (sid_peek_check_rid(get_global_sam_sid(), psid, &rid)) {
-               if (pdb_sid_to_id(psid, &id, &type)) {
-                       if ((type != SID_NAME_DOM_GRP) &&
-                           (type != SID_NAME_ALIAS)) {
-                               DEBUG(5, ("sid %s is a %s, expected a group\n",
-                                         sid_string_static(psid),
-                                         sid_type_lookup(type)));
-                               return False;
-                       }
-                       *pgid = id.gid;
-                       goto done;
+       if (!ret || expired) {
+               /* Not in cache or negative. Ask winbindd. */
+               /* Ask winbindd if it can map this sid to a gid.
+                * (Idmap will check it is a valid SID and of the right type) */
+
+               if ( !winbind_sid_to_gid(pgid, psid) ) {
+
+                       DEBUG(10,("winbind failed to find a gid for sid %s\n",
+                                 sid_string_dbg(psid)));
+                       /* winbind failed. do legacy */
+                       return legacy_sid_to_gid(psid, pgid);
                }
+       }
 
-               /* This was ours, but it was not mapped.  Fail */
+       DEBUG(10,("sid %s -> gid %u\n", sid_string_dbg(psid),
+                 (unsigned int)*pgid ));
 
-               return False;
+       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 (!winbind_lookup_sid(NULL, psid, NULL, NULL, &type)) {
-               DEBUG(11,("sid_to_gid: no one knows the SID %s (tried local, "
-                         "then winbind)\n", sid_string_static(psid)));
-               
-               return False;
+
+       if (!pwd) {
+               pwd = Get_Pwnam_alloc(mem_ctx, username);
+               if (!pwd) {
+                       DEBUG(0, ("Failed to find a Unix account for %s",
+                                 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;
        }
 
-       /* winbindd knows it; Ensure this is a group sid */
+       gid_to_sid(group_sid, pwd->pw_gid);
+       if (!is_null_sid(group_sid)) {
+               struct dom_sid domain_sid;
+               uint32_t rid;
 
-       if ((type != SID_NAME_DOM_GRP) && (type != SID_NAME_ALIAS) &&
-           (type != SID_NAME_WKN_GRP)) {
-               DEBUG(10,("sid_to_gid: winbind lookup succeeded but SID is "
-                         "a %s\n", sid_type_lookup(type)));
-               return False;
+               /* 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 */
+                       ZERO_STRUCTP(group_sid);
+                       if (pdb_gid_to_sid(pwd->pw_gid, group_sid)) {
+                               need_lookup_sid = true;
+                       }
+               }
        }
-       
-       /* winbindd knows it and it is a type of group; sid_to_gid must succeed
-          or we are dead in the water */
 
-       if ( !winbind_sid_to_gid(pgid, psid) ) {
-               DEBUG(10,("sid_to_gid: winbind failed to allocate a new gid "
-                         "for sid %s\n", sid_string_static(psid)));
-               return False;
+       /* 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;
+
+               DEBUG(10, ("do lookup_sid(%s) for group of user %s\n",
+                          sid_string_dbg(group_sid), 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",
+                         sid_string_dbg(group_sid), username,
+                         sid_type_lookup(type)));
        }
 
- done:
-       DEBUG(10,("sid_to_gid: %s -> %u\n", sid_string_static(psid),
-                 (unsigned int)*pgid ));
+       /* 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));
 
-       store_gid_sid_cache(psid, *pgid);
-       
-       return True;
-}
+       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;
+}