2004-11-18 06:31:35 +03:00
/*
Unix SMB / CIFS implementation .
POSIX NTVFS backend - ACL support
Copyright ( C ) Andrew Tridgell 2004
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
2007-07-10 06:07:03 +04:00
the Free Software Foundation ; either version 3 of the License , or
2004-11-18 06:31:35 +03:00
( 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
2007-07-10 06:07:03 +04:00
along with this program . If not , see < http : //www.gnu.org/licenses/>.
2004-11-18 06:31:35 +03:00
*/
# include "includes.h"
# include "auth/auth.h"
# include "vfs_posix.h"
2006-03-16 03:23:11 +03:00
# include "librpc/gen_ndr/xattr.h"
2006-04-02 16:02:01 +04:00
# include "libcli/security/security.h"
2008-06-28 12:28:15 +04:00
# include "param/param.h"
2004-11-18 06:31:35 +03:00
2006-10-09 15:13:49 +04:00
/* the list of currently registered ACL backends */
static struct pvfs_acl_backend {
const struct pvfs_acl_ops * ops ;
} * backends = NULL ;
static int num_backends ;
/*
register a pvfs acl backend .
The ' name ' can be later used by other backends to find the operations
structure for this backend .
*/
2008-04-02 06:53:27 +04:00
NTSTATUS pvfs_acl_register ( const struct pvfs_acl_ops * ops )
2006-10-09 15:13:49 +04:00
{
struct pvfs_acl_ops * new_ops ;
if ( pvfs_acl_backend_byname ( ops - > name ) ! = NULL ) {
DEBUG ( 0 , ( " pvfs acl backend '%s' already registered \n " , ops - > name ) ) ;
return NT_STATUS_OBJECT_NAME_COLLISION ;
}
backends = talloc_realloc ( talloc_autofree_context ( ) , backends , struct pvfs_acl_backend , num_backends + 1 ) ;
NT_STATUS_HAVE_NO_MEMORY ( backends ) ;
2009-02-04 10:50:46 +03:00
new_ops = ( struct pvfs_acl_ops * ) talloc_memdup ( backends , ops , sizeof ( * ops ) ) ;
2006-10-09 15:13:49 +04:00
new_ops - > name = talloc_strdup ( new_ops , ops - > name ) ;
backends [ num_backends ] . ops = new_ops ;
num_backends + + ;
DEBUG ( 3 , ( " NTVFS backend '%s' registered \n " , ops - > name ) ) ;
return NT_STATUS_OK ;
}
/*
return the operations structure for a named backend
*/
2008-04-02 06:53:27 +04:00
const struct pvfs_acl_ops * pvfs_acl_backend_byname ( const char * name )
2006-10-09 15:13:49 +04:00
{
int i ;
for ( i = 0 ; i < num_backends ; i + + ) {
if ( strcmp ( backends [ i ] . ops - > name , name ) = = 0 ) {
return backends [ i ] . ops ;
}
}
return NULL ;
}
2008-06-28 12:28:15 +04:00
NTSTATUS pvfs_acl_init ( struct loadparm_context * lp_ctx )
{
static bool initialized = false ;
extern NTSTATUS pvfs_acl_nfs4_init ( void ) ;
extern NTSTATUS pvfs_acl_xattr_init ( void ) ;
init_module_fn static_init [ ] = { STATIC_pvfs_acl_MODULES } ;
init_module_fn * shared_init ;
if ( initialized ) return NT_STATUS_OK ;
initialized = true ;
shared_init = load_samba_modules ( NULL , lp_ctx , " pvfs_acl " ) ;
run_init_functions ( static_init ) ;
run_init_functions ( shared_init ) ;
talloc_free ( shared_init ) ;
return NT_STATUS_OK ;
}
2006-10-09 15:13:49 +04:00
2004-12-03 16:04:10 +03:00
/*
map a single access_mask from generic to specific bits for files / dirs
*/
static uint32_t pvfs_translate_mask ( uint32_t access_mask )
{
if ( access_mask & SEC_MASK_GENERIC ) {
if ( access_mask & SEC_GENERIC_READ ) access_mask | = SEC_RIGHTS_FILE_READ ;
if ( access_mask & SEC_GENERIC_WRITE ) access_mask | = SEC_RIGHTS_FILE_WRITE ;
if ( access_mask & SEC_GENERIC_EXECUTE ) access_mask | = SEC_RIGHTS_FILE_EXECUTE ;
if ( access_mask & SEC_GENERIC_ALL ) access_mask | = SEC_RIGHTS_FILE_ALL ;
access_mask & = ~ SEC_MASK_GENERIC ;
}
return access_mask ;
}
/*
map any generic access bits in the given acl
this relies on the fact that the mappings for files and directories
are the same
*/
static void pvfs_translate_generic_bits ( struct security_acl * acl )
{
unsigned i ;
2006-04-13 15:34:39 +04:00
if ( ! acl ) return ;
2004-12-03 16:04:10 +03:00
for ( i = 0 ; i < acl - > num_aces ; i + + ) {
struct security_ace * ace = & acl - > aces [ i ] ;
ace - > access_mask = pvfs_translate_mask ( ace - > access_mask ) ;
}
}
2004-11-18 06:31:35 +03:00
/*
setup a default ACL for a file
*/
static NTSTATUS pvfs_default_acl ( struct pvfs_state * pvfs ,
2006-03-10 17:31:17 +03:00
struct ntvfs_request * req ,
2004-11-18 06:31:35 +03:00
struct pvfs_filename * name , int fd ,
2006-10-09 15:13:49 +04:00
struct security_descriptor * * psd )
2004-11-18 06:31:35 +03:00
{
struct security_descriptor * sd ;
2004-11-29 06:22:44 +03:00
NTSTATUS status ;
2004-12-29 15:41:27 +03:00
struct security_ace ace ;
2004-11-29 06:22:44 +03:00
mode_t mode ;
2008-03-29 01:29:01 +03:00
struct id_mapping * ids ;
struct composite_context * ctx ;
2004-11-18 06:31:35 +03:00
2006-10-09 15:13:49 +04:00
* psd = security_descriptor_initialise ( req ) ;
if ( * psd = = NULL ) {
2004-11-18 06:31:35 +03:00
return NT_STATUS_NO_MEMORY ;
}
2006-10-09 15:13:49 +04:00
sd = * psd ;
2004-11-18 06:31:35 +03:00
2008-04-14 20:59:40 +04:00
ids = talloc_zero_array ( sd , struct id_mapping , 2 ) ;
2008-03-29 01:29:01 +03:00
NT_STATUS_HAVE_NO_MEMORY ( ids ) ;
ids [ 0 ] . unixid = talloc ( ids , struct unixid ) ;
NT_STATUS_HAVE_NO_MEMORY ( ids [ 0 ] . unixid ) ;
ids [ 0 ] . unixid - > id = name - > st . st_uid ;
ids [ 0 ] . unixid - > type = ID_TYPE_UID ;
ids [ 0 ] . sid = NULL ;
2004-11-29 06:22:44 +03:00
2008-03-29 01:29:01 +03:00
ids [ 1 ] . unixid = talloc ( ids , struct unixid ) ;
NT_STATUS_HAVE_NO_MEMORY ( ids [ 1 ] . unixid ) ;
ids [ 1 ] . unixid - > id = name - > st . st_gid ;
ids [ 1 ] . unixid - > type = ID_TYPE_GID ;
ids [ 1 ] . sid = NULL ;
ctx = wbc_xids_to_sids_send ( pvfs - > wbc_ctx , ids , 2 , ids ) ;
NT_STATUS_HAVE_NO_MEMORY ( ctx ) ;
status = wbc_xids_to_sids_recv ( ctx , & ids ) ;
NT_STATUS_NOT_OK_RETURN ( status ) ;
sd - > owner_sid = talloc_steal ( sd , ids [ 0 ] . sid ) ;
sd - > group_sid = talloc_steal ( sd , ids [ 1 ] . sid ) ;
talloc_free ( ids ) ;
2004-11-18 07:19:26 +03:00
sd - > type | = SEC_DESC_DACL_PRESENT ;
2004-11-18 06:31:35 +03:00
2004-12-29 15:41:27 +03:00
mode = name - > st . st_mode ;
2004-11-29 06:22:44 +03:00
/*
2004-12-29 15:41:27 +03:00
we provide up to 4 ACEs
2004-11-29 06:22:44 +03:00
- Owner
- Group
- Everyone
2004-12-29 15:41:27 +03:00
- Administrator
2004-11-29 06:22:44 +03:00
*/
2004-12-29 15:41:27 +03:00
/* setup owner ACE */
ace . type = SEC_ACE_TYPE_ACCESS_ALLOWED ;
ace . flags = 0 ;
ace . trustee = * sd - > owner_sid ;
ace . access_mask = 0 ;
2004-11-29 06:22:44 +03:00
if ( mode & S_IRUSR ) {
2004-12-30 05:25:20 +03:00
if ( mode & S_IWUSR ) {
ace . access_mask | = SEC_RIGHTS_FILE_ALL ;
} else {
ace . access_mask | = SEC_RIGHTS_FILE_READ | SEC_FILE_EXECUTE ;
}
2004-11-29 06:22:44 +03:00
}
if ( mode & S_IWUSR ) {
2004-12-29 15:41:27 +03:00
ace . access_mask | = SEC_RIGHTS_FILE_WRITE | SEC_STD_DELETE ;
}
if ( ace . access_mask ) {
security_descriptor_dacl_add ( sd , & ace ) ;
2004-11-29 06:22:44 +03:00
}
2004-12-29 15:41:27 +03:00
/* setup group ACE */
ace . trustee = * sd - > group_sid ;
ace . access_mask = 0 ;
2004-11-29 06:22:44 +03:00
if ( mode & S_IRGRP ) {
2004-12-29 15:41:27 +03:00
ace . access_mask | = SEC_RIGHTS_FILE_READ | SEC_FILE_EXECUTE ;
2004-11-29 06:22:44 +03:00
}
if ( mode & S_IWGRP ) {
2004-12-30 05:25:20 +03:00
/* note that delete is not granted - this matches posix behaviour */
2004-12-29 15:41:27 +03:00
ace . access_mask | = SEC_RIGHTS_FILE_WRITE ;
}
if ( ace . access_mask ) {
security_descriptor_dacl_add ( sd , & ace ) ;
2004-11-29 06:22:44 +03:00
}
2004-11-18 06:31:35 +03:00
2004-12-29 15:41:27 +03:00
/* setup other ACE */
ace . trustee = * dom_sid_parse_talloc ( req , SID_WORLD ) ;
ace . access_mask = 0 ;
2004-11-29 06:22:44 +03:00
if ( mode & S_IROTH ) {
2004-12-29 15:41:27 +03:00
ace . access_mask | = SEC_RIGHTS_FILE_READ | SEC_FILE_EXECUTE ;
2004-11-29 06:22:44 +03:00
}
if ( mode & S_IWOTH ) {
2004-12-29 15:41:27 +03:00
ace . access_mask | = SEC_RIGHTS_FILE_WRITE ;
2004-11-29 06:22:44 +03:00
}
2004-12-29 15:41:27 +03:00
if ( ace . access_mask ) {
security_descriptor_dacl_add ( sd , & ace ) ;
2004-11-18 06:31:35 +03:00
}
2004-12-29 15:41:27 +03:00
/* setup system ACE */
ace . trustee = * dom_sid_parse_talloc ( req , SID_NT_SYSTEM ) ;
ace . access_mask = SEC_RIGHTS_FILE_ALL ;
security_descriptor_dacl_add ( sd , & ace ) ;
2004-11-18 06:31:35 +03:00
return NT_STATUS_OK ;
}
/*
omit any security_descriptor elements not specified in the given
secinfo flags
*/
static void normalise_sd_flags ( struct security_descriptor * sd , uint32_t secinfo_flags )
{
2004-11-30 07:33:27 +03:00
if ( ! ( secinfo_flags & SECINFO_OWNER ) ) {
2004-11-18 06:31:35 +03:00
sd - > owner_sid = NULL ;
}
2004-11-30 07:33:27 +03:00
if ( ! ( secinfo_flags & SECINFO_GROUP ) ) {
2004-11-18 06:31:35 +03:00
sd - > group_sid = NULL ;
}
2004-11-30 07:33:27 +03:00
if ( ! ( secinfo_flags & SECINFO_DACL ) ) {
2004-11-18 06:31:35 +03:00
sd - > dacl = NULL ;
}
2004-11-30 07:33:27 +03:00
if ( ! ( secinfo_flags & SECINFO_SACL ) ) {
2004-11-18 06:31:35 +03:00
sd - > sacl = NULL ;
}
}
/*
answer a setfileinfo for an ACL
*/
NTSTATUS pvfs_acl_set ( struct pvfs_state * pvfs ,
2006-03-10 17:31:17 +03:00
struct ntvfs_request * req ,
2004-11-18 06:31:35 +03:00
struct pvfs_filename * name , int fd ,
2004-12-31 11:56:32 +03:00
uint32_t access_mask ,
2004-11-18 06:31:35 +03:00
union smb_setfileinfo * info )
{
uint32_t secinfo_flags = info - > set_secdesc . in . secinfo_flags ;
2005-01-03 10:57:05 +03:00
struct security_descriptor * new_sd , * sd , orig_sd ;
2006-10-09 15:13:49 +04:00
NTSTATUS status = NT_STATUS_NOT_FOUND ;
2006-04-18 12:33:48 +04:00
uid_t old_uid = - 1 ;
gid_t old_gid = - 1 ;
uid_t new_uid = - 1 ;
gid_t new_gid = - 1 ;
2008-03-29 01:29:01 +03:00
struct id_mapping * ids ;
struct composite_context * ctx ;
2004-11-18 06:31:35 +03:00
2006-10-09 15:13:49 +04:00
if ( pvfs - > acl_ops ! = NULL ) {
status = pvfs - > acl_ops - > acl_load ( pvfs , name , fd , req , & sd ) ;
2004-11-18 06:31:35 +03:00
}
if ( NT_STATUS_EQUAL ( status , NT_STATUS_NOT_FOUND ) ) {
2006-10-09 15:13:49 +04:00
status = pvfs_default_acl ( pvfs , req , name , fd , & sd ) ;
2004-11-18 06:31:35 +03:00
}
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
2008-03-29 01:29:01 +03:00
ids = talloc ( req , struct id_mapping ) ;
NT_STATUS_HAVE_NO_MEMORY ( ids ) ;
ids - > unixid = NULL ;
ids - > sid = NULL ;
ids - > status = NT_STATUS_NONE_MAPPED ;
2004-11-18 06:31:35 +03:00
new_sd = info - > set_secdesc . in . sd ;
2005-01-03 10:57:05 +03:00
orig_sd = * sd ;
2004-11-18 06:31:35 +03:00
2006-04-18 12:33:48 +04:00
old_uid = name - > st . st_uid ;
old_gid = name - > st . st_gid ;
2004-12-31 11:56:32 +03:00
2004-11-18 06:31:35 +03:00
/* only set the elements that have been specified */
2006-04-18 12:33:48 +04:00
if ( secinfo_flags & SECINFO_OWNER ) {
2004-12-31 11:56:32 +03:00
if ( ! ( access_mask & SEC_STD_WRITE_OWNER ) ) {
return NT_STATUS_ACCESS_DENIED ;
}
2006-04-18 12:33:48 +04:00
if ( ! dom_sid_equal ( sd - > owner_sid , new_sd - > owner_sid ) ) {
2008-03-29 01:29:01 +03:00
ids - > sid = new_sd - > owner_sid ;
ctx = wbc_sids_to_xids_send ( pvfs - > wbc_ctx , ids , 1 , ids ) ;
NT_STATUS_HAVE_NO_MEMORY ( ctx ) ;
status = wbc_sids_to_xids_recv ( ctx , & ids ) ;
2006-04-18 12:33:48 +04:00
NT_STATUS_NOT_OK_RETURN ( status ) ;
2008-03-29 01:29:01 +03:00
if ( ids - > unixid - > type = = ID_TYPE_BOTH | |
ids - > unixid - > type = = ID_TYPE_UID ) {
new_uid = ids - > unixid - > id ;
}
2004-12-31 11:56:32 +03:00
}
2006-04-18 12:33:48 +04:00
sd - > owner_sid = new_sd - > owner_sid ;
2004-11-18 06:31:35 +03:00
}
2006-04-18 12:33:48 +04:00
if ( secinfo_flags & SECINFO_GROUP ) {
2006-04-13 15:34:39 +04:00
if ( ! ( access_mask & SEC_STD_WRITE_OWNER ) ) {
return NT_STATUS_ACCESS_DENIED ;
}
2006-04-18 12:33:48 +04:00
if ( ! dom_sid_equal ( sd - > group_sid , new_sd - > group_sid ) ) {
2008-03-29 01:29:01 +03:00
ids - > sid = new_sd - > group_sid ;
ctx = wbc_sids_to_xids_send ( pvfs - > wbc_ctx , ids , 1 , ids ) ;
NT_STATUS_HAVE_NO_MEMORY ( ctx ) ;
status = wbc_sids_to_xids_recv ( ctx , & ids ) ;
2006-04-18 12:33:48 +04:00
NT_STATUS_NOT_OK_RETURN ( status ) ;
2008-03-29 01:29:01 +03:00
if ( ids - > unixid - > type = = ID_TYPE_BOTH | |
ids - > unixid - > type = = ID_TYPE_GID ) {
new_gid = ids - > unixid - > id ;
}
2004-12-31 11:56:32 +03:00
}
2006-04-18 12:33:48 +04:00
sd - > group_sid = new_sd - > group_sid ;
2004-11-18 06:31:35 +03:00
}
2004-11-30 07:33:27 +03:00
if ( secinfo_flags & SECINFO_DACL ) {
2006-04-13 15:34:39 +04:00
if ( ! ( access_mask & SEC_STD_WRITE_DAC ) ) {
return NT_STATUS_ACCESS_DENIED ;
}
2004-11-18 06:31:35 +03:00
sd - > dacl = new_sd - > dacl ;
2004-12-03 16:04:10 +03:00
pvfs_translate_generic_bits ( sd - > dacl ) ;
2004-11-18 06:31:35 +03:00
}
2004-11-30 07:33:27 +03:00
if ( secinfo_flags & SECINFO_SACL ) {
2004-12-31 11:56:32 +03:00
if ( ! ( access_mask & SEC_FLAG_SYSTEM_SECURITY ) ) {
return NT_STATUS_ACCESS_DENIED ;
}
2006-04-13 15:34:39 +04:00
sd - > sacl = new_sd - > sacl ;
2004-12-03 16:04:10 +03:00
pvfs_translate_generic_bits ( sd - > sacl ) ;
2004-11-18 06:31:35 +03:00
}
2006-04-18 12:33:48 +04:00
if ( new_uid = = old_uid ) {
new_uid = - 1 ;
}
if ( new_gid = = old_gid ) {
new_gid = - 1 ;
}
/* if there's something to change try it */
if ( new_uid ! = - 1 | | new_gid ! = - 1 ) {
2004-12-31 11:56:32 +03:00
int ret ;
if ( fd = = - 1 ) {
2006-04-18 12:33:48 +04:00
ret = chown ( name - > full_name , new_uid , new_gid ) ;
2004-12-31 11:56:32 +03:00
} else {
2006-04-18 12:33:48 +04:00
ret = fchown ( fd , new_uid , new_gid ) ;
2004-12-31 11:56:32 +03:00
}
2009-10-17 05:58:17 +04:00
if ( errno = = EPERM & & uwrap_enabled ( ) ) {
ret = 0 ;
}
2004-12-31 11:56:32 +03:00
if ( ret = = - 1 ) {
return pvfs_map_errno ( pvfs , errno ) ;
}
}
2005-01-03 10:57:05 +03:00
/* we avoid saving if the sd is the same. This means when clients
copy files and end up copying the default sd that we don ' t
needlessly use xattrs */
2006-10-09 15:13:49 +04:00
if ( ! security_descriptor_equal ( sd , & orig_sd ) & & pvfs - > acl_ops ) {
status = pvfs - > acl_ops - > acl_save ( pvfs , name , fd , sd ) ;
2005-01-03 10:57:05 +03:00
}
2004-11-18 06:31:35 +03:00
return status ;
}
/*
answer a fileinfo query for the ACL
*/
NTSTATUS pvfs_acl_query ( struct pvfs_state * pvfs ,
2006-03-10 17:31:17 +03:00
struct ntvfs_request * req ,
2004-11-18 06:31:35 +03:00
struct pvfs_filename * name , int fd ,
union smb_fileinfo * info )
{
2006-10-09 15:13:49 +04:00
NTSTATUS status = NT_STATUS_NOT_FOUND ;
2004-11-18 06:31:35 +03:00
struct security_descriptor * sd ;
2006-10-09 15:13:49 +04:00
if ( pvfs - > acl_ops ) {
status = pvfs - > acl_ops - > acl_load ( pvfs , name , fd , req , & sd ) ;
2004-11-18 06:31:35 +03:00
}
if ( NT_STATUS_EQUAL ( status , NT_STATUS_NOT_FOUND ) ) {
2006-10-09 15:13:49 +04:00
status = pvfs_default_acl ( pvfs , req , name , fd , & sd ) ;
2004-11-18 06:31:35 +03:00
}
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
2006-03-10 23:49:20 +03:00
normalise_sd_flags ( sd , info - > query_secdesc . in . secinfo_flags ) ;
2004-11-18 06:31:35 +03:00
info - > query_secdesc . out . sd = sd ;
return NT_STATUS_OK ;
}
2004-12-01 14:35:01 +03:00
2006-09-16 19:37:45 +04:00
/*
check the read only bit against any of the write access bits
*/
2007-10-07 02:28:14 +04:00
static bool pvfs_read_only ( struct pvfs_state * pvfs , uint32_t access_mask )
2006-09-16 19:37:45 +04:00
{
if ( ( pvfs - > flags & PVFS_FLAG_READONLY ) & &
( access_mask & ( SEC_FILE_WRITE_DATA |
SEC_FILE_APPEND_DATA |
SEC_FILE_WRITE_EA |
SEC_FILE_WRITE_ATTRIBUTE |
SEC_STD_DELETE |
SEC_STD_WRITE_DAC |
SEC_STD_WRITE_OWNER |
SEC_DIR_DELETE_CHILD ) ) ) {
2007-10-07 02:28:14 +04:00
return true ;
2006-09-16 19:37:45 +04:00
}
2007-10-07 02:28:14 +04:00
return false ;
2006-09-16 19:37:45 +04:00
}
2009-08-05 11:51:21 +04:00
/*
see if we are a member of the appropriate unix group
*/
static bool pvfs_group_member ( struct pvfs_state * pvfs , gid_t gid )
{
int i , ngroups ;
gid_t * groups ;
if ( getegid ( ) = = gid ) {
return true ;
}
ngroups = getgroups ( 0 , NULL ) ;
if ( ngroups = = 0 ) {
return false ;
}
groups = talloc_array ( pvfs , gid_t , ngroups ) ;
if ( groups = = NULL ) {
return false ;
}
if ( getgroups ( ngroups , groups ) ! = ngroups ) {
talloc_free ( groups ) ;
return false ;
}
for ( i = 0 ; i < ngroups ; i + + ) {
if ( groups [ i ] = = gid ) break ;
}
talloc_free ( groups ) ;
return i < ngroups ;
}
2004-12-01 14:35:01 +03:00
/*
default access check function based on unix permissions
doing this saves on building a full security descriptor
for the common case of access check on files with no
specific NT ACL
*/
NTSTATUS pvfs_access_check_unix ( struct pvfs_state * pvfs ,
2006-03-10 17:31:17 +03:00
struct ntvfs_request * req ,
2004-12-01 14:35:01 +03:00
struct pvfs_filename * name ,
uint32_t * access_mask )
{
uid_t uid = geteuid ( ) ;
uint32_t max_bits = SEC_RIGHTS_FILE_READ | SEC_FILE_ALL ;
2009-10-16 16:03:42 +04:00
struct security_token * token = req - > session_info - > security_token ;
2004-12-01 14:35:01 +03:00
2006-09-16 19:37:45 +04:00
if ( pvfs_read_only ( pvfs , * access_mask ) ) {
2006-09-16 19:31:53 +04:00
return NT_STATUS_ACCESS_DENIED ;
}
2009-10-16 16:03:42 +04:00
if ( uid = = name - > st . st_uid ) {
2004-12-01 14:35:01 +03:00
max_bits | = SEC_STD_ALL ;
2009-10-17 02:09:42 +04:00
} else if ( security_token_has_privilege ( token , SEC_PRIV_RESTORE ) ) {
max_bits | = SEC_STD_DELETE ;
2004-12-01 14:35:01 +03:00
}
2009-08-05 11:51:21 +04:00
if ( ( name - > st . st_mode & S_IWOTH ) | |
( ( name - > st . st_mode & S_IWGRP ) & &
pvfs_group_member ( pvfs , name - > st . st_gid ) ) ) {
max_bits | = SEC_STD_ALL ;
}
2009-08-05 06:07:08 +04:00
if ( uwrap_enabled ( ) ) {
2009-08-05 05:21:06 +04:00
/* when running with the uid wrapper, files will be created
owned by the ruid , but we may have a different simulated
euid . We need to force the permission bits as though the
files owner matches the euid */
max_bits | = SEC_STD_ALL ;
}
2009-08-05 04:50:03 +04:00
2009-10-15 11:23:42 +04:00
if ( * access_mask & SEC_FLAG_MAXIMUM_ALLOWED ) {
2009-10-16 03:05:02 +04:00
* access_mask | = max_bits ;
* access_mask & = ~ SEC_FLAG_MAXIMUM_ALLOWED ;
2004-12-01 14:35:01 +03:00
}
2009-10-16 16:03:42 +04:00
if ( ( * access_mask & SEC_FLAG_SYSTEM_SECURITY ) & &
security_token_has_privilege ( token , SEC_PRIV_SECURITY ) ) {
max_bits | = SEC_FLAG_SYSTEM_SECURITY ;
2006-05-21 16:56:49 +04:00
}
2009-10-17 02:09:42 +04:00
if ( ( ( * access_mask & ~ max_bits ) & SEC_RIGHTS_PRIV_RESTORE ) & &
security_token_has_privilege ( token , SEC_PRIV_RESTORE ) ) {
max_bits | = ~ ( SEC_RIGHTS_PRIV_RESTORE ) ;
}
if ( ( ( * access_mask & ~ max_bits ) & SEC_RIGHTS_PRIV_BACKUP ) & &
security_token_has_privilege ( token , SEC_PRIV_BACKUP ) ) {
max_bits | = ~ ( SEC_RIGHTS_PRIV_BACKUP ) ;
}
2006-05-21 16:56:49 +04:00
2004-12-01 14:35:01 +03:00
if ( * access_mask & ~ max_bits ) {
2009-08-05 11:51:21 +04:00
DEBUG ( 0 , ( __location__ " denied access to '%s' - wanted 0x%08x but got 0x%08x (missing 0x%08x) \n " ,
name - > full_name , * access_mask , max_bits , * access_mask & ~ max_bits ) ) ;
2004-12-01 14:35:01 +03:00
return NT_STATUS_ACCESS_DENIED ;
}
2008-05-29 12:23:20 +04:00
if ( pvfs - > ntvfs - > ctx - > protocol ! = PROTOCOL_SMB2 ) {
/* on SMB, this bit is always granted, even if not
asked for */
* access_mask | = SEC_FILE_READ_ATTRIBUTE ;
}
2004-12-30 10:10:31 +03:00
2004-12-01 14:35:01 +03:00
return NT_STATUS_OK ;
}
/*
check the security descriptor on a file , if any
* access_mask is modified with the access actually granted
*/
NTSTATUS pvfs_access_check ( struct pvfs_state * pvfs ,
2006-03-10 17:31:17 +03:00
struct ntvfs_request * req ,
2004-12-01 14:35:01 +03:00
struct pvfs_filename * name ,
uint32_t * access_mask )
{
2006-03-16 21:54:19 +03:00
struct security_token * token = req - > session_info - > security_token ;
2004-12-01 14:35:01 +03:00
struct xattr_NTACL * acl ;
NTSTATUS status ;
struct security_descriptor * sd ;
2008-09-25 04:58:15 +04:00
/* on SMB2 a blank access mask is always denied */
if ( pvfs - > ntvfs - > ctx - > protocol = = PROTOCOL_SMB2 & &
* access_mask = = 0 ) {
2008-09-25 04:31:57 +04:00
return NT_STATUS_ACCESS_DENIED ;
}
2006-09-16 19:37:45 +04:00
if ( pvfs_read_only ( pvfs , * access_mask ) ) {
2006-09-16 19:31:53 +04:00
return NT_STATUS_ACCESS_DENIED ;
}
2005-01-27 10:08:20 +03:00
acl = talloc ( req , struct xattr_NTACL ) ;
2004-12-01 14:35:01 +03:00
if ( acl = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
2005-01-28 02:22:12 +03:00
/* expand the generic access bits to file specific bits */
* access_mask = pvfs_translate_mask ( * access_mask ) ;
2008-05-29 16:22:42 +04:00
if ( pvfs - > ntvfs - > ctx - > protocol ! = PROTOCOL_SMB2 ) {
* access_mask & = ~ SEC_FILE_READ_ATTRIBUTE ;
}
2005-01-28 02:22:12 +03:00
2004-12-01 14:35:01 +03:00
status = pvfs_acl_load ( pvfs , name , - 1 , acl ) ;
if ( NT_STATUS_EQUAL ( status , NT_STATUS_NOT_FOUND ) ) {
talloc_free ( acl ) ;
return pvfs_access_check_unix ( pvfs , req , name , access_mask ) ;
}
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
switch ( acl - > version ) {
case 1 :
sd = acl - > info . sd ;
break ;
default :
return NT_STATUS_INVALID_ACL ;
}
2004-12-03 16:04:10 +03:00
/* check the acl against the required access mask */
2004-12-01 14:35:01 +03:00
status = sec_access_check ( sd , token , * access_mask , access_mask ) ;
2008-05-29 12:23:20 +04:00
if ( pvfs - > ntvfs - > ctx - > protocol ! = PROTOCOL_SMB2 ) {
/* on SMB, this bit is always granted, even if not
asked for */
* access_mask | = SEC_FILE_READ_ATTRIBUTE ;
}
2004-12-03 16:04:10 +03:00
2004-12-01 14:35:01 +03:00
talloc_free ( acl ) ;
return status ;
}
2004-12-21 14:44:32 +03:00
/*
a simplified interface to access check , designed for calls that
do not take or return an access check mask
*/
NTSTATUS pvfs_access_check_simple ( struct pvfs_state * pvfs ,
2006-03-10 17:31:17 +03:00
struct ntvfs_request * req ,
2004-12-21 14:44:32 +03:00
struct pvfs_filename * name ,
uint32_t access_needed )
{
2004-12-30 08:50:23 +03:00
if ( access_needed = = 0 ) {
return NT_STATUS_OK ;
}
2004-12-21 14:44:32 +03:00
return pvfs_access_check ( pvfs , req , name , & access_needed ) ;
}
2004-12-30 05:25:20 +03:00
2004-12-30 08:50:23 +03:00
/*
access check for creating a new file / directory
*/
NTSTATUS pvfs_access_check_create ( struct pvfs_state * pvfs ,
2006-03-10 17:31:17 +03:00
struct ntvfs_request * req ,
2005-01-07 05:14:34 +03:00
struct pvfs_filename * name ,
uint32_t * access_mask )
{
struct pvfs_filename * parent ;
NTSTATUS status ;
status = pvfs_resolve_parent ( pvfs , req , name , & parent ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
status = pvfs_access_check ( pvfs , req , parent , access_mask ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
if ( ! ( ( * access_mask ) & SEC_DIR_ADD_FILE ) ) {
2005-01-07 08:24:38 +03:00
return pvfs_access_check_simple ( pvfs , req , parent , SEC_DIR_ADD_FILE ) ;
2005-01-07 05:14:34 +03:00
}
return status ;
}
/*
access check for creating a new file / directory - no access mask supplied
*/
2005-01-09 11:27:35 +03:00
NTSTATUS pvfs_access_check_parent ( struct pvfs_state * pvfs ,
2006-03-10 17:31:17 +03:00
struct ntvfs_request * req ,
2005-01-09 11:27:35 +03:00
struct pvfs_filename * name ,
uint32_t access_mask )
2004-12-30 08:50:23 +03:00
{
struct pvfs_filename * parent ;
NTSTATUS status ;
status = pvfs_resolve_parent ( pvfs , req , name , & parent ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
2005-01-09 11:27:35 +03:00
return pvfs_access_check_simple ( pvfs , req , parent , access_mask ) ;
2004-12-30 08:50:23 +03:00
}
2004-12-30 05:25:20 +03:00
/*
determine if an ACE is inheritable
*/
2007-10-07 02:28:14 +04:00
static bool pvfs_inheritable_ace ( struct pvfs_state * pvfs ,
2004-12-30 05:25:20 +03:00
const struct security_ace * ace ,
2007-10-07 02:28:14 +04:00
bool container )
2004-12-30 05:25:20 +03:00
{
if ( ! container ) {
return ( ace - > flags & SEC_ACE_FLAG_OBJECT_INHERIT ) ! = 0 ;
}
if ( ace - > flags & SEC_ACE_FLAG_CONTAINER_INHERIT ) {
2007-10-07 02:28:14 +04:00
return true ;
2004-12-30 05:25:20 +03:00
}
if ( ( ace - > flags & SEC_ACE_FLAG_OBJECT_INHERIT ) & &
! ( ace - > flags & SEC_ACE_FLAG_NO_PROPAGATE_INHERIT ) ) {
2007-10-07 02:28:14 +04:00
return true ;
2004-12-30 05:25:20 +03:00
}
2007-10-07 02:28:14 +04:00
return false ;
2004-12-30 05:25:20 +03:00
}
/*
this is the core of ACL inheritance . It copies any inheritable
aces from the parent SD to the child SD . Note that the algorithm
depends on whether the child is a container or not
*/
static NTSTATUS pvfs_acl_inherit_aces ( struct pvfs_state * pvfs ,
struct security_descriptor * parent_sd ,
struct security_descriptor * sd ,
2007-10-07 02:28:14 +04:00
bool container )
2004-12-30 05:25:20 +03:00
{
int i ;
for ( i = 0 ; i < parent_sd - > dacl - > num_aces ; i + + ) {
struct security_ace ace = parent_sd - > dacl - > aces [ i ] ;
NTSTATUS status ;
2005-01-01 07:25:46 +03:00
const struct dom_sid * creator = NULL , * new_id = NULL ;
uint32_t orig_flags ;
2004-12-30 05:25:20 +03:00
if ( ! pvfs_inheritable_ace ( pvfs , & ace , container ) ) {
continue ;
}
2005-01-01 07:25:46 +03:00
orig_flags = ace . flags ;
2004-12-30 05:25:20 +03:00
/* see the RAW-ACLS inheritance test for details on these rules */
if ( ! container ) {
ace . flags = 0 ;
} else {
ace . flags & = ~ SEC_ACE_FLAG_INHERIT_ONLY ;
if ( ! ( ace . flags & SEC_ACE_FLAG_CONTAINER_INHERIT ) ) {
ace . flags | = SEC_ACE_FLAG_INHERIT_ONLY ;
}
if ( ace . flags & SEC_ACE_FLAG_NO_PROPAGATE_INHERIT ) {
ace . flags = 0 ;
}
}
2005-01-01 07:25:46 +03:00
/* the CREATOR sids are special when inherited */
if ( dom_sid_equal ( & ace . trustee , pvfs - > sid_cache . creator_owner ) ) {
creator = pvfs - > sid_cache . creator_owner ;
new_id = sd - > owner_sid ;
} else if ( dom_sid_equal ( & ace . trustee , pvfs - > sid_cache . creator_group ) ) {
creator = pvfs - > sid_cache . creator_group ;
new_id = sd - > group_sid ;
} else {
new_id = & ace . trustee ;
}
if ( creator & & container & &
( ace . flags & SEC_ACE_FLAG_CONTAINER_INHERIT ) ) {
uint32_t flags = ace . flags ;
ace . trustee = * new_id ;
ace . flags = 0 ;
status = security_descriptor_dacl_add ( sd , & ace ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
ace . trustee = * creator ;
ace . flags = flags | SEC_ACE_FLAG_INHERIT_ONLY ;
status = security_descriptor_dacl_add ( sd , & ace ) ;
} else if ( container & &
! ( orig_flags & SEC_ACE_FLAG_NO_PROPAGATE_INHERIT ) ) {
status = security_descriptor_dacl_add ( sd , & ace ) ;
} else {
ace . trustee = * new_id ;
status = security_descriptor_dacl_add ( sd , & ace ) ;
}
2004-12-30 05:25:20 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
}
return NT_STATUS_OK ;
}
/*
setup an ACL on a new file / directory based on the inherited ACL from
the parent . If there is no inherited ACL then we don ' t set anything ,
as the default ACL applies anyway
*/
NTSTATUS pvfs_acl_inherit ( struct pvfs_state * pvfs ,
2006-03-10 17:31:17 +03:00
struct ntvfs_request * req ,
2004-12-30 05:25:20 +03:00
struct pvfs_filename * name ,
int fd )
{
struct xattr_NTACL * acl ;
NTSTATUS status ;
struct pvfs_filename * parent ;
struct security_descriptor * parent_sd , * sd ;
2007-10-07 02:28:14 +04:00
bool container ;
2008-03-29 01:29:01 +03:00
struct id_mapping * ids ;
struct composite_context * ctx ;
2004-12-30 05:25:20 +03:00
/* form the parents path */
status = pvfs_resolve_parent ( pvfs , req , name , & parent ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
2005-01-27 10:08:20 +03:00
acl = talloc ( req , struct xattr_NTACL ) ;
2004-12-30 05:25:20 +03:00
if ( acl = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
status = pvfs_acl_load ( pvfs , parent , - 1 , acl ) ;
if ( NT_STATUS_EQUAL ( status , NT_STATUS_NOT_FOUND ) ) {
return NT_STATUS_OK ;
}
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
switch ( acl - > version ) {
case 1 :
parent_sd = acl - > info . sd ;
break ;
default :
return NT_STATUS_INVALID_ACL ;
}
if ( parent_sd = = NULL | |
parent_sd - > dacl = = NULL | |
parent_sd - > dacl - > num_aces = = 0 ) {
/* go with the default ACL */
return NT_STATUS_OK ;
}
/* create the new sd */
sd = security_descriptor_initialise ( req ) ;
if ( sd = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
2008-03-29 01:29:01 +03:00
ids = talloc_array ( sd , struct id_mapping , 2 ) ;
NT_STATUS_HAVE_NO_MEMORY ( ids ) ;
ids [ 0 ] . unixid = talloc ( ids , struct unixid ) ;
NT_STATUS_HAVE_NO_MEMORY ( ids [ 0 ] . unixid ) ;
ids [ 0 ] . unixid - > id = name - > st . st_uid ;
ids [ 0 ] . unixid - > type = ID_TYPE_UID ;
ids [ 0 ] . sid = NULL ;
ids [ 0 ] . status = NT_STATUS_NONE_MAPPED ;
ids [ 1 ] . unixid = talloc ( ids , struct unixid ) ;
NT_STATUS_HAVE_NO_MEMORY ( ids [ 1 ] . unixid ) ;
ids [ 1 ] . unixid - > id = name - > st . st_gid ;
ids [ 1 ] . unixid - > type = ID_TYPE_GID ;
ids [ 1 ] . sid = NULL ;
ids [ 1 ] . status = NT_STATUS_NONE_MAPPED ;
ctx = wbc_xids_to_sids_send ( pvfs - > wbc_ctx , ids , 2 , ids ) ;
NT_STATUS_HAVE_NO_MEMORY ( ctx ) ;
status = wbc_xids_to_sids_recv ( ctx , & ids ) ;
NT_STATUS_NOT_OK_RETURN ( status ) ;
sd - > owner_sid = talloc_steal ( sd , ids [ 0 ] . sid ) ;
sd - > group_sid = talloc_steal ( sd , ids [ 1 ] . sid ) ;
2004-12-30 05:25:20 +03:00
sd - > type | = SEC_DESC_DACL_PRESENT ;
2007-10-07 02:28:14 +04:00
container = ( name - > dos . attrib & FILE_ATTRIBUTE_DIRECTORY ) ? true : false ;
2004-12-30 05:25:20 +03:00
/* fill in the aces from the parent */
status = pvfs_acl_inherit_aces ( pvfs , parent_sd , sd , container ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
/* if there is nothing to inherit then we fallback to the
default acl */
if ( sd - > dacl = = NULL | | sd - > dacl - > num_aces = = 0 ) {
return NT_STATUS_OK ;
}
acl - > info . sd = sd ;
status = pvfs_acl_save ( pvfs , name , fd , acl ) ;
return status ;
}
2008-05-29 13:16:26 +04:00
/*
return the maximum allowed access mask
*/
NTSTATUS pvfs_access_maximal_allowed ( struct pvfs_state * pvfs ,
struct ntvfs_request * req ,
struct pvfs_filename * name ,
uint32_t * maximal_access )
{
* maximal_access = SEC_FLAG_MAXIMUM_ALLOWED ;
return pvfs_access_check ( pvfs , req , name , maximal_access ) ;
}