📄 srv_samr_nt.c
字号:
/* * Unix SMB/CIFS implementation. * RPC Pipe client / server routines * Copyright (C) Andrew Tridgell 1992-1997, * Copyright (C) Luke Kenneth Casson Leighton 1996-1997, * Copyright (C) Paul Ashton 1997, * Copyright (C) Marc Jacobsen 1999, * Copyright (C) Jeremy Allison 2001-2005, * Copyright (C) Jean François Micouleau 1998-2001, * Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002, * Copyright (C) Gerald (Jerry) Carter 2003-2004, * Copyright (C) Simo Sorce 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 2 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, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *//* * This is the implementation of the SAMR code. */#include "includes.h"#undef DBGC_CLASS#define DBGC_CLASS DBGC_RPC_SRV#define SAMR_USR_RIGHTS_WRITE_PW \ ( READ_CONTROL_ACCESS | \ SA_RIGHT_USER_CHANGE_PASSWORD | \ SA_RIGHT_USER_SET_LOC_COM )#define DISP_INFO_CACHE_TIMEOUT 10typedef struct disp_info { struct disp_info *next, *prev; TALLOC_CTX *mem_ctx; DOM_SID sid; /* identify which domain this is. */ BOOL builtin_domain; /* Quick flag to check if this is the builtin domain. */ struct pdb_search *users; /* querydispinfo 1 and 4 */ struct pdb_search *machines; /* querydispinfo 2 */ struct pdb_search *groups; /* querydispinfo 3 and 5, enumgroups */ struct pdb_search *aliases; /* enumaliases */ uint16 enum_acb_mask; struct pdb_search *enum_users; /* enumusers with a mask */ smb_event_id_t di_cache_timeout_event; /* cache idle timeout handler. */} DISP_INFO;/* We keep a static list of these by SID as modern clients close down all resources between each request in a complete enumeration. */static DISP_INFO *disp_info_list;struct samr_info { /* for use by the \PIPE\samr policy */ DOM_SID sid; BOOL builtin_domain; /* Quick flag to check if this is the builtin domain. */ uint32 status; /* some sort of flag. best to record it. comes from opnum 0x39 */ uint32 acc_granted; DISP_INFO *disp_info; TALLOC_CTX *mem_ctx;};static struct generic_mapping sam_generic_mapping = { GENERIC_RIGHTS_SAM_READ, GENERIC_RIGHTS_SAM_WRITE, GENERIC_RIGHTS_SAM_EXECUTE, GENERIC_RIGHTS_SAM_ALL_ACCESS};static struct generic_mapping dom_generic_mapping = { GENERIC_RIGHTS_DOMAIN_READ, GENERIC_RIGHTS_DOMAIN_WRITE, GENERIC_RIGHTS_DOMAIN_EXECUTE, GENERIC_RIGHTS_DOMAIN_ALL_ACCESS};static struct generic_mapping usr_generic_mapping = { GENERIC_RIGHTS_USER_READ, GENERIC_RIGHTS_USER_WRITE, GENERIC_RIGHTS_USER_EXECUTE, GENERIC_RIGHTS_USER_ALL_ACCESS};static struct generic_mapping grp_generic_mapping = { GENERIC_RIGHTS_GROUP_READ, GENERIC_RIGHTS_GROUP_WRITE, GENERIC_RIGHTS_GROUP_EXECUTE, GENERIC_RIGHTS_GROUP_ALL_ACCESS};static struct generic_mapping ali_generic_mapping = { GENERIC_RIGHTS_ALIAS_READ, GENERIC_RIGHTS_ALIAS_WRITE, GENERIC_RIGHTS_ALIAS_EXECUTE, GENERIC_RIGHTS_ALIAS_ALL_ACCESS};/**************************************************************************************************************************************/static NTSTATUS make_samr_object_sd( TALLOC_CTX *ctx, SEC_DESC **psd, size_t *sd_size, struct generic_mapping *map, DOM_SID *sid, uint32 sid_access ){ DOM_SID domadmin_sid; SEC_ACE ace[5]; /* at most 5 entries */ SEC_ACCESS mask; size_t i = 0; SEC_ACL *psa = NULL; /* basic access for Everyone */ init_sec_access(&mask, map->generic_execute | map->generic_read ); init_sec_ace(&ace[i++], &global_sid_World, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0); /* add Full Access 'BUILTIN\Administrators' and 'BUILTIN\Account Operators */ init_sec_access(&mask, map->generic_all); init_sec_ace(&ace[i++], &global_sid_Builtin_Administrators, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0); init_sec_ace(&ace[i++], &global_sid_Builtin_Account_Operators, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0); /* Add Full Access for Domain Admins if we are a DC */ if ( IS_DC ) { sid_copy( &domadmin_sid, get_global_sam_sid() ); sid_append_rid( &domadmin_sid, DOMAIN_GROUP_RID_ADMINS ); init_sec_ace(&ace[i++], &domadmin_sid, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0); } /* if we have a sid, give it some special access */ if ( sid ) { init_sec_access( &mask, sid_access ); init_sec_ace(&ace[i++], sid, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);} /* create the security descriptor */ if ((psa = make_sec_acl(ctx, NT4_ACL_REVISION, i, ace)) == NULL) return NT_STATUS_NO_MEMORY; if ((*psd = make_sec_desc(ctx, SEC_DESC_REVISION, SEC_DESC_SELF_RELATIVE, NULL, NULL, NULL, psa, sd_size)) == NULL) return NT_STATUS_NO_MEMORY; return NT_STATUS_OK;}/******************************************************************* Checks if access to an object should be granted, and returns that level of access for further checks.********************************************************************/static NTSTATUS access_check_samr_object( SEC_DESC *psd, NT_USER_TOKEN *token, SE_PRIV *rights, uint32 rights_mask, uint32 des_access, uint32 *acc_granted, const char *debug ){ NTSTATUS status = NT_STATUS_ACCESS_DENIED; uint32 saved_mask = 0; /* check privileges; certain SAM access bits should be overridden by privileges (mostly having to do with creating/modifying/deleting users and groups) */ if ( rights && user_has_any_privilege( token, rights ) ) { saved_mask = (des_access & rights_mask); des_access &= ~saved_mask; DEBUG(4,("access_check_samr_object: user rights access mask [0x%x]\n", rights_mask)); } /* check the security descriptor first */ if ( se_access_check(psd, token, des_access, acc_granted, &status) ) goto done; /* give root a free pass */ if ( geteuid() == sec_initial_uid() ) { DEBUG(4,("%s: ACCESS should be DENIED (requested: %#010x)\n", debug, des_access)); DEBUGADD(4,("but overritten by euid == sec_initial_uid()\n")); *acc_granted = des_access; status = NT_STATUS_OK; goto done; } done: /* add in any bits saved during the privilege check (only matters is status is ok) */ *acc_granted |= rights_mask; DEBUG(4,("%s: access %s (requested: 0x%08x, granted: 0x%08x)\n", debug, NT_STATUS_IS_OK(status) ? "GRANTED" : "DENIED", des_access, *acc_granted)); return status;}/******************************************************************* Checks if access to a function can be granted********************************************************************/static NTSTATUS access_check_samr_function(uint32 acc_granted, uint32 acc_required, const char *debug){ DEBUG(5,("%s: access check ((granted: %#010x; required: %#010x)\n", debug, acc_granted, acc_required)); /* check the security descriptor first */ if ( (acc_granted&acc_required) == acc_required ) return NT_STATUS_OK; /* give root a free pass */ if (geteuid() == sec_initial_uid()) { DEBUG(4,("%s: ACCESS should be DENIED (granted: %#010x; required: %#010x)\n", debug, acc_granted, acc_required)); DEBUGADD(4,("but overwritten by euid == 0\n")); return NT_STATUS_OK; } DEBUG(2,("%s: ACCESS DENIED (granted: %#010x; required: %#010x)\n", debug, acc_granted, acc_required)); return NT_STATUS_ACCESS_DENIED;}/******************************************************************* Fetch or create a dispinfo struct.********************************************************************/static DISP_INFO *get_samr_dispinfo_by_sid(DOM_SID *psid, const char *sid_str){ TALLOC_CTX *mem_ctx; DISP_INFO *dpi; /* There are two cases to consider here: 1) The SID is a domain SID and we look for an equality match, or 2) This is an account SID and so we return the DISP_INFO* for our domain */ if ( psid && sid_check_is_in_our_domain( psid ) ) { DEBUG(10,("get_samr_dispinfo_by_sid: Replacing %s with our domain SID\n", sid_str)); psid = get_global_sam_sid(); } for (dpi = disp_info_list; dpi; dpi = dpi->next) { if (sid_equal(psid, &dpi->sid)) { return dpi; } } /* This struct is never free'd - I'm using talloc so we can get a list out of smbd using smbcontrol. There will be one of these per SID we're authorative for. JRA. */ mem_ctx = talloc_init("DISP_INFO for domain sid %s", sid_str); if ((dpi = TALLOC_ZERO_P(mem_ctx, DISP_INFO)) == NULL) return NULL; dpi->mem_ctx = mem_ctx; if (psid) { sid_copy( &dpi->sid, psid); dpi->builtin_domain = sid_check_is_builtin(psid); } else { dpi->builtin_domain = False; } DLIST_ADD(disp_info_list, dpi); return dpi;}/******************************************************************* Create a samr_info struct.********************************************************************/static struct samr_info *get_samr_info_by_sid(DOM_SID *psid){ struct samr_info *info; fstring sid_str; TALLOC_CTX *mem_ctx; if (psid) { sid_to_string(sid_str, psid); } else { fstrcpy(sid_str,"(NULL)"); } mem_ctx = talloc_init("samr_info for domain sid %s", sid_str); if ((info = TALLOC_ZERO_P(mem_ctx, struct samr_info)) == NULL) return NULL; DEBUG(10,("get_samr_info_by_sid: created new info for sid %s\n", sid_str)); if (psid) { sid_copy( &info->sid, psid); info->builtin_domain = sid_check_is_builtin(psid); } else { DEBUG(10,("get_samr_info_by_sid: created new info for NULL sid.\n")); info->builtin_domain = False; } info->mem_ctx = mem_ctx; info->disp_info = get_samr_dispinfo_by_sid(psid, sid_str); if (!info->disp_info) { talloc_destroy(mem_ctx); return NULL; } return info;}/******************************************************************* Function to free the per SID data. ********************************************************************/static void free_samr_cache(DISP_INFO *disp_info, const char *sid_str){ DEBUG(10,("free_samr_cache: deleting cache for SID %s\n", sid_str)); /* We need to become root here because the paged search might have to * tell the LDAP server we're not interested in the rest anymore. */ become_root(); if (disp_info->users) { DEBUG(10,("free_samr_cache: deleting users cache\n")); pdb_search_destroy(disp_info->users); disp_info->users = NULL; } if (disp_info->machines) { DEBUG(10,("free_samr_cache: deleting machines cache\n")); pdb_search_destroy(disp_info->machines); disp_info->machines = NULL; } if (disp_info->groups) { DEBUG(10,("free_samr_cache: deleting groups cache\n")); pdb_search_destroy(disp_info->groups); disp_info->groups = NULL; } if (disp_info->aliases) { DEBUG(10,("free_samr_cache: deleting aliases cache\n")); pdb_search_destroy(disp_info->aliases); disp_info->aliases = NULL; } if (disp_info->enum_users) { DEBUG(10,("free_samr_cache: deleting enum_users cache\n")); pdb_search_destroy(disp_info->enum_users); disp_info->enum_users = NULL; } disp_info->enum_acb_mask = 0; unbecome_root();}/******************************************************************* Function to free the per handle data. ********************************************************************/static void free_samr_info(void *ptr){ struct samr_info *info=(struct samr_info *) ptr; /* Only free the dispinfo cache if no one bothered to set up a timeout. */ if (info->disp_info && info->disp_info->di_cache_timeout_event == (smb_event_id_t)0) { fstring sid_str; sid_to_string(sid_str, &info->disp_info->sid); free_samr_cache(info->disp_info, sid_str); } talloc_destroy(info->mem_ctx);}/******************************************************************* Idle event handler. Throw away the disp info cache. ********************************************************************/static void disp_info_cache_idle_timeout_handler(void **private_data, time_t *ev_interval, time_t ev_now){ fstring sid_str; DISP_INFO *disp_info = (DISP_INFO *)(*private_data); sid_to_string(sid_str, &disp_info->sid); free_samr_cache(disp_info, sid_str); /* Remove the event. */ smb_unregister_idle_event(disp_info->di_cache_timeout_event); disp_info->di_cache_timeout_event = (smb_event_id_t)0; DEBUG(10,("disp_info_cache_idle_timeout_handler: caching timed out for SID %s at %u\n", sid_str, (unsigned int)ev_now));}/******************************************************************* Setup cache removal idle event handler. ********************************************************************/static void set_disp_info_cache_timeout(DISP_INFO *disp_info, time_t secs_fromnow){ fstring sid_str; sid_to_string(sid_str, &disp_info->sid); /* Remove any pending timeout and update. */ if (disp_info->di_cache_timeout_event) { smb_unregister_idle_event(disp_info->di_cache_timeout_event); disp_info->di_cache_timeout_event = (smb_event_id_t)0; } DEBUG(10,("set_disp_info_cache_timeout: caching enumeration for SID %s for %u seconds\n", sid_str, (unsigned int)secs_fromnow )); disp_info->di_cache_timeout_event = smb_register_idle_event(disp_info_cache_idle_timeout_handler, disp_info, secs_fromnow);}/******************************************************************* Force flush any cache. We do this on any samr_set_xxx call. We must also remove the timeout handler. ********************************************************************/static void force_flush_samr_cache(DISP_INFO *disp_info){ if (disp_info) { fstring sid_str; sid_to_string(sid_str, &disp_info->sid); if (disp_info->di_cache_timeout_event) { smb_unregister_idle_event(disp_info->di_cache_timeout_event); disp_info->di_cache_timeout_event = (smb_event_id_t)0; DEBUG(10,("force_flush_samr_cache: clearing idle event for SID %s\n",
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -