From 8daa83a594a2e98f39d764422bfbdbc62c9efd44 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 19:20:00 +0200 Subject: Adding upstream version 2:4.20.0+dfsg. Signed-off-by: Daniel Baumann --- source3/auth/token_util.c | 1338 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1338 insertions(+) create mode 100644 source3/auth/token_util.c (limited to 'source3/auth/token_util.c') diff --git a/source3/auth/token_util.c b/source3/auth/token_util.c new file mode 100644 index 0000000..023ad7c --- /dev/null +++ b/source3/auth/token_util.c @@ -0,0 +1,1338 @@ +/* + * Unix SMB/CIFS implementation. + * Authentication utility functions + * Copyright (C) Andrew Tridgell 1992-1998 + * Copyright (C) Andrew Bartlett 2001 + * Copyright (C) Jeremy Allison 2000-2001 + * Copyright (C) Rafal Szczesniak 2002 + * Copyright (C) Volker Lendecke 2006 + * Copyright (C) Michael Adam 2007 + * + * 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 . + */ + +/* functions moved from auth/auth_util.c to minimize linker deps */ + +#include "includes.h" +#include "lib/util_unixsids.h" +#include "system/passwd.h" +#include "auth.h" +#include "secrets.h" +#include "../lib/util/memcache.h" +#include "../librpc/gen_ndr/netlogon.h" +#include "../libcli/security/security.h" +#include "../lib/util/util_pw.h" +#include "passdb.h" +#include "lib/privileges.h" + +/**************************************************************************** + Check for a SID in an struct security_token +****************************************************************************/ + +bool nt_token_check_sid ( const struct dom_sid *sid, const struct security_token *token ) +{ + if ( !sid || !token ) + return False; + + return security_token_has_sid(token, sid); +} + +bool nt_token_check_domain_rid( struct security_token *token, uint32_t rid ) +{ + struct dom_sid domain_sid; + + /* if we are a domain member, the get the domain SID, else for + a DC or standalone server, use our own SID */ + + if ( lp_server_role() == ROLE_DOMAIN_MEMBER ) { + if ( !secrets_fetch_domain_sid( lp_workgroup(), + &domain_sid ) ) { + DEBUG(1,("nt_token_check_domain_rid: Cannot lookup " + "SID for domain [%s]\n", lp_workgroup())); + return False; + } + } + else + sid_copy( &domain_sid, get_global_sam_sid() ); + + sid_append_rid( &domain_sid, rid ); + + return nt_token_check_sid( &domain_sid, token );\ +} + +/****************************************************************************** + Create a token for the root user to be used internally by smbd. + This is similar to running under the context of the LOCAL_SYSTEM account + in Windows. This is a read-only token. Do not modify it or free() it. + Create a copy if you need to change it. +******************************************************************************/ + +NTSTATUS get_root_nt_token( struct security_token **token ) +{ + struct security_token *for_cache; + struct dom_sid u_sid, g_sid; + struct passwd *pw; + void *cache_data; + NTSTATUS status = NT_STATUS_OK; + + cache_data = memcache_lookup_talloc( + NULL, SINGLETON_CACHE_TALLOC, + data_blob_string_const_null("root_nt_token")); + + if (cache_data != NULL) { + *token = talloc_get_type_abort( + cache_data, struct security_token); + return NT_STATUS_OK; + } + + if ( !(pw = getpwuid(0)) ) { + if ( !(pw = getpwnam("root")) ) { + DBG_ERR("get_root_nt_token: both getpwuid(0) " + "and getpwnam(\"root\") failed!\n"); + return NT_STATUS_NO_SUCH_USER; + } + } + + /* get the user and primary group SIDs; although the + BUILTIN\Administrators SId is really the one that matters here */ + + uid_to_sid(&u_sid, pw->pw_uid); + gid_to_sid(&g_sid, pw->pw_gid); + + status = create_local_nt_token(talloc_tos(), &u_sid, False, + 1, &global_sid_Builtin_Administrators, token); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + security_token_set_privilege(*token, SEC_PRIV_DISK_OPERATOR); + + for_cache = *token; + + memcache_add_talloc( + NULL, SINGLETON_CACHE_TALLOC, + data_blob_string_const_null("root_nt_token"), &for_cache); + + return status; +} + + +/* + * Add alias SIDs from memberships within the partially created token SID list + */ + +NTSTATUS add_aliases(const struct dom_sid *domain_sid, + struct security_token *token) +{ + uint32_t *aliases; + size_t i, num_aliases; + NTSTATUS status; + TALLOC_CTX *tmp_ctx; + + if (!(tmp_ctx = talloc_init("add_aliases"))) { + return NT_STATUS_NO_MEMORY; + } + + aliases = NULL; + num_aliases = 0; + + status = pdb_enum_alias_memberships(tmp_ctx, domain_sid, + token->sids, + token->num_sids, + &aliases, &num_aliases); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10, ("pdb_enum_alias_memberships failed: %s\n", + nt_errstr(status))); + goto done; + } + + for (i=0; isids, + &token->num_sids); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("add_sid_to_array failed\n")); + goto done; + } + } + +done: + TALLOC_FREE(tmp_ctx); + return NT_STATUS_OK; +} + +/******************************************************************* +*******************************************************************/ + +static NTSTATUS add_builtin_administrators(struct security_token *token, + const struct dom_sid *dom_sid) +{ + struct dom_sid domadm; + NTSTATUS status; + + /* nothing to do if we aren't in a domain */ + + if ( !(IS_DC || lp_server_role()==ROLE_DOMAIN_MEMBER) ) { + return NT_STATUS_OK; + } + + /* Find the Domain Admins SID */ + + if ( IS_DC ) { + sid_copy( &domadm, get_global_sam_sid() ); + } else { + if (dom_sid == NULL) { + return NT_STATUS_INVALID_PARAMETER_MIX; + } + sid_copy(&domadm, dom_sid); + } + sid_append_rid( &domadm, DOMAIN_RID_ADMINS ); + + /* Add Administrators if the user beloongs to Domain Admins */ + + if ( nt_token_check_sid( &domadm, token ) ) { + status = add_sid_to_array(token, + &global_sid_Builtin_Administrators, + &token->sids, &token->num_sids); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + } + + return NT_STATUS_OK; +} + +static NTSTATUS add_builtin_guests(struct security_token *token, + const struct dom_sid *dom_sid) +{ + struct dom_sid tmp_sid; + NTSTATUS status; + + /* + * First check the local GUEST account. + */ + sid_compose(&tmp_sid, get_global_sam_sid(), DOMAIN_RID_GUEST); + + if (nt_token_check_sid(&tmp_sid, token)) { + status = add_sid_to_array_unique(token, + &global_sid_Builtin_Guests, + &token->sids, &token->num_sids); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + return NT_STATUS_OK; + } + + /* + * First check the local GUESTS group. + */ + sid_compose(&tmp_sid, get_global_sam_sid(), DOMAIN_RID_GUESTS); + + if (nt_token_check_sid(&tmp_sid, token)) { + status = add_sid_to_array_unique(token, + &global_sid_Builtin_Guests, + &token->sids, &token->num_sids); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + return NT_STATUS_OK; + } + + if (lp_server_role() != ROLE_DOMAIN_MEMBER) { + return NT_STATUS_OK; + } + + if (dom_sid == NULL) { + return NT_STATUS_INVALID_PARAMETER_MIX; + } + + /* + * First check the domain GUESTS group. + */ + sid_copy(&tmp_sid, dom_sid); + sid_append_rid(&tmp_sid, DOMAIN_RID_GUESTS); + + if (nt_token_check_sid(&tmp_sid, token)) { + status = add_sid_to_array_unique(token, + &global_sid_Builtin_Guests, + &token->sids, &token->num_sids); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + return NT_STATUS_OK; + } + + return NT_STATUS_OK; +} + +static NTSTATUS add_local_groups(struct security_token *result, + bool is_guest); + +NTSTATUS get_user_sid_info3_and_extra(const struct netr_SamInfo3 *info3, + const struct extra_auth_info *extra, + struct dom_sid *sid) +{ + /* USER SID */ + if (info3->base.rid == (uint32_t)(-1)) { + /* this is a signal the user was fake and generated, + * the actual SID we want to use is stored in the extra + * sids */ + if (is_null_sid(&extra->user_sid)) { + /* we couldn't find the user sid, bail out */ + DEBUG(3, ("Invalid user SID\n")); + return NT_STATUS_UNSUCCESSFUL; + } + sid_copy(sid, &extra->user_sid); + } else { + sid_copy(sid, info3->base.domain_sid); + sid_append_rid(sid, info3->base.rid); + } + return NT_STATUS_OK; +} + +static struct security_token *init_local_nt_token(TALLOC_CTX *mem_ctx) +{ + /* + * We do not have a method to populate the claims into this + * buffer in the source3/ stack. When that changes, we will + * instead make this optional based on lp_acl_claims_evaluation() + */ + + struct security_token *result + = security_token_initialise(mem_ctx, + CLAIMS_EVALUATION_NEVER); + + if (result == NULL) { + DBG_ERR("talloc failed for security_token\n"); + return NULL; + } + + return result; +} + +NTSTATUS create_local_nt_token_from_info3(TALLOC_CTX *mem_ctx, + bool is_guest, + const struct netr_SamInfo3 *info3, + const struct extra_auth_info *extra, + struct security_token **ntok) +{ + struct security_token *usrtok = NULL; + uint32_t session_info_flags = 0; + NTSTATUS status; + uint32_t i; + + DEBUG(10, ("Create local NT token for %s\n", + info3->base.account_name.string)); + + usrtok = init_local_nt_token(mem_ctx); + if (!usrtok) { + return NT_STATUS_NO_MEMORY; + } + + /* Add the user and primary group sid FIRST */ + /* check if the user rid is the special "Domain Guests" rid. + * If so pick the first sid for the extra sids instead as it + * is a local fake account */ + usrtok->sids = talloc_array(usrtok, struct dom_sid, 2); + if (!usrtok->sids) { + TALLOC_FREE(usrtok); + return NT_STATUS_NO_MEMORY; + } + usrtok->num_sids = 2; + + status = get_user_sid_info3_and_extra(info3, extra, &usrtok->sids[0]); + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(usrtok); + return status; + } + + /* GROUP SID */ + if (info3->base.primary_gid == (uint32_t)(-1)) { + /* this is a signal the user was fake and generated, + * the actual SID we want to use is stored in the extra + * sids */ + if (is_null_sid(&extra->pgid_sid)) { + /* we couldn't find the user sid, bail out */ + DEBUG(3, ("Invalid group SID\n")); + TALLOC_FREE(usrtok); + return NT_STATUS_UNSUCCESSFUL; + } + sid_copy(&usrtok->sids[1], &extra->pgid_sid); + } else { + sid_copy(&usrtok->sids[1], info3->base.domain_sid); + sid_append_rid(&usrtok->sids[1], + info3->base.primary_gid); + } + + /* Now the SIDs we got from authentication. These are the ones from + * the info3 struct or from the pdb_enum_group_memberships, depending + * on who authenticated the user. + * Note that we start the for loop at "1" here, we already added the + * first group sid as primary above. */ + + for (i = 0; i < info3->base.groups.count; i++) { + struct dom_sid tmp_sid; + + sid_copy(&tmp_sid, info3->base.domain_sid); + sid_append_rid(&tmp_sid, info3->base.groups.rids[i].rid); + + status = add_sid_to_array_unique(usrtok, &tmp_sid, + &usrtok->sids, + &usrtok->num_sids); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(3, ("Failed to add SID to nt token\n")); + TALLOC_FREE(usrtok); + return status; + } + } + + /* now also add extra sids if they are not the special user/group + * sids */ + for (i = 0; i < info3->sidcount; i++) { + status = add_sid_to_array_unique(usrtok, + info3->sids[i].sid, + &usrtok->sids, + &usrtok->num_sids); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(3, ("Failed to add SID to nt token\n")); + TALLOC_FREE(usrtok); + return status; + } + } + + status = add_local_groups(usrtok, is_guest); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(3, ("Failed to add local groups\n")); + TALLOC_FREE(usrtok); + return status; + } + + session_info_flags |= AUTH_SESSION_INFO_DEFAULT_GROUPS; + if (!is_guest) { + session_info_flags |= AUTH_SESSION_INFO_AUTHENTICATED; + } + + status = finalize_local_nt_token(usrtok, session_info_flags); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(3, ("Failed to finalize nt token\n")); + TALLOC_FREE(usrtok); + return status; + } + + *ntok = usrtok; + return NT_STATUS_OK; +} + +/******************************************************************* + Create a NT token for the user, expanding local aliases +*******************************************************************/ + +NTSTATUS create_local_nt_token(TALLOC_CTX *mem_ctx, + const struct dom_sid *user_sid, + bool is_guest, + int num_groupsids, + const struct dom_sid *groupsids, + struct security_token **token) +{ + struct security_token *result = NULL; + int i; + NTSTATUS status; + uint32_t session_info_flags = 0; + struct dom_sid_buf buf; + + DEBUG(10, ("Create local NT token for %s\n", + dom_sid_str_buf(user_sid, &buf))); + + result = init_local_nt_token(mem_ctx); + if (result == NULL) { + status = NT_STATUS_NO_MEMORY; + goto err; + } + + /* Add the user and primary group sid */ + + status = add_sid_to_array(result, user_sid, + &result->sids, &result->num_sids); + if (!NT_STATUS_IS_OK(status)) { + goto err; + } + + /* For guest, num_groupsids may be zero. */ + if (num_groupsids) { + status = add_sid_to_array(result, &groupsids[0], + &result->sids, + &result->num_sids); + if (!NT_STATUS_IS_OK(status)) { + goto err; + } + } + + /* Now the SIDs we got from authentication. These are the ones from + * the info3 struct or from the pdb_enum_group_memberships, depending + * on who authenticated the user. + * Note that we start the for loop at "1" here, we already added the + * first group sid as primary above. */ + + for (i=1; isids, + &result->num_sids); + if (!NT_STATUS_IS_OK(status)) { + goto err; + } + } + + status = add_local_groups(result, is_guest); + if (!NT_STATUS_IS_OK(status)) { + goto err; + } + + session_info_flags |= AUTH_SESSION_INFO_DEFAULT_GROUPS; + if (!is_guest) { + session_info_flags |= AUTH_SESSION_INFO_AUTHENTICATED; + } + + status = finalize_local_nt_token(result, session_info_flags); + if (!NT_STATUS_IS_OK(status)) { + goto err; + } + + if (is_guest) { + /* + * It's ugly, but for now it's + * needed to add Builtin_Guests + * here, the "local" token only + * consist of S-1-22-* SIDs + * and finalize_local_nt_token() + * doesn't have the chance to + * to detect it need to + * add Builtin_Guests via + * add_builtin_guests(). + */ + status = add_sid_to_array_unique(result, + &global_sid_Builtin_Guests, + &result->sids, + &result->num_sids); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(3, ("Failed to add SID to nt token\n")); + goto err; + } + } + + *token = result; + return NT_STATUS_SUCCESS; + +err: + TALLOC_FREE(result); + return status; +} + +/*************************************************** + Merge in any groups from /etc/group. +***************************************************/ + +static NTSTATUS add_local_groups(struct security_token *result, + bool is_guest) +{ + gid_t *gids = NULL; + uint32_t getgroups_num_group_sids = 0; + struct passwd *pass = NULL; + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + uint32_t i; + + if (is_guest) { + /* + * Guest is a special case. It's always + * a user that can be looked up, but + * result->sids[0] is set to DOMAIN\Guest. + * Lookup by account name instead. + */ + pass = Get_Pwnam_alloc(tmp_ctx, lp_guest_account()); + } else { + uid_t uid; + + /* For non-guest result->sids[0] is always the user sid. */ + if (!sid_to_uid(&result->sids[0], &uid)) { + /* + * Non-mappable SID like SYSTEM. + * Can't be in any /etc/group groups. + */ + TALLOC_FREE(tmp_ctx); + return NT_STATUS_OK; + } + + pass = getpwuid_alloc(tmp_ctx, uid); + if (pass == NULL) { + struct dom_sid_buf buf; + DBG_ERR("SID %s -> getpwuid(%u) failed, is nsswitch configured?\n", + dom_sid_str_buf(&result->sids[0], &buf), + (unsigned int)uid); + TALLOC_FREE(tmp_ctx); + return NT_STATUS_NO_SUCH_USER; + } + } + + if (!pass) { + TALLOC_FREE(tmp_ctx); + return NT_STATUS_UNSUCCESSFUL; + } + + /* + * Now we must get any groups this user has been + * added to in /etc/group and merge them in. + * This has to be done in every code path + * that creates an NT token, as remote users + * may have been added to the local /etc/group + * database. Tokens created merely from the + * info3 structs (via the DC or via the krb5 PAC) + * won't have these local groups. Note the + * groups added here will only be UNIX groups + * (S-1-22-2-XXXX groups) as getgroups_unix_user() + * turns off winbindd before calling getgroups(). + * + * NB. This is duplicating work already + * done in the 'unix_user:' case of + * create_token_from_sid() but won't + * do anything other than be inefficient + * in that case. + */ + + if (!getgroups_unix_user(tmp_ctx, pass->pw_name, pass->pw_gid, + &gids, &getgroups_num_group_sids)) { + DEBUG(1, ("getgroups_unix_user for user %s failed\n", + pass->pw_name)); + TALLOC_FREE(tmp_ctx); + return NT_STATUS_UNSUCCESSFUL; + } + + for (i=0; isids, + &result->num_sids); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(3, ("Failed to add UNIX SID to nt token\n")); + TALLOC_FREE(tmp_ctx); + return status; + } + } + TALLOC_FREE(tmp_ctx); + return NT_STATUS_OK; +} + +NTSTATUS finalize_local_nt_token(struct security_token *result, + uint32_t session_info_flags) +{ + struct dom_sid _dom_sid = { 0, }; + struct dom_sid *domain_sid = NULL; + NTSTATUS status; + struct acct_info *info; + bool ok; + + result->privilege_mask = 0; + result->rights_mask = 0; + + if (result->num_sids == 0) { + return NT_STATUS_INVALID_TOKEN; + } + + if (session_info_flags & AUTH_SESSION_INFO_DEFAULT_GROUPS) { + status = add_sid_to_array(result, &global_sid_World, + &result->sids, &result->num_sids); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + status = add_sid_to_array(result, &global_sid_Network, + &result->sids, &result->num_sids); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + } + + /* + * Don't expand nested groups of system, anonymous etc + * + * Note that they still get SID_WORLD and SID_NETWORK + * for now in order let existing tests pass. + * + * But SYSTEM doesn't get AUTHENTICATED_USERS + * and ANONYMOUS doesn't get BUILTIN GUESTS anymore. + */ + if (security_token_is_anonymous(result)) { + return NT_STATUS_OK; + } + if (security_token_is_system(result)) { + result->privilege_mask = ~0; + return NT_STATUS_OK; + } + + if (session_info_flags & AUTH_SESSION_INFO_AUTHENTICATED) { + status = add_sid_to_array(result, + &global_sid_Authenticated_Users, + &result->sids, + &result->num_sids); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + } + + /* Add in BUILTIN sids */ + + become_root(); + ok = secrets_fetch_domain_sid(lp_workgroup(), &_dom_sid); + if (ok) { + domain_sid = &_dom_sid; + } else { + DEBUG(3, ("Failed to fetch domain sid for %s\n", + lp_workgroup())); + } + unbecome_root(); + + info = talloc_zero(talloc_tos(), struct acct_info); + if (info == NULL) { + DEBUG(0, ("talloc failed!\n")); + return NT_STATUS_NO_MEMORY; + } + + /* Deal with the BUILTIN\Administrators group. If the SID can + be resolved then assume that the add_aliasmem( S-1-5-32 ) + handled it. */ + + status = pdb_get_aliasinfo(&global_sid_Builtin_Administrators, info); + if (!NT_STATUS_IS_OK(status)) { + + become_root(); + status = create_builtin_administrators(domain_sid); + unbecome_root(); + + if (NT_STATUS_EQUAL(status, NT_STATUS_PROTOCOL_UNREACHABLE)) { + /* Add BUILTIN\Administrators directly to token. */ + status = add_builtin_administrators(result, domain_sid); + if ( !NT_STATUS_IS_OK(status) ) { + DEBUG(3, ("Failed to check for local " + "Administrators membership (%s)\n", + nt_errstr(status))); + } + } else if (!NT_STATUS_IS_OK(status)) { + DEBUG(2, ("WARNING: Failed to create " + "BUILTIN\\Administrators group! Can " + "Winbind allocate gids?\n")); + } + } + + /* Deal with the BUILTIN\Users group. If the SID can + be resolved then assume that the add_aliasmem( S-1-5-32 ) + handled it. */ + + status = pdb_get_aliasinfo(&global_sid_Builtin_Users, info); + if (!NT_STATUS_IS_OK(status)) { + + become_root(); + status = create_builtin_users(domain_sid); + unbecome_root(); + + if (!NT_STATUS_EQUAL(status, NT_STATUS_PROTOCOL_UNREACHABLE) && + !NT_STATUS_IS_OK(status)) + { + DEBUG(2, ("WARNING: Failed to create BUILTIN\\Users group! " + "Can Winbind allocate gids?\n")); + } + } + + /* + * Deal with the BUILTIN\Guests group. If the SID can + * be resolved then assume that the add_aliasmem( S-1-5-32 ) + * handled it. + */ + status = pdb_get_aliasinfo(&global_sid_Builtin_Guests, info); + if (!NT_STATUS_IS_OK(status)) { + + become_root(); + status = create_builtin_guests(domain_sid); + unbecome_root(); + + /* + * NT_STATUS_PROTOCOL_UNREACHABLE: + * => winbindd is not running. + * + * NT_STATUS_ACCESS_DENIED: + * => no idmap config at all + * and wbint_AllocateGid()/winbind_allocate_gid() + * failed. + * + * NT_STATUS_NO_SUCH_GROUP: + * => no idmap config at all and + * "tdbsam:map builtin = no" means + * wbint_Sids2UnixIDs() fails. + */ + if (NT_STATUS_EQUAL(status, NT_STATUS_PROTOCOL_UNREACHABLE) || + NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED) || + NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_GROUP)) { + /* + * Add BUILTIN\Guests directly to token. + * But only if the token already indicates + * real guest access by: + * - local GUEST account + * - local GUESTS group + * - domain GUESTS group + * + * Even if a user was authenticated, it + * can be member of a guest related group. + */ + status = add_builtin_guests(result, domain_sid); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(3, ("Failed to check for local " + "Guests membership (%s)\n", + nt_errstr(status))); + /* + * This is a hard error. + */ + return status; + } + } else if (!NT_STATUS_IS_OK(status)) { + DEBUG(2, ("Failed to create " + "BUILTIN\\Guests group %s! Can " + "Winbind allocate gids?\n", + nt_errstr(status))); + /* + * This is a hard error. + */ + return status; + } + } + + TALLOC_FREE(info); + + /* Deal with local groups */ + + if (lp_winbind_nested_groups()) { + + become_root(); + + /* Now add the aliases. First the one from our local SAM */ + + status = add_aliases(get_global_sam_sid(), result); + + if (!NT_STATUS_IS_OK(status)) { + unbecome_root(); + return status; + } + + /* Finally the builtin ones */ + + status = add_aliases(&global_sid_Builtin, result); + + if (!NT_STATUS_IS_OK(status)) { + unbecome_root(); + return status; + } + + unbecome_root(); + } + + if (session_info_flags & AUTH_SESSION_INFO_NTLM) { + struct dom_sid tmp_sid = { 0, }; + + ok = dom_sid_parse(SID_NT_NTLM_AUTHENTICATION, &tmp_sid); + if (!ok) { + return NT_STATUS_NO_MEMORY; + } + + status = add_sid_to_array(result, + &tmp_sid, + &result->sids, + &result->num_sids); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + } + + if (session_info_flags & AUTH_SESSION_INFO_SIMPLE_PRIVILEGES) { + if (security_token_has_builtin_administrators(result)) { + result->privilege_mask = ~0; + } + } else { + /* Add privileges based on current user sids */ + get_privileges_for_sids(&result->privilege_mask, result->sids, + result->num_sids); + } + + return NT_STATUS_OK; +} + +/**************************************************************************** + prints a UNIX 'token' to debug output. +****************************************************************************/ + +void debug_unix_user_token(int dbg_class, int dbg_lev, uid_t uid, gid_t gid, + int n_groups, gid_t *groups) +{ + TALLOC_CTX *frame = talloc_stackframe(); + char *s = NULL; + int i; + + s = talloc_asprintf(frame, + "UNIX token of user %ld\n", + (long int)uid); + + talloc_asprintf_addbuf( + &s, + "Primary group is %ld and contains %i supplementary " + "groups\n", + (long int)gid, + n_groups); + for (i = 0; i < n_groups; i++) { + talloc_asprintf_addbuf(&s, + "Group[%3i]: %ld\n", + i, + (long int)groups[i]); + } + + DEBUGC(dbg_class, dbg_lev, ("%s", s ? s : "(NULL)")); + TALLOC_FREE(frame); +} + +/* + * Create an artificial NT token given just a domain SID. + * + * We have 3 cases: + * + * unmapped unix users: Go directly to nss to find the user's group. + * + * A passdb user: The list of groups is provided by pdb_enum_group_memberships. + * + * If the user is provided by winbind, the primary gid is set to "domain + * users" of the user's domain. For an explanation why this is necessary, see + * the thread starting at + * http://lists.samba.org/archive/samba-technical/2006-January/044803.html. + */ + +static NTSTATUS create_token_from_sid(TALLOC_CTX *mem_ctx, + const struct dom_sid *user_sid, + bool is_guest, + uid_t *uid, gid_t *gid, + char **found_username, + struct security_token **token) +{ + NTSTATUS result = NT_STATUS_NO_SUCH_USER; + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + gid_t *gids; + struct dom_sid *group_sids; + struct dom_sid tmp_sid; + uint32_t num_group_sids; + uint32_t num_gids; + uint32_t i; + uint32_t high, low; + bool range_ok; + struct dom_sid_buf buf; + + if (sid_check_is_in_our_sam(user_sid)) { + bool ret; + uint32_t pdb_num_group_sids; + /* This is a passdb user, so ask passdb */ + + struct samu *sam_acct = NULL; + + if ( !(sam_acct = samu_new( tmp_ctx )) ) { + result = NT_STATUS_NO_MEMORY; + goto done; + } + + become_root(); + ret = pdb_getsampwsid(sam_acct, user_sid); + unbecome_root(); + + if (!ret) { + DEBUG(1, ("pdb_getsampwsid(%s) failed\n", + dom_sid_str_buf(user_sid, &buf))); + DEBUGADD(1, ("Fall back to unix user\n")); + goto unix_user; + } + + result = pdb_enum_group_memberships(tmp_ctx, sam_acct, + &group_sids, &gids, + &pdb_num_group_sids); + if (!NT_STATUS_IS_OK(result)) { + DEBUG(1, ("enum_group_memberships failed for %s: " + "%s\n", + dom_sid_str_buf(user_sid, &buf), + nt_errstr(result))); + DEBUGADD(1, ("Fall back to unix uid lookup\n")); + goto unix_user; + } + num_group_sids = pdb_num_group_sids; + + /* see the smb_panic() in pdb_default_enum_group_memberships */ + SMB_ASSERT(num_group_sids > 0); + + /* Ensure we're returning the found_username on the right context. */ + *found_username = talloc_strdup(mem_ctx, + pdb_get_username(sam_acct)); + + if (*found_username == NULL) { + result = NT_STATUS_NO_MEMORY; + goto done; + } + + /* + * If the SID from lookup_name() was the guest sid, passdb knows + * about the mapping of guest sid to lp_guest_account() + * username and will return the unix_pw info for a guest + * user. Use it if it's there, else lookup the *uid details + * using Get_Pwnam_alloc(). See bug #6291 for details. JRA. + */ + + /* We must always assign the *uid. */ + if (sam_acct->unix_pw == NULL) { + struct passwd *pwd = Get_Pwnam_alloc(sam_acct, *found_username ); + if (!pwd) { + DEBUG(10, ("Get_Pwnam_alloc failed for %s\n", + *found_username)); + result = NT_STATUS_NO_SUCH_USER; + goto done; + } + result = samu_set_unix(sam_acct, pwd ); + if (!NT_STATUS_IS_OK(result)) { + DEBUG(10, ("samu_set_unix failed for %s\n", + *found_username)); + result = NT_STATUS_NO_SUCH_USER; + goto done; + } + } + *uid = sam_acct->unix_pw->pw_uid; + + } else if (sid_check_is_in_unix_users(user_sid)) { + uint32_t getgroups_num_group_sids; + /* This is a unix user not in passdb. We need to ask nss + * directly, without consulting passdb */ + + struct passwd *pass; + + /* + * This goto target is used as a fallback for the passdb + * case. The concrete bug report is when passdb gave us an + * unmapped gid. + */ + + unix_user: + + if (!sid_to_uid(user_sid, uid)) { + DEBUG(1, ("unix_user case, sid_to_uid for %s failed\n", + dom_sid_str_buf(user_sid, &buf))); + result = NT_STATUS_NO_SUCH_USER; + goto done; + } + + uid_to_unix_users_sid(*uid, &tmp_sid); + user_sid = &tmp_sid; + + pass = getpwuid_alloc(tmp_ctx, *uid); + if (pass == NULL) { + DEBUG(1, ("getpwuid(%u) failed\n", + (unsigned int)*uid)); + goto done; + } + + if (!getgroups_unix_user(tmp_ctx, pass->pw_name, pass->pw_gid, + &gids, &getgroups_num_group_sids)) { + DEBUG(1, ("getgroups_unix_user for user %s failed\n", + pass->pw_name)); + goto done; + } + num_group_sids = getgroups_num_group_sids; + + group_sids = talloc_array(tmp_ctx, struct dom_sid, num_group_sids); + if (group_sids == NULL) { + DEBUG(1, ("talloc_array failed\n")); + result = NT_STATUS_NO_MEMORY; + goto done; + } + + for (i=0; i 0); + + /* Ensure we're returning the found_username on the right context. */ + *found_username = talloc_strdup(mem_ctx, pass->pw_name); + if (*found_username == NULL) { + result = NT_STATUS_NO_MEMORY; + goto done; + } + } else { + + /* This user is from winbind, force the primary gid to the + * user's "domain users" group. Under certain circumstances + * (user comes from NT4), this might be a loss of + * information. But we can not rely on winbind getting the + * correct info. AD might prohibit winbind looking up that + * information. */ + + /* We must always assign the *uid. */ + if (!sid_to_uid(user_sid, uid)) { + DEBUG(1, ("winbindd case, sid_to_uid for %s failed\n", + dom_sid_str_buf(user_sid, &buf))); + result = NT_STATUS_NO_SUCH_USER; + goto done; + } + + num_group_sids = 1; + group_sids = talloc_array(tmp_ctx, struct dom_sid, num_group_sids); + if (group_sids == NULL) { + DEBUG(1, ("talloc_array failed\n")); + result = NT_STATUS_NO_MEMORY; + goto done; + } + + gids = talloc_array(tmp_ctx, gid_t, num_group_sids); + if (gids == NULL) { + result = NT_STATUS_NO_MEMORY; + goto done; + } + + sid_copy(&group_sids[0], user_sid); + sid_split_rid(&group_sids[0], NULL); + sid_append_rid(&group_sids[0], DOMAIN_RID_USERS); + + if (!sid_to_gid(&group_sids[0], &gids[0])) { + DEBUG(1, ("sid_to_gid(%s) failed\n", + dom_sid_str_buf(&group_sids[0], &buf))); + goto done; + } + + *found_username = NULL; + } + + *gid = gids[0]; + + /* Add the "Unix Group" SID for each gid to catch mapped groups + and their Unix equivalent. This is to solve the backwards + compatibility problem of 'valid users = +ntadmin' where + ntadmin has been paired with "Domain Admins" in the group + mapping table. Otherwise smb.conf would need to be changed + to 'valid user = "Domain Admins"'. --jerry */ + + num_gids = num_group_sids; + range_ok = lp_idmap_default_range(&low, &high); + for ( i=0; i= low) && (gids[i] <= high)) { + continue; + } + + gid_to_unix_groups_sid(gids[i], &unix_group_sid); + + result = add_sid_to_array_unique(tmp_ctx, &unix_group_sid, + &group_sids, &num_group_sids); + if (!NT_STATUS_IS_OK(result)) { + goto done; + } + } + + /* Ensure we're creating the nt_token on the right context. */ + result = create_local_nt_token(mem_ctx, user_sid, + is_guest, num_group_sids, group_sids, token); + + if (!NT_STATUS_IS_OK(result)) { + goto done; + } + + result = NT_STATUS_OK; + done: + TALLOC_FREE(tmp_ctx); + return result; +} + +/* + * Create an artificial NT token given just a username. (Initially intended + * for force user) + * + * We go through lookup_name() to avoid problems we had with 'winbind use + * default domain'. + * + * We have 3 cases: + * + * unmapped unix users: Go directly to nss to find the user's group. + * + * A passdb user: The list of groups is provided by pdb_enum_group_memberships. + * + * If the user is provided by winbind, the primary gid is set to "domain + * users" of the user's domain. For an explanation why this is necessary, see + * the thread starting at + * http://lists.samba.org/archive/samba-technical/2006-January/044803.html. + */ + +NTSTATUS create_token_from_username(TALLOC_CTX *mem_ctx, const char *username, + bool is_guest, + uid_t *uid, gid_t *gid, + char **found_username, + struct security_token **token) +{ + NTSTATUS result = NT_STATUS_NO_SUCH_USER; + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + struct dom_sid user_sid; + enum lsa_SidType type; + + if (!lookup_name_smbconf(tmp_ctx, username, LOOKUP_NAME_ALL, + NULL, NULL, &user_sid, &type)) { + DEBUG(1, ("lookup_name_smbconf for %s failed\n", username)); + goto done; + } + + if (type != SID_NAME_USER) { + DEBUG(1, ("%s is a %s, not a user\n", username, + sid_type_lookup(type))); + goto done; + } + + result = create_token_from_sid(mem_ctx, &user_sid, is_guest, uid, gid, found_username, token); + + if (!NT_STATUS_IS_OK(result)) { + goto done; + } + + /* + * If result == NT_STATUS_OK then + * we know we have a valid token. Ensure + * we also have a valid username to match. + */ + + if (*found_username == NULL) { + *found_username = talloc_strdup(mem_ctx, username); + if (*found_username == NULL) { + result = NT_STATUS_NO_MEMORY; + } + } + +done: + TALLOC_FREE(tmp_ctx); + return result; +} + +/*************************************************************************** + Build upon create_token_from_sid: + + Expensive helper function to figure out whether a user given its sid is + member of a particular group. +***************************************************************************/ + +bool user_sid_in_group_sid(const struct dom_sid *sid, const struct dom_sid *group_sid) +{ + NTSTATUS status; + uid_t uid; + gid_t gid; + char *found_username; + struct security_token *token; + bool result = false; + enum lsa_SidType type; + TALLOC_CTX *mem_ctx = talloc_stackframe(); + struct dom_sid_buf buf; + + if (!lookup_sid(mem_ctx, sid, + NULL, NULL, &type)) { + DEBUG(1, ("lookup_sid for %s failed\n", + dom_sid_str_buf(sid, &buf))); + goto done; + } + + if (type != SID_NAME_USER) { + DEBUG(5, ("%s is a %s, not a user\n", + dom_sid_str_buf(sid, &buf), + sid_type_lookup(type))); + goto done; + } + + status = create_token_from_sid(mem_ctx, sid, False, + &uid, &gid, &found_username, + &token); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10, ("could not create token for %s\n", + dom_sid_str_buf(sid, &buf))); + goto done; + } + + result = security_token_has_sid(token, group_sid); + +done: + TALLOC_FREE(mem_ctx); + return result; +} + +/*************************************************************************** + Build upon create_token_from_username: + + Expensive helper function to figure out whether a user given its name is + member of a particular group. +***************************************************************************/ + +bool user_in_group_sid(const char *username, const struct dom_sid *group_sid) +{ + NTSTATUS status; + uid_t uid; + gid_t gid; + char *found_username; + struct security_token *token; + bool result; + TALLOC_CTX *mem_ctx = talloc_stackframe(); + + status = create_token_from_username(mem_ctx, username, False, + &uid, &gid, &found_username, + &token); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10, ("could not create token for %s\n", username)); + TALLOC_FREE(mem_ctx); + return False; + } + + result = security_token_has_sid(token, group_sid); + + TALLOC_FREE(mem_ctx); + return result; +} + +bool user_in_group(const char *username, const char *groupname) +{ + TALLOC_CTX *mem_ctx = talloc_stackframe(); + struct dom_sid group_sid; + bool ret; + + ret = lookup_name(mem_ctx, groupname, LOOKUP_NAME_ALL, + NULL, NULL, &group_sid, NULL); + TALLOC_FREE(mem_ctx); + + if (!ret) { + DEBUG(10, ("lookup_name for (%s) failed.\n", groupname)); + return False; + } + + return user_in_group_sid(username, &group_sid); +} + +/* END */ -- cgit v1.2.3