2007-10-26 21:50:55 +04:00
/*
Unix SMB / CIFS implementation .
Check access to files based on security descriptors .
Copyright ( C ) Jeremy Allison 2005 - 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 < http : //www.gnu.org/licenses/>.
*/
# include "includes.h"
2010-10-12 08:27:50 +04:00
# include "../libcli/security/security.h"
2010-05-28 04:19:32 +04:00
# include "../librpc/gen_ndr/ndr_security.h"
2007-10-26 21:50:55 +04:00
# undef DBGC_CLASS
# define DBGC_CLASS DBGC_ACLS
2008-06-18 02:06:46 +04:00
/**
* Security descriptor / NT Token level access check function .
*/
2008-05-03 04:22:10 +04:00
bool can_access_file_acl ( struct connection_struct * conn ,
2009-06-23 02:26:56 +04:00
const struct smb_filename * smb_fname ,
uint32_t access_mask )
2007-10-26 21:50:55 +04:00
{
NTSTATUS status ;
uint32_t access_granted ;
struct security_descriptor * secdesc = NULL ;
2009-06-23 02:26:56 +04:00
bool ret ;
2007-10-26 21:50:55 +04:00
2010-03-15 22:13:30 +03:00
if ( get_current_uid ( conn ) = = ( uid_t ) 0 ) {
2009-03-05 03:57:37 +03:00
/* I'm sorry sir, I didn't know you were root... */
return true ;
}
2009-07-21 22:35:17 +04:00
status = SMB_VFS_GET_NT_ACL ( conn , smb_fname - > base_name ,
2010-06-03 01:22:12 +04:00
( SECINFO_OWNER |
2010-06-03 01:25:18 +04:00
SECINFO_GROUP |
2010-06-03 01:35:44 +04:00
SECINFO_DACL ) ,
2007-12-05 11:53:10 +03:00
& secdesc ) ;
2007-10-26 21:50:55 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 5 , ( " Could not get acl: %s \n " , nt_errstr ( status ) ) ) ;
2009-06-23 02:26:56 +04:00
ret = false ;
goto out ;
2007-10-26 21:50:55 +04:00
}
2010-03-15 22:13:30 +03:00
status = se_access_check ( secdesc , get_current_nttok ( conn ) ,
2008-10-31 20:51:45 +03:00
access_mask , & access_granted ) ;
2009-06-23 02:26:56 +04:00
ret = NT_STATUS_IS_OK ( status ) ;
2010-01-13 08:06:09 +03:00
if ( DEBUGLEVEL > = 10 ) {
DEBUG ( 10 , ( " can_access_file_acl for file %s "
" access_mask 0x%x, access_granted 0x%x "
" access %s \n " ,
smb_fname_str_dbg ( smb_fname ) ,
( unsigned int ) access_mask ,
( unsigned int ) access_granted ,
ret ? " ALLOWED " : " DENIED " ) ) ;
NDR_PRINT_DEBUG ( security_descriptor , secdesc ) ;
}
2009-06-23 02:26:56 +04:00
out :
2007-10-26 21:50:55 +04:00
TALLOC_FREE ( secdesc ) ;
2009-06-23 02:26:56 +04:00
return ret ;
2007-10-26 21:50:55 +04:00
}
/****************************************************************************
Actually emulate the in - kernel access checking for delete access . We need
this to successfully return ACCESS_DENIED on a file open for delete access .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2009-06-16 01:14:31 +04:00
bool can_delete_file_in_directory ( connection_struct * conn ,
2009-06-23 02:26:56 +04:00
struct smb_filename * smb_fname )
2007-10-26 21:50:55 +04:00
{
TALLOC_CTX * ctx = talloc_tos ( ) ;
char * dname = NULL ;
2009-06-23 02:26:56 +04:00
struct smb_filename * smb_fname_parent = NULL ;
NTSTATUS status ;
bool ret ;
2007-10-26 21:50:55 +04:00
if ( ! CAN_WRITE ( conn ) ) {
return False ;
}
/* Get the parent directory permission mask and owners. */
2009-06-16 01:14:31 +04:00
if ( ! parent_dirname ( ctx , smb_fname - > base_name , & dname , NULL ) ) {
2007-10-26 21:50:55 +04:00
return False ;
}
2009-06-23 02:26:56 +04:00
status = create_synthetic_smb_fname ( ctx , dname , NULL , NULL ,
& smb_fname_parent ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
ret = false ;
goto out ;
}
if ( SMB_VFS_STAT ( conn , smb_fname_parent ) ! = 0 ) {
ret = false ;
goto out ;
2007-10-26 21:50:55 +04:00
}
/* fast paths first */
2009-06-23 02:26:56 +04:00
if ( ! S_ISDIR ( smb_fname_parent - > st . st_ex_mode ) ) {
ret = false ;
goto out ;
2007-10-26 21:50:55 +04:00
}
2010-03-15 22:13:30 +03:00
if ( get_current_uid ( conn ) = = ( uid_t ) 0 ) {
2007-10-26 21:50:55 +04:00
/* I'm sorry sir, I didn't know you were root... */
2009-06-23 02:26:56 +04:00
ret = true ;
goto out ;
2007-10-26 21:50:55 +04:00
}
# ifdef S_ISVTX
2009-06-11 23:51:45 +04:00
/* sticky bit means delete only by owner of file or by root or
* by owner of directory . */
2009-06-23 02:26:56 +04:00
if ( smb_fname_parent - > st . st_ex_mode & S_ISVTX ) {
if ( SMB_VFS_STAT ( conn , smb_fname ) ! = 0 ) {
2007-10-26 21:50:55 +04:00
if ( errno = = ENOENT ) {
/* If the file doesn't already exist then
* yes we ' ll be able to delete it . */
2009-06-23 02:26:56 +04:00
ret = true ;
goto out ;
2007-10-26 21:50:55 +04:00
}
2009-06-11 23:51:45 +04:00
DEBUG ( 10 , ( " can_delete_file_in_directory: can't "
2009-06-16 01:14:31 +04:00
" stat file %s (%s) " ,
smb_fname_str_dbg ( smb_fname ) ,
strerror ( errno ) ) ) ;
2009-06-23 02:26:56 +04:00
ret = false ;
goto out ;
2007-10-26 21:50:55 +04:00
}
2009-06-16 01:14:31 +04:00
2007-10-26 21:50:55 +04:00
/*
* Patch from SATOH Fumiyasu < fumiyas @ miraclelinux . com >
* for bug # 3348. Don ' t assume owning sticky bit
* directory means write access allowed .
2009-06-11 23:51:45 +04:00
* Fail to delete if we ' re not the owner of the file ,
* or the owner of the directory as we have no possible
* chance of deleting . Otherwise , go on and check the ACL .
2007-10-26 21:50:55 +04:00
*/
2010-03-15 22:13:30 +03:00
if ( ( get_current_uid ( conn ) ! =
2009-06-23 02:26:56 +04:00
smb_fname_parent - > st . st_ex_uid ) & &
2010-03-15 22:13:30 +03:00
( get_current_uid ( conn ) ! = smb_fname - > st . st_ex_uid ) ) {
2009-06-11 23:51:45 +04:00
DEBUG ( 10 , ( " can_delete_file_in_directory: not "
2009-06-16 01:14:31 +04:00
" owner of file %s or directory %s " ,
2009-06-23 02:26:56 +04:00
smb_fname_str_dbg ( smb_fname ) ,
smb_fname_str_dbg ( smb_fname_parent ) ) ) ;
ret = false ;
goto out ;
2007-10-26 21:50:55 +04:00
}
}
# endif
/* now for ACL checks */
2008-06-19 16:53:46 +04:00
/*
* There ' s two ways to get the permission to delete a file : First by
* having the DELETE bit on the file itself and second if that does
* not help , by the DELETE_CHILD bit on the containing directory .
*
2009-02-03 04:10:27 +03:00
* Here we only check the directory permissions , we will
* check the file DELETE permission separately .
2008-06-19 16:53:46 +04:00
*/
2009-06-23 02:26:56 +04:00
ret = can_access_file_acl ( conn , smb_fname_parent , FILE_DELETE_CHILD ) ;
out :
TALLOC_FREE ( dname ) ;
TALLOC_FREE ( smb_fname_parent ) ;
return ret ;
2007-10-26 21:50:55 +04:00
}
/****************************************************************************
Actually emulate the in - kernel access checking for read / write access . We need
this to successfully check for ability to write for dos filetimes .
Note this doesn ' t take into account share write permissions .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2009-06-23 02:26:56 +04:00
bool can_access_file_data ( connection_struct * conn ,
const struct smb_filename * smb_fname ,
uint32 access_mask )
2007-10-26 21:50:55 +04:00
{
if ( ! ( access_mask & ( FILE_READ_DATA | FILE_WRITE_DATA ) ) ) {
return False ;
}
access_mask & = ( FILE_READ_DATA | FILE_WRITE_DATA ) ;
/* some fast paths first */
2008-05-03 04:22:10 +04:00
DEBUG ( 10 , ( " can_access_file_data: requesting 0x%x on file %s \n " ,
2009-06-23 02:26:56 +04:00
( unsigned int ) access_mask , smb_fname_str_dbg ( smb_fname ) ) ) ;
2007-10-26 21:50:55 +04:00
2010-03-15 22:13:30 +03:00
if ( get_current_uid ( conn ) = = ( uid_t ) 0 ) {
2007-10-26 21:50:55 +04:00
/* I'm sorry sir, I didn't know you were root... */
return True ;
}
2009-06-23 02:26:56 +04:00
SMB_ASSERT ( VALID_STAT ( smb_fname - > st ) ) ;
2007-10-26 21:50:55 +04:00
/* Check primary owner access. */
2010-03-15 22:13:30 +03:00
if ( get_current_uid ( conn ) = = smb_fname - > st . st_ex_uid ) {
2007-10-26 21:50:55 +04:00
switch ( access_mask ) {
case FILE_READ_DATA :
2009-06-23 02:26:56 +04:00
return ( smb_fname - > st . st_ex_mode & S_IRUSR ) ?
True : False ;
2007-10-26 21:50:55 +04:00
case FILE_WRITE_DATA :
2009-06-23 02:26:56 +04:00
return ( smb_fname - > st . st_ex_mode & S_IWUSR ) ?
True : False ;
2007-10-26 21:50:55 +04:00
default : /* FILE_READ_DATA|FILE_WRITE_DATA */
2009-06-23 02:26:56 +04:00
if ( ( smb_fname - > st . st_ex_mode &
( S_IWUSR | S_IRUSR ) ) = =
( S_IWUSR | S_IRUSR ) ) {
2007-10-26 21:50:55 +04:00
return True ;
} else {
return False ;
}
}
}
/* now for ACL checks */
2009-06-23 02:26:56 +04:00
return can_access_file_acl ( conn , smb_fname , access_mask ) ;
2007-10-26 21:50:55 +04:00
}
/****************************************************************************
Userspace check for write access .
Note this doesn ' t take into account share write permissions .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2009-06-26 05:19:09 +04:00
bool can_write_to_file ( connection_struct * conn ,
const struct smb_filename * smb_fname )
2007-10-26 21:50:55 +04:00
{
2009-06-26 05:19:09 +04:00
return can_access_file_data ( conn , smb_fname , FILE_WRITE_DATA ) ;
2007-10-26 21:50:55 +04:00
}
2008-05-02 21:09:00 +04:00
/****************************************************************************
Check for an existing default Windows ACL on a directory .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
bool directory_has_default_acl ( connection_struct * conn , const char * fname )
{
/* returns talloced off tos. */
struct security_descriptor * secdesc = NULL ;
unsigned int i ;
NTSTATUS status = SMB_VFS_GET_NT_ACL ( conn , fname ,
2010-06-03 01:35:44 +04:00
SECINFO_DACL , & secdesc ) ;
2008-05-02 21:09:00 +04:00
if ( ! NT_STATUS_IS_OK ( status ) | | secdesc = = NULL ) {
return false ;
}
for ( i = 0 ; i < secdesc - > dacl - > num_aces ; i + + ) {
struct security_ace * psa = & secdesc - > dacl - > aces [ i ] ;
if ( psa - > flags & ( SEC_ACE_FLAG_OBJECT_INHERIT |
SEC_ACE_FLAG_CONTAINER_INHERIT ) ) {
TALLOC_FREE ( secdesc ) ;
return true ;
}
}
TALLOC_FREE ( secdesc ) ;
return false ;
}