From 0af1500fc0bafe61019f1b2ab1d9e1d369221240 Mon Sep 17 00:00:00 2001 From: Gerald Carter Date: Fri, 3 Feb 2006 22:19:41 +0000 Subject: r13316: Let the carnage begin.... Sync with trunk as off r13315 (This used to be commit 17e63ac4ed8325c0d44fe62b2442449f3298559f) --- source3/passdb/lookup_sid.c | 831 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 659 insertions(+), 172 deletions(-) (limited to 'source3/passdb/lookup_sid.c') diff --git a/source3/passdb/lookup_sid.c b/source3/passdb/lookup_sid.c index 4640eb6ae5..6266aa9cab 100644 --- a/source3/passdb/lookup_sid.c +++ b/source3/passdb/lookup_sid.c @@ -3,6 +3,7 @@ uid/user handling Copyright (C) Andrew Tridgell 1992-1998 Copyright (C) Gerald (Jerry) Carter 2003 + Copyright (C) Volker Lendecke 2005 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -67,7 +68,7 @@ BOOL lookup_name(TALLOC_CTX *mem_ctx, if (strequal(domain, get_global_sam_name())) { /* It's our own domain, lookup the name in passdb */ - if (lookup_global_sam_name(name, &rid, &type)) { + if (lookup_global_sam_name(name, flags, &rid, &type)) { sid_copy(&sid, get_global_sam_sid()); sid_append_rid(&sid, rid); goto ok; @@ -87,16 +88,31 @@ BOOL lookup_name(TALLOC_CTX *mem_ctx, goto failed; } - if (domain[0] != '\0') { - /* An explicit domain name was given, here our last resort is - * winbind. */ - if (winbind_lookup_name(domain, name, &sid, &type)) { + /* 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') && + (winbind_lookup_name(domain, name, &sid, &type))) { + goto ok; + } + + if (strequal(domain, unix_users_domain_name())) { + if (lookup_unix_user_name(name, &sid)) { + type = SID_NAME_USER; + goto ok; + } + goto failed; + } + + if (strequal(domain, unix_groups_domain_name())) { + if (lookup_unix_group_name(name, &sid)) { + type = SID_NAME_DOM_GRP; goto ok; } goto failed; } - if (!(flags & LOOKUP_NAME_ISOLATED)) { + if ((domain[0] == '\0') && (!(flags & LOOKUP_NAME_ISOLATED))) { goto failed; } @@ -176,7 +192,7 @@ BOOL lookup_name(TALLOC_CTX *mem_ctx, /* Both cases are done by looking at our passdb */ - if (lookup_global_sam_name(name, &rid, &type)) { + if (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); @@ -232,6 +248,22 @@ BOOL lookup_name(TALLOC_CTX *mem_ctx, } /* 10. Don't translate */ + + /* 11. Ok, windows would end here. Samba has two more options: + Unmapped users and unmapped groups */ + + if (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)) { + domain = talloc_strdup(tmp_ctx, unix_groups_domain_name()); + type = SID_NAME_DOM_GRP; + goto ok; + } + failed: talloc_free(tmp_ctx); return False; @@ -265,113 +297,513 @@ BOOL lookup_name(TALLOC_CTX *mem_ctx, return True; } -/***************************************************************** - *THE CANONICAL* convert SID to name function. - Tries local lookup first - for local sids, then tries winbind. -*****************************************************************/ +static BOOL winbind_lookup_rids(TALLOC_CTX *mem_ctx, + const DOM_SID *domain_sid, + int num_rids, uint32 *rids, + const char **domain_name, + const char **names, uint32 *types) +{ + /* Unless the winbind interface is upgraded, fall back to ask for + * individual sids. I imagine introducing a lookuprids operation that + * directly proxies to lsa_lookupsids to the correct DC. -- vl */ + + int i; + for (i=0; inum_auths != 4) { + /* This can't be a domain */ + return False; } - if (sid_check_is_in_builtin(sid)) { - uint32 rid; + if (IS_DC) { + uint32 i, num_domains; + struct trustdom_info **domains; - SMB_ASSERT(sid_peek_rid(sid, &rid)); + /* This is relatively expensive, but it happens only on DCs + * and for SIDs that have 4 sub-authorities and thus look like + * domains */ - if (!lookup_builtin_rid(tmp_ctx, rid, &name)) { - goto failed; + if (!NT_STATUS_IS_OK(secrets_trusted_domains(mem_ctx, + &num_domains, + &domains))) { + return False; } - /* There's only aliases in S-1-5-32 */ - type = SID_NAME_ALIAS; - domain = talloc_strdup(tmp_ctx, builtin_domain_name()); + for (i=0; isid)) { + *name = talloc_strdup(mem_ctx, + domains[i]->name); + return True; + } + } + return False; + } - goto ok; + if (winbind_lookup_sid(mem_ctx, sid, &tmp, NULL, &type) && + (type == SID_NAME_DOMAIN)) { + *name = tmp; + return True; } - if (winbind_lookup_sid(tmp_ctx, sid, &domain, &name, &type)) { - goto ok; + return False; +} + +/* + * This tries to implement the rather weird rules for the lsa_lookup level + * parameter. + * + * This is as close as we can get to what W2k3 does. With this we survive the + * RPC-LSALOOKUP samba4 test as of 2006-01-08. NT4 as a PDC is a bit more + * different, but I assume that's just being too liberal. For example, W2k3 + * replies to everything else but the levels 1-6 with INVALID_PARAMETER + * whereas NT4 does the same as level 1 (I think). I did not fully test that + * with NT4, this is what w2k3 does. + * + * Level 1: Ask everywhere + * 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 6: Like 4 + */ + +static BOOL check_dom_sid_to_level(const DOM_SID *sid, int level) +{ + int ret = False; + + switch(level) { + case 1: + ret = True; + break; + case 2: + ret = (!sid_check_is_builtin(sid) && + !sid_check_is_wellknown_domain(sid, NULL)); + break; + case 3: + case 4: + case 6: + ret = sid_check_is_domain(sid); + break; + case 5: + ret = False; + break; } - DEBUG(10,("lookup_sid: winbind lookup for SID %s failed - trying " - "special SIDs.\n", sid_string_static(sid))); + DEBUG(10, ("%s SID %s in level %d\n", + ret ? "Accepting" : "Rejecting", + sid_string_static(sid), level)); + return ret; +} - if (lookup_wellknown_sid(tmp_ctx, sid, &domain, &name)) { - type = SID_NAME_WKN_GRP; - goto ok; +/* + * Lookup a bunch of SIDs. This is modeled after lsa_lookup_sids with + * references to domains, it is explicitly made for this. + * + * 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. + */ + +NTSTATUS lookup_sids(TALLOC_CTX *mem_ctx, int num_sids, + const 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; + + int i, j; + + tmp_ctx = talloc_new(mem_ctx); + if (tmp_ctx == NULL) { + DEBUG(0, ("talloc_new failed\n")); + return NT_STATUS_NO_MEMORY; } - failed: - DEBUG(10, ("Failed to lookup sid %s\n", sid_string_static(sid))); + name_infos = TALLOC_ARRAY(tmp_ctx, struct lsa_name_info, num_sids); + dom_infos = TALLOC_ZERO_ARRAY(tmp_ctx, struct lsa_dom_info, + MAX_REF_DOMAINS); + if ((name_infos == NULL) || (dom_infos == NULL)) { + result = NT_STATUS_NO_MEMORY; + goto done; + } + + /* First build up the data structures: + * + * dom_infos is a list of domains referenced in the list of + * SIDs. Later we will walk the list of domains and look up the RIDs + * in bulk. + * + * name_infos is a shadow-copy of the SIDs array to collect the real + * data. + * + * dom_info->idxs is an index into the name_infos array. The + * difficulty we have here is that we need to keep the SIDs the client + * asked for in the same order for the reply + */ + + for (i=0; ivalid) { + /* No domains left, we're done */ + break; + } + + rids = TALLOC_ARRAY(tmp_ctx, uint32, dom->num_idxs); + + if (rids == NULL) { + result = NT_STATUS_NO_MEMORY; + goto done; + } + + for (j=0; jnum_idxs; j++) { + rids[j] = name_infos[dom->idxs[j]].rid; + } + + if (!lookup_rids(tmp_ctx, &dom->sid, + dom->num_idxs, rids, &dom->name, + &names, &types)) { + result = NT_STATUS_NO_MEMORY; + goto done; + } + + talloc_steal(dom_infos, dom->name); + + for (j=0; jnum_idxs; j++) { + int idx = dom->idxs[j]; + name_infos[idx].type = types[j]; + if (types[j] != SID_NAME_UNKNOWN) { + name_infos[idx].name = + talloc_steal(name_infos, names[j]); + } else { + name_infos[idx].name = NULL; + } + } + } + + *ret_domains = talloc_steal(mem_ctx, dom_infos); + *ret_names = talloc_steal(mem_ctx, name_infos); + result = NT_STATUS_OK; + + done: talloc_free(tmp_ctx); - return False; + return result; +} - ok: +/***************************************************************** + *THE CANONICAL* convert SID to name function. +*****************************************************************/ - if ((domain == NULL) || (name == NULL)) { - DEBUG(0, ("talloc failed\n")); - talloc_free(tmp_ctx); +BOOL lookup_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid, + const char **ret_domain, const char **ret_name, + enum SID_NAME_USE *ret_type) +{ + struct lsa_dom_info *domain; + struct lsa_name_info *name; + TALLOC_CTX *tmp_ctx; + BOOL ret = False; + + tmp_ctx = talloc_new(mem_ctx); + + if (tmp_ctx == NULL) { + DEBUG(0, ("talloc_new failed\n")); return False; } + if (!NT_STATUS_IS_OK(lookup_sids(tmp_ctx, 1, &sid, 1, + &domain, &name))) { + goto done; + } + + if (name->type == SID_NAME_UNKNOWN) { + goto done; + } + if (ret_domain != NULL) { - *ret_domain = talloc_steal(mem_ctx, domain); + *ret_domain = talloc_steal(mem_ctx, domain->name); } if (ret_name != NULL) { - *ret_name = talloc_steal(mem_ctx, name); + *ret_name = talloc_steal(mem_ctx, name->name); } if (ret_type != NULL) { - *ret_type = type; + *ret_type = name->type; } + ret = True; + + done: + if (ret) { + DEBUG(10, ("Sid %s -> %s\\%s(%d)\n", + sid_string_static(sid), domain->name, + name->name, name->type)); + } else { + DEBUG(10, ("failed to lookup sid %s\n", + sid_string_static(sid))); + } talloc_free(tmp_ctx); - return True; + return ret; } /***************************************************************** @@ -448,7 +880,7 @@ static BOOL fetch_uid_from_cache( uid_t *puid, const DOM_SID *psid ) Store uid to SID mapping in cache. *****************************************************************/ -static void store_uid_sid_cache(const DOM_SID *psid, uid_t uid) +void store_uid_sid_cache(const DOM_SID *psid, uid_t uid) { struct uid_sid_cache *pc; @@ -520,7 +952,7 @@ static BOOL fetch_gid_from_cache(gid_t *pgid, const DOM_SID *psid) Store gid to SID mapping in cache. *****************************************************************/ -static void store_gid_sid_cache(const DOM_SID *psid, gid_t gid) +void store_gid_sid_cache(const DOM_SID *psid, gid_t gid) { struct gid_sid_cache *pc; @@ -552,200 +984,255 @@ static void store_gid_sid_cache(const DOM_SID *psid, gid_t gid) *THE CANONICAL* convert uid_t to SID function. *****************************************************************/ -NTSTATUS uid_to_sid(DOM_SID *psid, uid_t uid) +void uid_to_sid(DOM_SID *psid, uid_t uid) { uid_t low, high; + uint32 rid; ZERO_STRUCTP(psid); if (fetch_sid_from_uid_cache(psid, uid)) - return ( psid ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL ); + return; - /* DC's never use winbindd to resolve users outside the - defined idmap range */ + if (lp_idmap_uid(&low, &high) && (uid >= low) && (uid <= high) && + winbind_uid_to_sid(psid, uid)) { - if ( lp_server_role()==ROLE_DOMAIN_MEMBER - || (lp_idmap_uid(&low, &high) && uid >= low && uid <= high) ) - { - if (winbind_uid_to_sid(psid, uid)) { - - DEBUG(10,("uid_to_sid: winbindd %u -> %s\n", - (unsigned int)uid, sid_string_static(psid))); + DEBUG(10,("uid_to_sid: winbindd %u -> %s\n", + (unsigned int)uid, sid_string_static(psid))); + goto done; + } - if (psid) - store_uid_sid_cache(psid, uid); - return ( psid ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL ); - } + 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 (!local_uid_to_sid(psid, uid)) { - DEBUG(10,("uid_to_sid: local %u failed to map to sid\n", (unsigned int)uid )); - return NT_STATUS_UNSUCCESSFUL; + if (pdb_rid_algorithm() && (uid < max_algorithmic_uid())) { + sid_copy(psid, get_global_sam_sid()); + sid_append_rid(psid, algorithmic_pdb_uid_to_user_rid(uid)); + goto done; + } else { + sid_copy(psid, &global_sid_Unix_Users); + sid_append_rid(psid, uid); + goto done; } - + + done: DEBUG(10,("uid_to_sid: local %u -> %s\n", (unsigned int)uid, sid_string_static(psid))); store_uid_sid_cache(psid, uid); - return NT_STATUS_OK; + return; } /***************************************************************** *THE CANONICAL* convert gid_t to SID function. *****************************************************************/ -NTSTATUS gid_to_sid(DOM_SID *psid, gid_t gid) +void gid_to_sid(DOM_SID *psid, gid_t gid) { gid_t low, high; ZERO_STRUCTP(psid); if (fetch_sid_from_gid_cache(psid, gid)) - return ( psid ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL ); + return; - /* DC's never use winbindd to resolve groups outside the - defined idmap range */ + if (lp_idmap_gid(&low, &high) && (gid >= low) && (gid <= high) && + winbind_gid_to_sid(psid, gid)) { - if ( lp_server_role()==ROLE_DOMAIN_MEMBER - || (lp_idmap_gid(&low, &high) && gid >= low && gid <= high) ) - { - if (winbind_gid_to_sid(psid, gid)) { + DEBUG(10,("gid_to_sid: winbindd %u -> %s\n", + (unsigned int)gid, sid_string_static(psid))); + goto done; + } - DEBUG(10,("gid_to_sid: winbindd %u -> %s\n", - (unsigned int)gid, sid_string_static(psid))); - - if (psid) - store_gid_sid_cache(psid, gid); - return ( psid ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL ); - } + if (pdb_gid_to_sid(gid, psid)) { + /* This is a mapped group */ + goto done; } - if (!local_gid_to_sid(psid, gid)) { - DEBUG(10,("gid_to_sid: local %u failed to map to sid\n", (unsigned int)gid )); - return NT_STATUS_UNSUCCESSFUL; + if (pdb_rid_algorithm() && (gid < max_algorithmic_gid())) { + sid_copy(psid, get_global_sam_sid()); + sid_append_rid(psid, pdb_gid_to_group_rid(gid)); + goto done; + } else { + sid_copy(psid, &global_sid_Unix_Groups); + sid_append_rid(psid, gid); + goto done; } - + + done: DEBUG(10,("gid_to_sid: local %u -> %s\n", (unsigned int)gid, sid_string_static(psid))); store_gid_sid_cache(psid, gid); - return NT_STATUS_OK; + return; } /***************************************************************** *THE CANONICAL* convert SID to uid function. *****************************************************************/ -NTSTATUS sid_to_uid(const DOM_SID *psid, uid_t *puid) +BOOL sid_to_uid(const DOM_SID *psid, uid_t *puid) { - enum SID_NAME_USE name_type; + enum SID_NAME_USE type; + uint32 rid; + gid_t gid; if (fetch_uid_from_cache(puid, psid)) - return NT_STATUS_OK; + return True; - /* if this is our SID then go straight to a local lookup */ - - if ( sid_compare_domain(get_global_sam_sid(), psid) == 0 ) { - DEBUG(10,("sid_to_uid: my domain (%s) - trying local.\n", - sid_string_static(psid) )); - - if ( local_sid_to_uid(puid, psid, &name_type) ) - goto success; - - DEBUG(10,("sid_to_uid: local lookup failed\n")); - - return NT_STATUS_UNSUCCESSFUL; + if (fetch_gid_from_cache(&gid, psid)) { + return False; } - - /* If it is not our local domain, only hope is winbindd */ - if ( !winbind_lookup_sid(NULL, psid, NULL, NULL, &name_type) ) { - DEBUG(10,("sid_to_uid: winbind lookup for non-local sid %s failed\n", - sid_string_static(psid) )); - - return NT_STATUS_UNSUCCESSFUL; + if (sid_peek_check_rid(&global_sid_Unix_Users, psid, &rid)) { + uid_t uid = rid; + *puid = uid; + goto done; } - /* If winbindd does know the SID, ensure this is a user */ + 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; + } + if (pdb_rid_algorithm() && + algorithmic_pdb_rid_is_user(rid)) { + *puid = algorithmic_pdb_user_rid_to_uid(rid); + goto done; + } - if (name_type != SID_NAME_USER) { - DEBUG(10,("sid_to_uid: winbind lookup succeeded but SID is not a user (%u)\n", - (unsigned int)name_type )); - return NT_STATUS_INVALID_PARAMETER; + /* This was ours, but it was neither mapped nor + * algorithmic. Fail */ + return False; } - /* get the uid. Has to work or else we are dead in the water */ + if (winbind_lookup_sid(NULL, psid, NULL, NULL, &type)) { + + 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 ( !winbind_sid_to_uid(puid, psid) ) { - DEBUG(10,("sid_to_uid: winbind failed to allocate a new uid for sid %s\n", - sid_string_static(psid))); - return NT_STATUS_UNSUCCESSFUL; + 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; + } + goto done; } -success: + /* 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), (unsigned int)*puid )); store_uid_sid_cache(psid, *puid); - - return NT_STATUS_OK; + return True; } + /***************************************************************** *THE CANONICAL* convert SID to gid function. Group mapping is used for gids that maps to Wellknown SIDs *****************************************************************/ -NTSTATUS sid_to_gid(const DOM_SID *psid, gid_t *pgid) +BOOL sid_to_gid(const DOM_SID *psid, gid_t *pgid) { - enum SID_NAME_USE name_type; + uint32 rid; + GROUP_MAP map; + union unid_t id; + enum SID_NAME_USE type; + uid_t uid; if (fetch_gid_from_cache(pgid, psid)) - return NT_STATUS_OK; + return True; - /* - * First we must look up the name and decide if this is a group sid. - * Group mapping can deal with foreign SIDs - */ + if (fetch_uid_from_cache(&uid, psid)) + return False; + + if (sid_peek_check_rid(&global_sid_Unix_Groups, psid, &rid)) { + gid_t gid = rid; + *pgid = gid; + goto done; + } + + if (sid_check_is_in_builtin(psid) && pdb_getgrsid(&map, *psid)) { + *pgid = map.gid; + goto done; + } - if ( local_sid_to_gid(pgid, psid, &name_type) ) - goto success; + 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 (pdb_rid_algorithm() && + !algorithmic_pdb_rid_is_user(rid)) { + /* This must be a group, presented as alias */ + *pgid = pdb_group_rid_to_gid(rid); + goto done; + } + /* This was ours, but it was neither mapped nor + * algorithmic. Fail. */ + return False; + } - if (!winbind_lookup_sid(NULL, psid, NULL, NULL, &name_type)) { - DEBUG(10,("sid_to_gid: no one knows the SID %s (tried local, then " - "winbind)\n", sid_string_static(psid))); + 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 NT_STATUS_UNSUCCESSFUL; + return False; } /* winbindd knows it; Ensure this is a group sid */ - if ((name_type != SID_NAME_DOM_GRP) && (name_type != SID_NAME_ALIAS) - && (name_type != SID_NAME_WKN_GRP)) - { - DEBUG(10,("sid_to_gid: winbind lookup succeeded but SID is not a known group (%u)\n", - (unsigned int)name_type )); - - /* winbindd is running and knows about this SID. Just the wrong type. - Don't fallback to a local lookup here */ - - return NT_STATUS_INVALID_PARAMETER; + 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; } /* 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 NT_STATUS_UNSUCCESSFUL; + DEBUG(10,("sid_to_gid: winbind failed to allocate a new gid " + "for sid %s\n", sid_string_static(psid))); + return False; } -success: + done: DEBUG(10,("sid_to_gid: %s -> %u\n", sid_string_static(psid), (unsigned int)*pgid )); store_gid_sid_cache(psid, *pgid); - return NT_STATUS_OK; + return True; } -- cgit