2012-08-15 14:34:41 +04:00
/*
* Fake ACLs VFS module . Implements passthrough operation of all VFS
* calls to disk functions , except for file ownership and ACLs , which
* are stored in xattrs .
*
* Copyright ( C ) Tim Potter , 1999 - 2000
* Copyright ( C ) Alexander Bokovoy , 2002
* Copyright ( C ) Andrew Bartlett , 2002 , 2012
*
* 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"
# include "smbd/smbd.h"
# include "system/filesys.h"
# include "auth.h"
# include "librpc/gen_ndr/ndr_smb_acl.h"
# undef DBGC_CLASS
# define DBGC_CLASS DBGC_VFS
2012-08-20 04:10:27 +04:00
# define FAKE_UID "system.fake_uid"
# define FAKE_GID "system.fake_gid"
# define FAKE_ACL_ACCESS_XATTR "system.fake_access_acl"
# define FAKE_ACL_DEFAULT_XATTR "system.fake_default_acl"
2012-08-15 14:34:41 +04:00
static int fake_acls_uid ( vfs_handle_struct * handle ,
const char * path ,
uid_t * uid )
{
ssize_t size ;
uint8_t uid_buf [ 4 ] ;
size = SMB_VFS_NEXT_GETXATTR ( handle , path , FAKE_UID , uid_buf , sizeof ( uid_buf ) ) ;
if ( size = = - 1 & & errno = = ENOATTR ) {
return 0 ;
}
if ( size ! = 4 ) {
return - 1 ;
}
* uid = IVAL ( uid_buf , 0 ) ;
return 0 ;
}
static int fake_acls_gid ( vfs_handle_struct * handle ,
const char * path ,
uid_t * gid )
{
ssize_t size ;
uint8_t gid_buf [ 4 ] ;
size = SMB_VFS_NEXT_GETXATTR ( handle , path , FAKE_GID , gid_buf , sizeof ( gid_buf ) ) ;
if ( size = = - 1 & & errno = = ENOATTR ) {
return 0 ;
}
if ( size ! = 4 ) {
return - 1 ;
}
* gid = IVAL ( gid_buf , 0 ) ;
return 0 ;
}
static int fake_acls_fuid ( vfs_handle_struct * handle ,
files_struct * fsp ,
uid_t * uid )
{
ssize_t size ;
uint8_t uid_buf [ 4 ] ;
size = SMB_VFS_NEXT_FGETXATTR ( handle , fsp , FAKE_UID , uid_buf , sizeof ( uid_buf ) ) ;
if ( size = = - 1 & & errno = = ENOATTR ) {
return 0 ;
}
if ( size ! = 4 ) {
return - 1 ;
}
* uid = IVAL ( uid_buf , 0 ) ;
return 0 ;
}
static int fake_acls_fgid ( vfs_handle_struct * handle ,
files_struct * fsp ,
uid_t * gid )
{
ssize_t size ;
uint8_t gid_buf [ 4 ] ;
size = SMB_VFS_NEXT_FGETXATTR ( handle , fsp , FAKE_GID , gid_buf , sizeof ( gid_buf ) ) ;
if ( size = = - 1 & & errno = = ENOATTR ) {
return 0 ;
}
if ( size ! = 4 ) {
return - 1 ;
}
* gid = IVAL ( gid_buf , 0 ) ;
return 0 ;
}
static int fake_acls_stat ( vfs_handle_struct * handle ,
struct smb_filename * smb_fname )
{
int ret = - 1 ;
ret = SMB_VFS_NEXT_STAT ( handle , smb_fname ) ;
if ( ret = = 0 ) {
TALLOC_CTX * frame = talloc_stackframe ( ) ;
char * path ;
2015-05-05 05:56:39 +03:00
struct smb_filename smb_fname_base = {
. base_name = smb_fname - > base_name
} ;
2012-08-15 14:34:41 +04:00
NTSTATUS status ;
2015-05-05 05:56:39 +03:00
/*
* As we ' re calling getxattr directly here
* we need to use only the base_name , not
* the full name containing any stream name .
*/
status = get_full_smb_filename ( frame , & smb_fname_base , & path ) ;
2012-08-15 14:34:41 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
errno = map_errno_from_nt_status ( status ) ;
TALLOC_FREE ( frame ) ;
return - 1 ;
}
ret = fake_acls_uid ( handle , path , & smb_fname - > st . st_ex_uid ) ;
if ( ret ! = 0 ) {
TALLOC_FREE ( frame ) ;
return ret ;
}
ret = fake_acls_gid ( handle , path , & smb_fname - > st . st_ex_gid ) ;
if ( ret ! = 0 ) {
TALLOC_FREE ( frame ) ;
return ret ;
}
TALLOC_FREE ( frame ) ;
}
return ret ;
}
2012-08-20 15:28:57 +04:00
static int fake_acls_lstat ( vfs_handle_struct * handle ,
struct smb_filename * smb_fname )
{
int ret = - 1 ;
ret = SMB_VFS_NEXT_LSTAT ( handle , smb_fname ) ;
if ( ret = = 0 ) {
TALLOC_CTX * frame = talloc_stackframe ( ) ;
char * path ;
2015-05-05 05:56:39 +03:00
struct smb_filename smb_fname_base = {
. base_name = smb_fname - > base_name
} ;
2012-08-20 15:28:57 +04:00
NTSTATUS status ;
2015-05-05 05:56:39 +03:00
/*
* As we ' re calling getxattr directly here
* we need to use only the base_name , not
* the full name containing any stream name .
*/
status = get_full_smb_filename ( frame , & smb_fname_base , & path ) ;
2012-08-20 15:28:57 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
errno = map_errno_from_nt_status ( status ) ;
TALLOC_FREE ( frame ) ;
return - 1 ;
}
/* This isn't quite right (calling getxattr not
* lgetxattr ) , but for the test purposes of this
* module ( fake NT ACLs from windows clients ) , it is
* close enough . We removed the l * xattr functions
* because linux doesn ' t support using them , but we
* could fake them in xattr_tdb if we really wanted
* to . We ignore errors because the link might not point anywhere */
fake_acls_uid ( handle , path , & smb_fname - > st . st_ex_uid ) ;
fake_acls_gid ( handle , path , & smb_fname - > st . st_ex_gid ) ;
TALLOC_FREE ( frame ) ;
}
return ret ;
}
2012-08-15 14:34:41 +04:00
static int fake_acls_fstat ( vfs_handle_struct * handle , files_struct * fsp , SMB_STRUCT_STAT * sbuf )
{
int ret = - 1 ;
ret = SMB_VFS_NEXT_FSTAT ( handle , fsp , sbuf ) ;
if ( ret = = 0 ) {
ret = fake_acls_fuid ( handle , fsp , & sbuf - > st_ex_uid ) ;
if ( ret ! = 0 ) {
return ret ;
}
ret = fake_acls_fgid ( handle , fsp , & sbuf - > st_ex_gid ) ;
if ( ret ! = 0 ) {
return ret ;
}
}
return ret ;
}
2012-10-10 03:18:32 +04:00
static SMB_ACL_T fake_acls_blob2acl ( DATA_BLOB * blob , TALLOC_CTX * mem_ctx )
2012-08-15 14:34:41 +04:00
{
enum ndr_err_code ndr_err ;
2012-10-10 03:18:32 +04:00
struct smb_acl_t * acl = talloc ( mem_ctx , struct smb_acl_t ) ;
2012-08-15 14:34:41 +04:00
if ( ! acl ) {
errno = ENOMEM ;
return NULL ;
}
ndr_err = ndr_pull_struct_blob ( blob , acl , acl ,
( ndr_pull_flags_fn_t ) ndr_pull_smb_acl_t ) ;
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) {
DEBUG ( 0 , ( " ndr_pull_acl_t failed: %s \n " ,
ndr_errstr ( ndr_err ) ) ) ;
TALLOC_FREE ( acl ) ;
return NULL ;
}
return acl ;
}
static DATA_BLOB fake_acls_acl2blob ( TALLOC_CTX * mem_ctx , SMB_ACL_T acl )
{
enum ndr_err_code ndr_err ;
DATA_BLOB blob ;
ndr_err = ndr_push_struct_blob ( & blob , mem_ctx , acl ,
( ndr_push_flags_fn_t ) ndr_push_smb_acl_t ) ;
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) {
DEBUG ( 0 , ( " ndr_push_acl_t failed: %s \n " ,
ndr_errstr ( ndr_err ) ) ) ;
return data_blob_null ;
}
return blob ;
}
2012-10-10 03:18:32 +04:00
static SMB_ACL_T fake_acls_sys_acl_get_file ( struct vfs_handle_struct * handle ,
const char * path ,
SMB_ACL_TYPE_T type ,
TALLOC_CTX * mem_ctx )
2012-08-15 14:34:41 +04:00
{
DATA_BLOB blob = data_blob_null ;
ssize_t length ;
const char * name = NULL ;
struct smb_acl_t * acl = NULL ;
TALLOC_CTX * frame = talloc_stackframe ( ) ;
switch ( type ) {
case SMB_ACL_TYPE_ACCESS :
name = FAKE_ACL_ACCESS_XATTR ;
break ;
case SMB_ACL_TYPE_DEFAULT :
name = FAKE_ACL_DEFAULT_XATTR ;
break ;
}
do {
blob . length + = 1000 ;
blob . data = talloc_realloc ( frame , blob . data , uint8_t , blob . length ) ;
if ( ! blob . data ) {
errno = ENOMEM ;
TALLOC_FREE ( frame ) ;
return NULL ;
}
length = SMB_VFS_NEXT_GETXATTR ( handle , path , name , blob . data , blob . length ) ;
blob . length = length ;
} while ( length = = - 1 & & errno = = ERANGE ) ;
if ( length = = - 1 & & errno = = ENOATTR ) {
TALLOC_FREE ( frame ) ;
return NULL ;
}
if ( length ! = - 1 ) {
2012-10-10 03:18:32 +04:00
acl = fake_acls_blob2acl ( & blob , mem_ctx ) ;
2012-08-15 14:34:41 +04:00
}
TALLOC_FREE ( frame ) ;
return acl ;
}
2012-10-10 03:18:32 +04:00
static SMB_ACL_T fake_acls_sys_acl_get_fd ( struct vfs_handle_struct * handle ,
files_struct * fsp ,
TALLOC_CTX * mem_ctx )
2012-08-15 14:34:41 +04:00
{
DATA_BLOB blob = data_blob_null ;
ssize_t length ;
const char * name = FAKE_ACL_ACCESS_XATTR ;
2012-09-23 00:33:02 +04:00
struct smb_acl_t * acl = NULL ;
2012-08-15 14:34:41 +04:00
TALLOC_CTX * frame = talloc_stackframe ( ) ;
do {
blob . length + = 1000 ;
blob . data = talloc_realloc ( frame , blob . data , uint8_t , blob . length ) ;
if ( ! blob . data ) {
errno = ENOMEM ;
TALLOC_FREE ( frame ) ;
return NULL ;
}
length = SMB_VFS_NEXT_FGETXATTR ( handle , fsp , name , blob . data , blob . length ) ;
blob . length = length ;
} while ( length = = - 1 & & errno = = ERANGE ) ;
if ( length = = - 1 & & errno = = ENOATTR ) {
TALLOC_FREE ( frame ) ;
return NULL ;
}
if ( length ! = - 1 ) {
2012-10-10 03:18:32 +04:00
acl = fake_acls_blob2acl ( & blob , mem_ctx ) ;
2012-08-15 14:34:41 +04:00
}
TALLOC_FREE ( frame ) ;
return acl ;
}
2012-09-10 06:44:01 +04:00
2012-08-15 14:34:41 +04:00
static int fake_acls_sys_acl_set_file ( vfs_handle_struct * handle , const char * path , SMB_ACL_TYPE_T acltype , SMB_ACL_T theacl )
{
int ret ;
const char * name = NULL ;
TALLOC_CTX * frame = talloc_stackframe ( ) ;
DATA_BLOB blob = fake_acls_acl2blob ( frame , theacl ) ;
if ( ! blob . data ) {
DEBUG ( 0 , ( " Failed to convert ACL to linear blob for xattr storage \n " ) ) ;
TALLOC_FREE ( frame ) ;
errno = EINVAL ;
return - 1 ;
}
switch ( acltype ) {
case SMB_ACL_TYPE_ACCESS :
name = FAKE_ACL_ACCESS_XATTR ;
break ;
case SMB_ACL_TYPE_DEFAULT :
name = FAKE_ACL_DEFAULT_XATTR ;
break ;
}
ret = SMB_VFS_NEXT_SETXATTR ( handle , path , name , blob . data , blob . length , 0 ) ;
TALLOC_FREE ( frame ) ;
return ret ;
}
static int fake_acls_sys_acl_set_fd ( vfs_handle_struct * handle , files_struct * fsp , SMB_ACL_T theacl )
{
int ret ;
const char * name = FAKE_ACL_ACCESS_XATTR ;
TALLOC_CTX * frame = talloc_stackframe ( ) ;
DATA_BLOB blob = fake_acls_acl2blob ( frame , theacl ) ;
if ( ! blob . data ) {
DEBUG ( 0 , ( " Failed to convert ACL to linear blob for xattr storage \n " ) ) ;
TALLOC_FREE ( frame ) ;
errno = EINVAL ;
return - 1 ;
}
ret = SMB_VFS_NEXT_FSETXATTR ( handle , fsp , name , blob . data , blob . length , 0 ) ;
TALLOC_FREE ( frame ) ;
return ret ;
}
static int fake_acls_sys_acl_delete_def_file ( vfs_handle_struct * handle , const char * path )
{
2012-08-18 18:01:57 +04:00
int ret ;
2012-08-15 14:34:41 +04:00
const char * name = FAKE_ACL_DEFAULT_XATTR ;
2012-08-18 18:01:57 +04:00
TALLOC_CTX * frame = talloc_stackframe ( ) ;
2013-04-12 13:22:23 +04:00
struct smb_filename * smb_fname ;
2015-05-02 07:06:20 +03:00
smb_fname = synthetic_smb_fname ( frame , path , NULL , NULL ) ;
2013-04-12 13:22:23 +04:00
if ( smb_fname = = NULL ) {
2012-08-18 18:01:57 +04:00
TALLOC_FREE ( frame ) ;
2013-04-12 13:22:23 +04:00
errno = ENOMEM ;
2012-08-18 18:01:57 +04:00
return - 1 ;
}
ret = SMB_VFS_NEXT_STAT ( handle , smb_fname ) ;
if ( ret = = - 1 ) {
TALLOC_FREE ( frame ) ;
return - 1 ;
}
if ( ! S_ISDIR ( smb_fname - > st . st_ex_mode ) ) {
errno = EINVAL ;
TALLOC_FREE ( frame ) ;
return - 1 ;
}
ret = SMB_VFS_NEXT_REMOVEXATTR ( handle , path , name ) ;
if ( ret = = - 1 & & errno = = ENOATTR ) {
ret = 0 ;
errno = 0 ;
}
TALLOC_FREE ( frame ) ;
return ret ;
2012-08-15 14:34:41 +04:00
}
2012-10-11 03:47:17 +04:00
static int fake_acls_chown ( vfs_handle_struct * handle , const char * path , uid_t uid , gid_t gid )
2012-08-15 14:34:41 +04:00
{
int ret ;
uint8_t id_buf [ 4 ] ;
if ( uid ! = - 1 ) {
SIVAL ( id_buf , 0 , uid ) ;
ret = SMB_VFS_NEXT_SETXATTR ( handle , path , FAKE_UID , id_buf , sizeof ( id_buf ) , 0 ) ;
if ( ret ! = 0 ) {
return ret ;
}
}
if ( gid ! = - 1 ) {
SIVAL ( id_buf , 0 , gid ) ;
ret = SMB_VFS_NEXT_SETXATTR ( handle , path , FAKE_GID , id_buf , sizeof ( id_buf ) , 0 ) ;
if ( ret ! = 0 ) {
return ret ;
}
}
return 0 ;
}
2012-10-11 03:47:17 +04:00
static int fake_acls_lchown ( vfs_handle_struct * handle , const char * path , uid_t uid , gid_t gid )
2012-08-20 15:28:57 +04:00
{
int ret ;
uint8_t id_buf [ 4 ] ;
if ( uid ! = - 1 ) {
/* This isn't quite right (calling setxattr not
* lsetxattr ) , but for the test purposes of this
* module ( fake NT ACLs from windows clients ) , it is
* close enough . We removed the l * xattr functions
* because linux doesn ' t support using them , but we
* could fake them in xattr_tdb if we really wanted
* to .
*/
SIVAL ( id_buf , 0 , uid ) ;
ret = SMB_VFS_NEXT_SETXATTR ( handle , path , FAKE_UID , id_buf , sizeof ( id_buf ) , 0 ) ;
if ( ret ! = 0 ) {
return ret ;
}
}
if ( gid ! = - 1 ) {
SIVAL ( id_buf , 0 , gid ) ;
ret = SMB_VFS_NEXT_SETXATTR ( handle , path , FAKE_GID , id_buf , sizeof ( id_buf ) , 0 ) ;
if ( ret ! = 0 ) {
return ret ;
}
}
return 0 ;
}
2012-08-15 14:34:41 +04:00
static int fake_acls_fchown ( vfs_handle_struct * handle , files_struct * fsp , uid_t uid , gid_t gid )
{
int ret ;
uint8_t id_buf [ 4 ] ;
if ( uid ! = - 1 ) {
SIVAL ( id_buf , 0 , uid ) ;
ret = SMB_VFS_NEXT_FSETXATTR ( handle , fsp , FAKE_UID , id_buf , sizeof ( id_buf ) , 0 ) ;
if ( ret ! = 0 ) {
return ret ;
}
}
if ( gid ! = - 1 ) {
SIVAL ( id_buf , 0 , gid ) ;
ret = SMB_VFS_NEXT_FSETXATTR ( handle , fsp , FAKE_GID , id_buf , sizeof ( id_buf ) , 0 ) ;
if ( ret ! = 0 ) {
return ret ;
}
}
return 0 ;
}
static struct vfs_fn_pointers vfs_fake_acls_fns = {
. stat_fn = fake_acls_stat ,
2012-08-20 15:28:57 +04:00
. lstat_fn = fake_acls_lstat ,
2012-08-15 14:34:41 +04:00
. fstat_fn = fake_acls_fstat ,
. sys_acl_get_file_fn = fake_acls_sys_acl_get_file ,
. sys_acl_get_fd_fn = fake_acls_sys_acl_get_fd ,
2012-10-10 09:53:43 +04:00
. sys_acl_blob_get_file_fn = posix_sys_acl_blob_get_file ,
. sys_acl_blob_get_fd_fn = posix_sys_acl_blob_get_fd ,
2012-08-15 14:34:41 +04:00
. sys_acl_set_file_fn = fake_acls_sys_acl_set_file ,
. sys_acl_set_fd_fn = fake_acls_sys_acl_set_fd ,
. sys_acl_delete_def_file_fn = fake_acls_sys_acl_delete_def_file ,
. chown_fn = fake_acls_chown ,
2012-08-20 15:28:57 +04:00
. lchown_fn = fake_acls_lchown ,
2012-08-15 14:34:41 +04:00
. fchown_fn = fake_acls_fchown ,
} ;
NTSTATUS vfs_fake_acls_init ( void ) ;
NTSTATUS vfs_fake_acls_init ( void )
{
return smb_register_vfs ( SMB_VFS_INTERFACE_VERSION , " fake_acls " ,
& vfs_fake_acls_fns ) ;
}