X-Git-Url: http://git.samba.org/?a=blobdiff_plain;f=source3%2Fpassdb%2Flookup_sid.c;h=23566b657d1d1ae675a1831aceaf283e963b2871;hb=64421129b672d0ce55c5aa235e5038dd2ea1b32b;hp=f5b03ffff06bcd74fa7c1bf454f29214c6e8aaf3;hpb=9f5a16a22da812ed58dcb46979ab5ae27d3a905e;p=samba.git diff --git a/source3/passdb/lookup_sid.c b/source3/passdb/lookup_sid.c index f5b03ffff06..23566b657d1 100644 --- a/source3/passdb/lookup_sid.c +++ b/source3/passdb/lookup_sid.c @@ -4,22 +4,27 @@ 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 . */ #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. @@ -32,14 +37,14 @@ 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); @@ -65,7 +70,7 @@ 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)); @@ -75,8 +80,7 @@ bool lookup_name(TALLOC_CTX *mem_ctx, /* 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); @@ -86,10 +90,17 @@ bool lookup_name(TALLOC_CTX *mem_ctx, 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 +117,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) == 0) + && strequal(domain, unix_users_domain_name())) { if (lookup_unix_user_name(name, &sid)) { type = SID_NAME_USER; goto ok; @@ -115,7 +127,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; @@ -205,8 +218,7 @@ bool lookup_name(TALLOC_CTX *mem_ctx, 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 +232,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 +258,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 +272,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 +290,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) == 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; @@ -349,7 +361,7 @@ 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; @@ -390,7 +402,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,7 +419,7 @@ bool lookup_name_smbconf(TALLOC_CTX *mem_ctx, } static bool wb_lookup_rids(TALLOC_CTX *mem_ctx, - const DOM_SID *domain_sid, + const struct dom_sid *domain_sid, int num_rids, uint32 *rids, const char **domain_name, const char **names, enum lsa_SidType *types) @@ -457,20 +469,26 @@ 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; + DEBUG(10, ("lookup_rids called for domain sid '%s'\n", + sid_string_dbg(domain_sid))); + if (num_rids) { - *names = TALLOC_ARRAY(mem_ctx, const char *, 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; @@ -525,9 +543,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; inum_auths != 4) { /* This can't be a domain */ return false; @@ -616,7 +655,7 @@ static bool lookup_as_domain(const DOM_SID *sid, TALLOC_CTX *mem_ctx, } for (i=0; isid)) { + if (dom_sid_equal(sid, &domains[i]->sid)) { *name = talloc_strdup(mem_ctx, domains[i]->name); return true; @@ -653,7 +692,7 @@ 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) { int ret = false; @@ -688,13 +727,11 @@ 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) { @@ -721,7 +758,7 @@ NTSTATUS lookup_sids(TALLOC_CTX *mem_ctx, int num_sids, } dom_infos = TALLOC_ZERO_ARRAY(mem_ctx, struct lsa_dom_info, - MAX_REF_DOMAINS); + LSA_REF_DOMAIN_LIST_MULTIPLIER); if (dom_infos == NULL) { result = NT_STATUS_NO_MEMORY; goto fail; @@ -742,8 +779,8 @@ NTSTATUS lookup_sids(TALLOC_CTX *mem_ctx, int num_sids, */ for (i=0; inum_idxs; j++) { int idx = dom->idxs[j]; name_infos[idx].type = types[j]; @@ -913,7 +950,7 @@ NTSTATUS lookup_sids(TALLOC_CTX *mem_ctx, int num_sids, *THE CANONICAL* convert SID to name function. *****************************************************************/ -bool lookup_sid(TALLOC_CTX *mem_ctx, const 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) { @@ -922,6 +959,8 @@ bool lookup_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid, TALLOC_CTX *tmp_ctx; 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; @@ -971,205 +1010,132 @@ 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 - -static size_t n_uid_sid_cache = 0; -static size_t n_gid_sid_cache = 0; - -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; - -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; - /***************************************************************** Find a SID given a uid. -*****************************************************************/ +*****************************************************************/ -static bool fetch_sid_from_uid_cache(DOM_SID *psid, uid_t uid) +static bool fetch_sid_from_uid_cache(struct dom_sid *psid, uid_t uid) { - 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_dbg(psid))); - DLIST_PROMOTE(uid_sid_cache_head, pc); - return true; - } + DATA_BLOB cache_value; + + if (!memcache_lookup(NULL, UID_SID_CACHE, + data_blob_const(&uid, sizeof(uid)), + &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; } /***************************************************************** Find a uid given a SID. -*****************************************************************/ +*****************************************************************/ -static bool fetch_uid_from_cache( uid_t *puid, const DOM_SID *psid ) +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 (sid_compare(&pc->sid, psid) == 0) { - *puid = pc->uid; - DEBUG(3,("fetch uid from cache %u -> %s\n", - (unsigned int)*puid, sid_string_dbg(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; } /***************************************************************** Store uid to SID mapping in cache. -*****************************************************************/ +*****************************************************************/ -void store_uid_sid_cache(const DOM_SID *psid, uid_t uid) +void store_uid_sid_cache(const struct dom_sid *psid, uid_t uid) { - struct uid_sid_cache *pc; - - /* do not store SIDs in the "Unix Group" domain */ - - if ( sid_check_is_in_unix_users( psid ) ) - return; - - 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--; - } - } - - 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++; + 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(DOM_SID *psid, gid_t gid) +static bool fetch_sid_from_gid_cache(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_dbg(psid))); - DLIST_PROMOTE(gid_sid_cache_head, pc); - return true; - } + 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; } /***************************************************************** Find a gid given a SID. -*****************************************************************/ +*****************************************************************/ -static bool fetch_gid_from_cache(gid_t *pgid, const DOM_SID *psid) +static bool fetch_gid_from_cache(gid_t *pgid, const struct dom_sid *psid) { - 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_dbg(psid))); - DLIST_PROMOTE(gid_sid_cache_head, pc); - return true; - } + 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; } - 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) +void store_gid_sid_cache(const struct dom_sid *psid, gid_t gid) { - struct gid_sid_cache *pc; - - /* do not store SIDs in the "Unix Group" domain */ - - if ( sid_check_is_in_unix_groups( psid ) ) - return; - - 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--; - } - } - - 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); - - DEBUG(3,("store_gid_sid_cache: gid %u in cache -> %s\n", - (unsigned int)gid, sid_string_dbg(psid))); - - n_gid_sid_cache++; + 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. *****************************************************************/ -static void legacy_uid_to_sid(DOM_SID *psid, uid_t uid) +static void legacy_uid_to_sid(struct dom_sid *psid, uid_t uid) { - uint32 rid; bool ret; ZERO_STRUCTP(psid); become_root(); - ret = pdb_uid_to_rid(uid, &rid); + ret = pdb_uid_to_sid(uid, psid); unbecome_root(); if (ret) { /* This is a mapped user */ - sid_copy(psid, get_global_sam_sid()); - sid_append_rid(psid, rid); goto done; } @@ -1189,7 +1155,7 @@ static void legacy_uid_to_sid(DOM_SID *psid, uid_t uid) *THE LEGACY* convert gid_t to SID function. *****************************************************************/ -static void legacy_gid_to_sid(DOM_SID *psid, gid_t gid) +static void legacy_gid_to_sid(struct dom_sid *psid, gid_t gid) { bool ret; @@ -1203,7 +1169,7 @@ static void legacy_gid_to_sid(DOM_SID *psid, gid_t gid) /* This is a mapped group */ goto done; } - + /* This is an unmapped group */ gid_to_unix_groups_sid(gid, psid); @@ -1220,12 +1186,11 @@ static void legacy_gid_to_sid(DOM_SID *psid, gid_t gid) *THE LEGACY* convert SID to uid function. *****************************************************************/ -static bool legacy_sid_to_uid(const DOM_SID *psid, uid_t *puid) +static bool legacy_sid_to_uid(const struct dom_sid *psid, uid_t *puid) { enum lsa_SidType type; - uint32 rid; - if (sid_peek_check_rid(get_global_sam_sid(), psid, &rid)) { + if (sid_check_is_in_our_domain(psid)) { union unid_t id; bool ret; @@ -1264,9 +1229,8 @@ done: Group mapping is used for gids that maps to Wellknown SIDs *****************************************************************/ -static bool legacy_sid_to_gid(const DOM_SID *psid, gid_t *pgid) +static bool legacy_sid_to_gid(const struct dom_sid *psid, gid_t *pgid) { - uint32 rid; GROUP_MAP map; union unid_t id; enum lsa_SidType type; @@ -1288,7 +1252,7 @@ static bool legacy_sid_to_gid(const DOM_SID *psid, gid_t *pgid) return false; } - if (sid_peek_check_rid(get_global_sam_sid(), psid, &rid)) { + if (sid_check_is_in_our_domain(psid)) { bool ret; become_root(); @@ -1306,14 +1270,14 @@ static bool legacy_sid_to_gid(const DOM_SID *psid, gid_t *pgid) *pgid = id.gid; 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; - + done: DEBUG(10,("LEGACY: sid %s -> gid %u\n", sid_string_dbg(psid), (unsigned int)*pgid )); @@ -1327,22 +1291,46 @@ static bool legacy_sid_to_gid(const DOM_SID *psid, gid_t *pgid) *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) { + bool expired = true; + bool ret; ZERO_STRUCTP(psid); if (fetch_sid_from_uid_cache(psid, uid)) return; - if (!winbind_uid_to_sid(psid, uid)) { - if (!winbind_ping()) { + /* Check the winbindd cache directly. */ + ret = idmap_cache_find_uid2sid(uid, psid, &expired); + + if (ret && !expired && is_null_sid(psid)) { + /* + * Negative cache entry, we already asked. + * do legacy. + */ + legacy_uid_to_sid(psid, uid); + return; + } + + if (!ret || expired) { + /* Not in cache. Ask winbindd. */ + if (!winbind_uid_to_sid(psid, uid)) { + /* + * 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)); + legacy_uid_to_sid(psid, uid); return; } - - DEBUG(5, ("uid_to_sid: winbind failed to find a sid for uid %u\n", - uid)); - return; } DEBUG(10,("uid %u -> sid %s\n", (unsigned int)uid, @@ -1356,27 +1344,51 @@ 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) { + bool expired = true; + bool ret; ZERO_STRUCTP(psid); if (fetch_sid_from_gid_cache(psid, gid)) return; - if (!winbind_gid_to_sid(psid, gid)) { - if (!winbind_ping()) { + /* Check the winbindd cache directly. */ + ret = idmap_cache_find_gid2sid(gid, psid, &expired); + + if (ret && !expired && is_null_sid(psid)) { + /* + * Negative cache entry, we already asked. + * do legacy. + */ + legacy_gid_to_sid(psid, gid); + return; + } + + 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)); + legacy_gid_to_sid(psid, gid); return; } - - DEBUG(5, ("gid_to_sid: winbind failed to find a sid for gid %u\n", - gid)); - return; } DEBUG(10,("gid %u -> sid %s\n", (unsigned int)gid, sid_string_dbg(psid))); - + store_gid_sid_cache(psid, gid); return; } @@ -1385,8 +1397,10 @@ 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) { + bool expired = true; + bool ret; uint32 rid; gid_t gid; @@ -1409,14 +1423,25 @@ bool sid_to_uid(const DOM_SID *psid, uid_t *puid) return true; } - if (!winbind_sid_to_uid(puid, psid)) { - if (!winbind_ping()) { + /* Check the winbindd cache directly. */ + ret = idmap_cache_find_sid2uid(psid, puid, &expired); + + 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, ("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); } - - DEBUG(5, ("winbind failed to find a uid for sid %s\n", - sid_string_dbg(psid))); - return false; } /* TODO: Here would be the place to allocate both a gid and a uid for @@ -1434,8 +1459,10 @@ 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; uid_t uid; @@ -1457,24 +1484,224 @@ bool sid_to_gid(const DOM_SID *psid, gid_t *pgid) return true; } - /* Ask winbindd if it can map this sid to a gid. - * (Idmap will check it is a valid SID and of the right type) */ + /* 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 (!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) ) { - if ( !winbind_sid_to_gid(pgid, psid) ) { - if (!winbind_ping()) { + 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); } - - DEBUG(10,("winbind failed to find a gid for sid %s\n", - sid_string_dbg(psid))); - return false; } DEBUG(10,("sid %s -> gid %u\n", sid_string_dbg(psid), (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", + 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 */ + ZERO_STRUCTP(group_sid); + if (pdb_gid_to_sid(pwd->pw_gid, 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; + + 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))); + } + + /* 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; +} + +bool delete_uid_cache(uid_t puid) +{ + DATA_BLOB uid = data_blob_const(&puid, sizeof(puid)); + DATA_BLOB sid; + + if (!memcache_lookup(NULL, UID_SID_CACHE, uid, &sid)) { + DEBUG(3, ("UID %d is not memcached!\n", (int)puid)); + return false; + } + DEBUG(3, ("Delete mapping UID %d <-> %s from memcache\n", (int)puid, + sid_string_dbg((struct dom_sid*)sid.data))); + memcache_delete(NULL, SID_UID_CACHE, sid); + memcache_delete(NULL, UID_SID_CACHE, uid); + return true; +} + +bool delete_gid_cache(gid_t pgid) +{ + DATA_BLOB gid = data_blob_const(&pgid, sizeof(pgid)); + DATA_BLOB sid; + if (!memcache_lookup(NULL, GID_SID_CACHE, gid, &sid)) { + DEBUG(3, ("GID %d is not memcached!\n", (int)pgid)); + return false; + } + DEBUG(3, ("Delete mapping GID %d <-> %s from memcache\n", (int)pgid, + sid_string_dbg((struct dom_sid*)sid.data))); + memcache_delete(NULL, SID_GID_CACHE, sid); + memcache_delete(NULL, GID_SID_CACHE, gid); + return true; +} + +bool delete_sid_cache(const struct dom_sid* psid) +{ + DATA_BLOB sid = data_blob_const(psid, ndr_size_dom_sid(psid, 0)); + DATA_BLOB id; + if (memcache_lookup(NULL, SID_GID_CACHE, sid, &id)) { + DEBUG(3, ("Delete mapping %s <-> GID %d from memcache\n", + sid_string_dbg(psid), *(int*)id.data)); + memcache_delete(NULL, SID_GID_CACHE, sid); + memcache_delete(NULL, GID_SID_CACHE, id); + } else if (memcache_lookup(NULL, SID_UID_CACHE, sid, &id)) { + DEBUG(3, ("Delete mapping %s <-> UID %d from memcache\n", + sid_string_dbg(psid), *(int*)id.data)); + memcache_delete(NULL, SID_UID_CACHE, sid); + memcache_delete(NULL, UID_SID_CACHE, id); + } else { + DEBUG(3, ("SID %s is not memcached!\n", sid_string_dbg(psid))); + return false; + } + return true; +} + +void flush_gid_cache(void) +{ + DEBUG(3, ("Flush GID <-> SID memcache\n")); + memcache_flush(NULL, SID_GID_CACHE); + memcache_flush(NULL, GID_SID_CACHE); +} + +void flush_uid_cache(void) +{ + DEBUG(3, ("Flush UID <-> SID memcache\n")); + memcache_flush(NULL, SID_UID_CACHE); + memcache_flush(NULL, UID_SID_CACHE); +}