mirror of
https://github.com/samba-team/samba.git
synced 2024-12-27 03:21:53 +03:00
b72287514c
When "ignore system acls" is set to "yes, we need to ensure filesystem permission always grant access so that when doing our own access checks we don't run into situations where we grant access but the filesystem doesn't. Bug: https://bugzilla.samba.org/show_bug.cgi?id=12181 Signed-off-by: Ralph Boehme <slow@samba.org> Reviewed-by: Jeremy Allison <jra@samba.org> Autobuild-User(master): Ralph Böhme <slow@samba.org> Autobuild-Date(master): Wed Aug 31 18:41:20 CEST 2016 on sn-devel-144
445 lines
11 KiB
C
445 lines
11 KiB
C
/*
|
|
* Store Windows ACLs in a tdb.
|
|
*
|
|
* Copyright (C) Volker Lendecke, 2008
|
|
* Copyright (C) Jeremy Allison, 2008
|
|
*
|
|
* 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 "librpc/gen_ndr/xattr.h"
|
|
#include "librpc/gen_ndr/ndr_xattr.h"
|
|
#include "../lib/crypto/sha256.h"
|
|
#include "dbwrap/dbwrap.h"
|
|
#include "dbwrap/dbwrap_open.h"
|
|
#include "auth.h"
|
|
#include "util_tdb.h"
|
|
|
|
#undef DBGC_CLASS
|
|
#define DBGC_CLASS DBGC_VFS
|
|
|
|
#define ACL_MODULE_NAME "acl_tdb"
|
|
#include "modules/vfs_acl_common.c"
|
|
|
|
static unsigned int ref_count;
|
|
static struct db_context *acl_db;
|
|
|
|
/*******************************************************************
|
|
Open acl_db if not already open, increment ref count.
|
|
*******************************************************************/
|
|
|
|
static bool acl_tdb_init(void)
|
|
{
|
|
char *dbname;
|
|
|
|
if (acl_db) {
|
|
ref_count++;
|
|
return true;
|
|
}
|
|
|
|
dbname = state_path("file_ntacls.tdb");
|
|
|
|
if (dbname == NULL) {
|
|
errno = ENOSYS;
|
|
return false;
|
|
}
|
|
|
|
become_root();
|
|
acl_db = db_open(NULL, dbname, 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600,
|
|
DBWRAP_LOCK_ORDER_1, DBWRAP_FLAG_NONE);
|
|
unbecome_root();
|
|
|
|
if (acl_db == NULL) {
|
|
#if defined(ENOTSUP)
|
|
errno = ENOTSUP;
|
|
#else
|
|
errno = ENOSYS;
|
|
#endif
|
|
TALLOC_FREE(dbname);
|
|
return false;
|
|
}
|
|
|
|
ref_count++;
|
|
TALLOC_FREE(dbname);
|
|
return true;
|
|
}
|
|
|
|
/*******************************************************************
|
|
Lower ref count and close acl_db if zero.
|
|
*******************************************************************/
|
|
|
|
static void disconnect_acl_tdb(struct vfs_handle_struct *handle)
|
|
{
|
|
SMB_VFS_NEXT_DISCONNECT(handle);
|
|
ref_count--;
|
|
if (ref_count == 0) {
|
|
TALLOC_FREE(acl_db);
|
|
}
|
|
}
|
|
|
|
/*******************************************************************
|
|
Fetch_lock the tdb acl record for a file
|
|
*******************************************************************/
|
|
|
|
static struct db_record *acl_tdb_lock(TALLOC_CTX *mem_ctx,
|
|
struct db_context *db,
|
|
const struct file_id *id)
|
|
{
|
|
uint8_t id_buf[16];
|
|
|
|
/* For backwards compatibility only store the dev/inode. */
|
|
push_file_id_16((char *)id_buf, id);
|
|
return dbwrap_fetch_locked(db,
|
|
mem_ctx,
|
|
make_tdb_data(id_buf,
|
|
sizeof(id_buf)));
|
|
}
|
|
|
|
/*******************************************************************
|
|
Delete the tdb acl record for a file
|
|
*******************************************************************/
|
|
|
|
static NTSTATUS acl_tdb_delete(vfs_handle_struct *handle,
|
|
struct db_context *db,
|
|
SMB_STRUCT_STAT *psbuf)
|
|
{
|
|
NTSTATUS status;
|
|
struct file_id id = vfs_file_id_from_sbuf(handle->conn, psbuf);
|
|
struct db_record *rec = acl_tdb_lock(talloc_tos(), db, &id);
|
|
|
|
/*
|
|
* If rec == NULL there's not much we can do about it
|
|
*/
|
|
|
|
if (rec == NULL) {
|
|
DEBUG(10,("acl_tdb_delete: rec == NULL\n"));
|
|
TALLOC_FREE(rec);
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
status = dbwrap_record_delete(rec);
|
|
TALLOC_FREE(rec);
|
|
return status;
|
|
}
|
|
|
|
/*******************************************************************
|
|
Pull a security descriptor into a DATA_BLOB from a tdb store.
|
|
*******************************************************************/
|
|
|
|
static NTSTATUS get_acl_blob(TALLOC_CTX *ctx,
|
|
vfs_handle_struct *handle,
|
|
files_struct *fsp,
|
|
const struct smb_filename *smb_fname,
|
|
DATA_BLOB *pblob)
|
|
{
|
|
uint8_t id_buf[16];
|
|
TDB_DATA data;
|
|
struct file_id id;
|
|
struct db_context *db = acl_db;
|
|
NTSTATUS status = NT_STATUS_OK;
|
|
SMB_STRUCT_STAT sbuf;
|
|
|
|
ZERO_STRUCT(sbuf);
|
|
|
|
if (fsp) {
|
|
status = vfs_stat_fsp(fsp);
|
|
sbuf = fsp->fsp_name->st;
|
|
} else {
|
|
int ret = vfs_stat_smb_basename(handle->conn,
|
|
smb_fname,
|
|
&sbuf);
|
|
if (ret == -1) {
|
|
status = map_nt_error_from_unix(errno);
|
|
}
|
|
}
|
|
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return status;
|
|
}
|
|
|
|
id = vfs_file_id_from_sbuf(handle->conn, &sbuf);
|
|
|
|
/* For backwards compatibility only store the dev/inode. */
|
|
push_file_id_16((char *)id_buf, &id);
|
|
|
|
status = dbwrap_fetch(db,
|
|
ctx,
|
|
make_tdb_data(id_buf, sizeof(id_buf)),
|
|
&data);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return NT_STATUS_INTERNAL_DB_CORRUPTION;
|
|
}
|
|
|
|
pblob->data = data.dptr;
|
|
pblob->length = data.dsize;
|
|
|
|
DEBUG(10,("get_acl_blob: returned %u bytes from file %s\n",
|
|
(unsigned int)data.dsize, smb_fname->base_name ));
|
|
|
|
if (pblob->length == 0 || pblob->data == NULL) {
|
|
return NT_STATUS_NOT_FOUND;
|
|
}
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
/*******************************************************************
|
|
Store a DATA_BLOB into a tdb record given an fsp pointer.
|
|
*******************************************************************/
|
|
|
|
static NTSTATUS store_acl_blob_fsp(vfs_handle_struct *handle,
|
|
files_struct *fsp,
|
|
DATA_BLOB *pblob)
|
|
{
|
|
uint8_t id_buf[16];
|
|
struct file_id id;
|
|
TDB_DATA data;
|
|
struct db_context *db = acl_db;
|
|
struct db_record *rec;
|
|
NTSTATUS status;
|
|
|
|
DEBUG(10,("store_acl_blob_fsp: storing blob length %u on file %s\n",
|
|
(unsigned int)pblob->length, fsp_str_dbg(fsp)));
|
|
|
|
status = vfs_stat_fsp(fsp);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return status;
|
|
}
|
|
|
|
id = vfs_file_id_from_sbuf(handle->conn, &fsp->fsp_name->st);
|
|
|
|
/* For backwards compatibility only store the dev/inode. */
|
|
push_file_id_16((char *)id_buf, &id);
|
|
rec = dbwrap_fetch_locked(db, talloc_tos(),
|
|
make_tdb_data(id_buf,
|
|
sizeof(id_buf)));
|
|
if (rec == NULL) {
|
|
DEBUG(0, ("store_acl_blob_fsp_tdb: fetch_lock failed\n"));
|
|
return NT_STATUS_INTERNAL_DB_CORRUPTION;
|
|
}
|
|
data.dptr = pblob->data;
|
|
data.dsize = pblob->length;
|
|
return dbwrap_record_store(rec, data, 0);
|
|
}
|
|
|
|
/*********************************************************************
|
|
On unlink we need to delete the tdb record (if using tdb).
|
|
*********************************************************************/
|
|
|
|
static int unlink_acl_tdb(vfs_handle_struct *handle,
|
|
const struct smb_filename *smb_fname)
|
|
{
|
|
struct smb_filename *smb_fname_tmp = NULL;
|
|
struct db_context *db = acl_db;
|
|
int ret = -1;
|
|
|
|
smb_fname_tmp = cp_smb_filename(talloc_tos(), smb_fname);
|
|
if (smb_fname_tmp == NULL) {
|
|
errno = ENOMEM;
|
|
goto out;
|
|
}
|
|
|
|
if (smb_fname_tmp->flags & SMB_FILENAME_POSIX_PATH) {
|
|
ret = SMB_VFS_LSTAT(handle->conn, smb_fname_tmp);
|
|
} else {
|
|
ret = SMB_VFS_STAT(handle->conn, smb_fname_tmp);
|
|
}
|
|
|
|
if (ret == -1) {
|
|
goto out;
|
|
}
|
|
|
|
ret = unlink_acl_common(handle, smb_fname_tmp);
|
|
|
|
if (ret == -1) {
|
|
goto out;
|
|
}
|
|
|
|
acl_tdb_delete(handle, db, &smb_fname_tmp->st);
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
/*********************************************************************
|
|
On rmdir we need to delete the tdb record (if using tdb).
|
|
*********************************************************************/
|
|
|
|
static int rmdir_acl_tdb(vfs_handle_struct *handle,
|
|
const struct smb_filename *smb_fname)
|
|
{
|
|
|
|
SMB_STRUCT_STAT sbuf;
|
|
struct db_context *db = acl_db;
|
|
int ret = -1;
|
|
|
|
ret = vfs_stat_smb_basename(handle->conn, smb_fname, &sbuf);
|
|
if (ret == -1) {
|
|
return -1;
|
|
}
|
|
|
|
ret = rmdir_acl_common(handle, smb_fname);
|
|
if (ret == -1) {
|
|
return -1;
|
|
}
|
|
|
|
acl_tdb_delete(handle, db, &sbuf);
|
|
return 0;
|
|
}
|
|
|
|
/*******************************************************************
|
|
Handle opening the storage tdb if so configured.
|
|
*******************************************************************/
|
|
|
|
static int connect_acl_tdb(struct vfs_handle_struct *handle,
|
|
const char *service,
|
|
const char *user)
|
|
{
|
|
int ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
|
|
bool ok;
|
|
struct acl_common_config *config = NULL;
|
|
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
|
|
if (!acl_tdb_init()) {
|
|
SMB_VFS_NEXT_DISCONNECT(handle);
|
|
return -1;
|
|
}
|
|
|
|
ok = init_acl_common_config(handle);
|
|
if (!ok) {
|
|
DBG_ERR("init_acl_common_config failed\n");
|
|
return -1;
|
|
}
|
|
|
|
/* Ensure we have the parameters correct if we're
|
|
* using this module. */
|
|
DEBUG(2,("connect_acl_tdb: setting 'inherit acls = true' "
|
|
"'dos filemode = true' and "
|
|
"'force unknown acl user = true' for service %s\n",
|
|
service ));
|
|
|
|
lp_do_parameter(SNUM(handle->conn), "inherit acls", "true");
|
|
lp_do_parameter(SNUM(handle->conn), "dos filemode", "true");
|
|
lp_do_parameter(SNUM(handle->conn), "force unknown acl user", "true");
|
|
|
|
SMB_VFS_HANDLE_GET_DATA(handle, config,
|
|
struct acl_common_config,
|
|
return -1);
|
|
|
|
if (config->ignore_system_acls) {
|
|
DBG_NOTICE("setting 'create mask = 0666', "
|
|
"'directory mask = 0777', "
|
|
"'store dos attributes = yes' and all "
|
|
"'map ...' options to 'no'\n");
|
|
|
|
lp_do_parameter(SNUM(handle->conn), "create mask", "0666");
|
|
lp_do_parameter(SNUM(handle->conn), "directory mask", "0777");
|
|
lp_do_parameter(SNUM(handle->conn), "map archive", "no");
|
|
lp_do_parameter(SNUM(handle->conn), "map hidden", "no");
|
|
lp_do_parameter(SNUM(handle->conn), "map readonly", "no");
|
|
lp_do_parameter(SNUM(handle->conn), "map system", "no");
|
|
lp_do_parameter(SNUM(handle->conn), "store dos attributes",
|
|
"yes");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*********************************************************************
|
|
Remove a Windows ACL - we're setting the underlying POSIX ACL.
|
|
*********************************************************************/
|
|
|
|
static int sys_acl_set_file_tdb(vfs_handle_struct *handle,
|
|
const char *path,
|
|
SMB_ACL_TYPE_T type,
|
|
SMB_ACL_T theacl)
|
|
{
|
|
struct db_context *db = acl_db;
|
|
int ret = -1;
|
|
struct smb_filename smb_fname = {
|
|
.base_name = discard_const_p(char, path)
|
|
};
|
|
|
|
ret = SMB_VFS_STAT(handle->conn, &smb_fname);
|
|
if (ret == -1) {
|
|
return -1;
|
|
}
|
|
|
|
ret = SMB_VFS_NEXT_SYS_ACL_SET_FILE(handle,
|
|
path,
|
|
type,
|
|
theacl);
|
|
if (ret == -1) {
|
|
return -1;
|
|
}
|
|
|
|
acl_tdb_delete(handle, db, &smb_fname.st);
|
|
return 0;
|
|
}
|
|
|
|
/*********************************************************************
|
|
Remove a Windows ACL - we're setting the underlying POSIX ACL.
|
|
*********************************************************************/
|
|
|
|
static int sys_acl_set_fd_tdb(vfs_handle_struct *handle,
|
|
files_struct *fsp,
|
|
SMB_ACL_T theacl)
|
|
{
|
|
struct db_context *db = acl_db;
|
|
NTSTATUS status;
|
|
int ret;
|
|
|
|
status = vfs_stat_fsp(fsp);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return -1;
|
|
}
|
|
|
|
ret = SMB_VFS_NEXT_SYS_ACL_SET_FD(handle,
|
|
fsp,
|
|
theacl);
|
|
if (ret == -1) {
|
|
return -1;
|
|
}
|
|
|
|
acl_tdb_delete(handle, db, &fsp->fsp_name->st);
|
|
return 0;
|
|
}
|
|
|
|
static struct vfs_fn_pointers vfs_acl_tdb_fns = {
|
|
.connect_fn = connect_acl_tdb,
|
|
.disconnect_fn = disconnect_acl_tdb,
|
|
.rmdir_fn = rmdir_acl_tdb,
|
|
.unlink_fn = unlink_acl_tdb,
|
|
.chmod_fn = chmod_acl_module_common,
|
|
.fchmod_fn = fchmod_acl_module_common,
|
|
.fget_nt_acl_fn = fget_nt_acl_common,
|
|
.get_nt_acl_fn = get_nt_acl_common,
|
|
.fset_nt_acl_fn = fset_nt_acl_common,
|
|
.chmod_acl_fn = chmod_acl_acl_module_common,
|
|
.fchmod_acl_fn = fchmod_acl_acl_module_common,
|
|
.sys_acl_set_file_fn = sys_acl_set_file_tdb,
|
|
.sys_acl_set_fd_fn = sys_acl_set_fd_tdb
|
|
};
|
|
|
|
static_decl_vfs;
|
|
NTSTATUS vfs_acl_tdb_init(void)
|
|
{
|
|
return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "acl_tdb",
|
|
&vfs_acl_tdb_fns);
|
|
}
|