#include "../libcli/security/security.h"
#include "../librpc/gen_ndr/ndr_security.h"
#include "../lib/util/bitmap.h"
+#include "passdb/lookup_sid.h"
static NTSTATUS create_acl_blob(const struct security_descriptor *psd,
DATA_BLOB *pblob,
static NTSTATUS get_acl_blob(TALLOC_CTX *ctx,
vfs_handle_struct *handle,
files_struct *fsp,
- const char *name,
+ const struct smb_filename *smb_fname,
DATA_BLOB *pblob);
static NTSTATUS store_acl_blob_fsp(vfs_handle_struct *handle,
return NT_STATUS_OK;
}
+static NTSTATUS make_default_filesystem_acl(TALLOC_CTX *ctx,
+ const char *name,
+ SMB_STRUCT_STAT *psbuf,
+ struct security_descriptor **ppdesc)
+{
+ struct dom_sid owner_sid, group_sid;
+ size_t size = 0;
+ struct security_ace aces[4];
+ uint32_t access_mask = 0;
+ mode_t mode = psbuf->st_ex_mode;
+ struct security_acl *new_dacl = NULL;
+ int idx = 0;
+
+ DEBUG(10,("make_default_filesystem_acl: file %s mode = 0%o\n",
+ name, (int)mode ));
+
+ uid_to_sid(&owner_sid, psbuf->st_ex_uid);
+ gid_to_sid(&group_sid, psbuf->st_ex_gid);
+
+ /*
+ We provide up to 4 ACEs
+ - Owner
+ - Group
+ - Everyone
+ - NT System
+ */
+
+ if (mode & S_IRUSR) {
+ if (mode & S_IWUSR) {
+ access_mask |= SEC_RIGHTS_FILE_ALL;
+ } else {
+ access_mask |= SEC_RIGHTS_FILE_READ | SEC_FILE_EXECUTE;
+ }
+ }
+ if (mode & S_IWUSR) {
+ access_mask |= SEC_RIGHTS_FILE_WRITE | SEC_STD_DELETE;
+ }
+
+ init_sec_ace(&aces[idx],
+ &owner_sid,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ access_mask,
+ 0);
+ idx++;
+
+ access_mask = 0;
+ if (mode & S_IRGRP) {
+ access_mask |= SEC_RIGHTS_FILE_READ | SEC_FILE_EXECUTE;
+ }
+ if (mode & S_IWGRP) {
+ /* note that delete is not granted - this matches posix behaviour */
+ access_mask |= SEC_RIGHTS_FILE_WRITE;
+ }
+ if (access_mask) {
+ init_sec_ace(&aces[idx],
+ &group_sid,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ access_mask,
+ 0);
+ idx++;
+ }
+
+ access_mask = 0;
+ if (mode & S_IROTH) {
+ access_mask |= SEC_RIGHTS_FILE_READ | SEC_FILE_EXECUTE;
+ }
+ if (mode & S_IWOTH) {
+ access_mask |= SEC_RIGHTS_FILE_WRITE;
+ }
+ if (access_mask) {
+ init_sec_ace(&aces[idx],
+ &global_sid_World,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ access_mask,
+ 0);
+ idx++;
+ }
+
+ init_sec_ace(&aces[idx],
+ &global_sid_System,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ SEC_RIGHTS_FILE_ALL,
+ 0);
+ idx++;
+
+ new_dacl = make_sec_acl(ctx,
+ NT4_ACL_REVISION,
+ idx,
+ aces);
+
+ if (!new_dacl) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ *ppdesc = make_sec_desc(ctx,
+ SECURITY_DESCRIPTOR_REVISION_1,
+ SEC_DESC_SELF_RELATIVE|SEC_DESC_DACL_PRESENT,
+ &owner_sid,
+ &group_sid,
+ NULL,
+ new_dacl,
+ &size);
+ if (!*ppdesc) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ return NT_STATUS_OK;
+}
+
/*******************************************************************
Pull a DATA_BLOB from an xattr given a pathname.
If the hash doesn't match, or doesn't exist - return the underlying
static NTSTATUS get_nt_acl_internal(vfs_handle_struct *handle,
files_struct *fsp,
- const struct smb_filename *smb_fname,
+ const struct smb_filename *smb_fname_in,
uint32_t security_info,
TALLOC_CTX *mem_ctx,
struct security_descriptor **ppdesc)
uint8_t hash_tmp[XATTR_SD_HASH_SIZE];
uint8_t sys_acl_hash_tmp[XATTR_SD_HASH_SIZE];
struct security_descriptor *psd = NULL;
- struct security_descriptor *pdesc_next = NULL;
- const char *name = NULL;
+ struct security_descriptor *psd_blob = NULL;
+ struct security_descriptor *psd_fs = NULL;
+ const struct smb_filename *smb_fname = NULL;
bool ignore_file_system_acl = lp_parm_bool(SNUM(handle->conn),
ACL_MODULE_NAME,
"ignore system acls",
false);
- TALLOC_CTX *frame = talloc_stackframe();
+ char *sys_acl_blob_description = NULL;
+ DATA_BLOB sys_acl_blob = { 0 };
+ bool psd_is_from_fs = false;
- if (fsp && smb_fname == NULL) {
- name = fsp->fsp_name->base_name;
+ if (fsp && smb_fname_in == NULL) {
+ smb_fname = fsp->fsp_name;
} else {
- name = smb_fname->base_name;
+ smb_fname = smb_fname_in;
}
- DEBUG(10, ("get_nt_acl_internal: name=%s\n", name));
+ DEBUG(10, ("get_nt_acl_internal: name=%s\n", smb_fname->base_name));
- status = get_acl_blob(frame, handle, fsp, name, &blob);
+ status = get_acl_blob(mem_ctx, handle, fsp, smb_fname, &blob);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(10, ("get_nt_acl_internal: get_acl_blob returned %s\n",
nt_errstr(status)));
- psd = NULL;
goto out;
} else {
- status = parse_acl_blob(&blob, mem_ctx, &psd,
+ status = parse_acl_blob(&blob, mem_ctx, &psd_blob,
&hash_type, &xattr_version, &hash[0], &sys_acl_hash[0]);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(10, ("parse_acl_blob returned %s\n",
nt_errstr(status)));
- psd = NULL;
+ TALLOC_FREE(blob.data);
goto out;
}
}
- /* Ensure we don't leak psd if we don't choose it.
- *
- * We don't allocate it onto frame as it is preferred not to
- * steal from a talloc pool.
- */
- talloc_steal(frame, psd);
+ TALLOC_FREE(blob.data);
/* determine which type of xattr we got */
switch (xattr_version) {
* require confirmation of the hash. In particular,
* the NTVFS file server uses version 1, but
* 'samba-tool ntacl' can set these as well */
+ psd = psd_blob;
+ psd_blob = NULL;
goto out;
case 3:
case 4:
if (ignore_file_system_acl) {
+ psd = psd_blob;
+ psd_blob = NULL;
goto out;
}
DEBUG(10, ("get_nt_acl_internal: ACL blob revision "
"mismatch (%u) for file %s\n",
(unsigned int)hash_type,
- name));
- TALLOC_FREE(psd);
- psd = NULL;
+ smb_fname->base_name));
+ TALLOC_FREE(psd_blob);
goto out;
}
DEBUG(10, ("get_nt_acl_internal: ACL blob hash type "
"(%u) unexpected for file %s\n",
(unsigned int)hash_type,
- name));
- TALLOC_FREE(psd);
- psd = NULL;
+ smb_fname->base_name));
+ TALLOC_FREE(psd_blob);
goto out;
}
case 4:
{
int ret;
- char *sys_acl_blob_description;
- DATA_BLOB sys_acl_blob;
if (fsp) {
/* Get the full underlying sd, then hash. */
ret = SMB_VFS_NEXT_SYS_ACL_BLOB_GET_FD(handle,
fsp,
- frame,
+ mem_ctx,
&sys_acl_blob_description,
&sys_acl_blob);
} else {
/* Get the full underlying sd, then hash. */
ret = SMB_VFS_NEXT_SYS_ACL_BLOB_GET_FILE(handle,
- name,
- frame,
- &sys_acl_blob_description,
- &sys_acl_blob);
+ smb_fname->base_name,
+ mem_ctx,
+ &sys_acl_blob_description,
+ &sys_acl_blob);
}
/* If we fail to get the ACL blob (for some reason) then this
if (ret == 0) {
status = hash_blob_sha256(sys_acl_blob, sys_acl_hash_tmp);
if (!NT_STATUS_IS_OK(status)) {
- TALLOC_FREE(frame);
- return status;
+ goto fail;
}
+ TALLOC_FREE(sys_acl_blob_description);
+ TALLOC_FREE(sys_acl_blob.data);
+
if (memcmp(&sys_acl_hash[0], &sys_acl_hash_tmp[0],
XATTR_SD_HASH_SIZE) == 0) {
/* Hash matches, return blob sd. */
DEBUG(10, ("get_nt_acl_internal: blob hash "
"matches for file %s\n",
- name ));
+ smb_fname->base_name ));
+ psd = psd_blob;
+ psd_blob = NULL;
goto out;
}
}
fsp,
HASH_SECURITY_INFO,
mem_ctx,
- &pdesc_next);
+ &psd_fs);
} else {
status = SMB_VFS_NEXT_GET_NT_ACL(handle,
smb_fname,
HASH_SECURITY_INFO,
mem_ctx,
- &pdesc_next);
+ &psd_fs);
}
if (!NT_STATUS_IS_OK(status)) {
DEBUG(10, ("get_nt_acl_internal: get_next_acl for file %s "
"returned %s\n",
- name,
+ smb_fname->base_name,
nt_errstr(status)));
- TALLOC_FREE(frame);
- return status;
+ goto fail;
}
- /* Ensure we don't leak psd_next if we don't choose it.
- *
- * We don't allocate it onto frame as it is preferred not to
- * steal from a talloc pool.
- */
- talloc_steal(frame, pdesc_next);
-
- status = hash_sd_sha256(pdesc_next, hash_tmp);
+ status = hash_sd_sha256(psd_fs, hash_tmp);
if (!NT_STATUS_IS_OK(status)) {
- TALLOC_FREE(psd);
- psd = pdesc_next;
+ TALLOC_FREE(psd_blob);
+ psd = psd_fs;
+ psd_fs = NULL;
+ psd_is_from_fs = true;
goto out;
}
/* Hash matches, return blob sd. */
DEBUG(10, ("get_nt_acl_internal: blob hash "
"matches for file %s\n",
- name ));
+ smb_fname->base_name ));
+ psd = psd_blob;
+ psd_blob = NULL;
goto out;
}
DEBUG(10, ("get_nt_acl_internal: blob hash "
"does not match for file %s - returning "
"file system SD mapping.\n",
- name ));
+ smb_fname->base_name ));
if (DEBUGLEVEL >= 10) {
DEBUG(10,("get_nt_acl_internal: acl for blob hash for %s is:\n",
- name ));
- NDR_PRINT_DEBUG(security_descriptor, pdesc_next);
+ smb_fname->base_name ));
+ NDR_PRINT_DEBUG(security_descriptor, psd_fs);
}
- TALLOC_FREE(psd);
- psd = pdesc_next;
+ TALLOC_FREE(psd_blob);
+ psd = psd_fs;
+ psd_fs = NULL;
+ psd_is_from_fs = true;
}
- out:
+out:
if (psd == NULL) {
/* Get the full underlying sd, as we failed to get the
* blob for the hash, or the revision/hash type wasn't
fsp,
security_info,
mem_ctx,
- &pdesc_next);
+ &psd);
} else {
status = SMB_VFS_NEXT_GET_NT_ACL(handle,
smb_fname,
security_info,
mem_ctx,
- &pdesc_next);
+ &psd);
}
if (!NT_STATUS_IS_OK(status)) {
DEBUG(10, ("get_nt_acl_internal: get_next_acl for file %s "
"returned %s\n",
- name,
+ smb_fname->base_name,
nt_errstr(status)));
- TALLOC_FREE(frame);
- return status;
+ goto fail;
}
- /* Ensure we don't leak psd_next if we don't choose it.
- *
- * We don't allocate it onto frame as it is preferred not to
- * steal from a talloc pool.
- */
- talloc_steal(frame, pdesc_next);
- psd = pdesc_next;
+ psd_is_from_fs = true;
}
- if (psd != pdesc_next) {
- /* We're returning the blob, throw
- * away the filesystem SD. */
- TALLOC_FREE(pdesc_next);
- } else {
+ if (psd_is_from_fs) {
SMB_STRUCT_STAT sbuf;
SMB_STRUCT_STAT *psbuf = &sbuf;
bool is_directory = false;
if (fsp) {
status = vfs_stat_fsp(fsp);
if (!NT_STATUS_IS_OK(status)) {
- TALLOC_FREE(frame);
- return status;
+ goto fail;
}
psbuf = &fsp->fsp_name->st;
} else {
* is fully plumbed through the VFS.
*/
int ret = vfs_stat_smb_basename(handle->conn,
- name,
+ smb_fname,
&sbuf);
if (ret == -1) {
- TALLOC_FREE(frame);
- return map_nt_error_from_unix(errno);
+ status = map_nt_error_from_unix(errno);
+ goto fail;
}
}
is_directory = S_ISDIR(psbuf->st_ex_mode);
if (ignore_file_system_acl) {
- TALLOC_FREE(pdesc_next);
+ TALLOC_FREE(psd);
status = make_default_filesystem_acl(mem_ctx,
- name,
+ smb_fname->base_name,
psbuf,
&psd);
if (!NT_STATUS_IS_OK(status)) {
- TALLOC_FREE(frame);
- return status;
+ goto fail;
}
} else {
if (is_directory &&
true)) {
status = add_directory_inheritable_components(
handle,
- name,
+ smb_fname->base_name,
psbuf,
psd);
if (!NT_STATUS_IS_OK(status)) {
- TALLOC_FREE(frame);
- return status;
+ goto fail;
}
}
/* The underlying POSIX module always sets
psd->sacl = NULL;
}
- TALLOC_FREE(blob.data);
-
if (DEBUGLEVEL >= 10) {
DEBUG(10,("get_nt_acl_internal: returning acl for %s is:\n",
- name ));
+ smb_fname->base_name ));
NDR_PRINT_DEBUG(security_descriptor, psd);
}
- /* The VFS API is that the ACL is expected to be on mem_ctx */
- *ppdesc = talloc_move(mem_ctx, &psd);
+ *ppdesc = psd;
- TALLOC_FREE(frame);
return NT_STATUS_OK;
+
+fail:
+ TALLOC_FREE(psd);
+ TALLOC_FREE(psd_blob);
+ TALLOC_FREE(psd_fs);
+ TALLOC_FREE(sys_acl_blob_description);
+ TALLOC_FREE(sys_acl_blob.data);
+ return status;
}
/*********************************************************************
ppdesc);
}
+/*********************************************************************
+ Set the underlying ACL (e.g. POSIX ACLS, POSIX owner, etc)
+*********************************************************************/
+static NTSTATUS set_underlying_acl(vfs_handle_struct *handle, files_struct *fsp,
+ struct security_descriptor *psd,
+ uint32_t security_info_sent,
+ bool chown_needed)
+{
+ NTSTATUS status =
+ SMB_VFS_NEXT_FSET_NT_ACL(handle, fsp, security_info_sent, psd);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+ return status;
+ }
+
+ /* We got access denied here. If we're already root,
+ or we didn't need to do a chown, or the fsp isn't
+ open with WRITE_OWNER access, just return. */
+ if (get_current_uid(handle->conn) == 0 || chown_needed == false ||
+ !(fsp->access_mask & SEC_STD_WRITE_OWNER)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ DEBUG(10, ("fset_nt_acl_common: overriding chown on file %s "
+ "for sid %s\n",
+ fsp_str_dbg(fsp), sid_string_tos(psd->owner_sid)));
+
+ /* Ok, we failed to chown and we have
+ SEC_STD_WRITE_OWNER access - override. */
+ become_root();
+ status = SMB_VFS_NEXT_FSET_NT_ACL(handle, fsp, security_info_sent, psd);
+ unbecome_root();
+
+ return status;
+}
+
+/*********************************************************************
+ Store a v3 security descriptor
+*********************************************************************/
+static NTSTATUS store_v3_blob(vfs_handle_struct *handle, files_struct *fsp,
+ struct security_descriptor *psd,
+ struct security_descriptor *pdesc_next,
+ uint8_t hash[XATTR_SD_HASH_SIZE])
+{
+ NTSTATUS status;
+ DATA_BLOB blob;
+
+ if (DEBUGLEVEL >= 10) {
+ DEBUG(10, ("fset_nt_acl_xattr: storing xattr sd for file %s\n",
+ fsp_str_dbg(fsp)));
+ NDR_PRINT_DEBUG(
+ security_descriptor,
+ discard_const_p(struct security_descriptor, psd));
+
+ if (pdesc_next != NULL) {
+ DEBUG(10, ("fset_nt_acl_xattr: storing has in xattr sd "
+ "based on \n"));
+ NDR_PRINT_DEBUG(
+ security_descriptor,
+ discard_const_p(struct security_descriptor,
+ pdesc_next));
+ } else {
+ DEBUG(10,
+ ("fset_nt_acl_xattr: ignoring underlying sd\n"));
+ }
+ }
+ status = create_acl_blob(psd, &blob, XATTR_SD_HASH_TYPE_SHA256, hash);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10, ("fset_nt_acl_xattr: create_acl_blob failed\n"));
+ return status;
+ }
+
+ status = store_acl_blob_fsp(handle, fsp, &blob);
+ return status;
+}
+
/*********************************************************************
Store a security descriptor given an fsp.
*********************************************************************/
bool chown_needed = false;
char *sys_acl_description;
TALLOC_CTX *frame = talloc_stackframe();
+ bool ignore_file_system_acl = lp_parm_bool(
+ SNUM(handle->conn), ACL_MODULE_NAME, "ignore system acls", false);
if (DEBUGLEVEL >= 10) {
DEBUG(10,("fset_nt_acl_xattr: incoming sd for file %s\n",
psd->type |= SEC_DESC_SACL_PRESENT;
}
- status = SMB_VFS_NEXT_FSET_NT_ACL(handle, fsp, security_info_sent, psd);
- if (!NT_STATUS_IS_OK(status)) {
- if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
- TALLOC_FREE(frame);
- return status;
- }
- /* We got access denied here. If we're already root,
- or we didn't need to do a chown, or the fsp isn't
- open with WRITE_OWNER access, just return. */
- if (get_current_uid(handle->conn) == 0 ||
- chown_needed == false ||
- !(fsp->access_mask & SEC_STD_WRITE_OWNER)) {
- TALLOC_FREE(frame);
- return NT_STATUS_ACCESS_DENIED;
+ if (ignore_file_system_acl) {
+ if (chown_needed) {
+ /* send only ownership stuff to lower layer */
+ security_info_sent &= (SECINFO_OWNER | SECINFO_GROUP);
+ status = set_underlying_acl(handle, fsp, psd,
+ security_info_sent, true);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ return status;
+ }
}
+ ZERO_ARRAY(hash);
+ status = store_v3_blob(handle, fsp, psd, NULL, hash);
- DEBUG(10,("fset_nt_acl_common: overriding chown on file %s "
- "for sid %s\n",
- fsp_str_dbg(fsp),
- sid_string_tos(psd->owner_sid)
- ));
-
- /* Ok, we failed to chown and we have
- SEC_STD_WRITE_OWNER access - override. */
- become_root();
- status = SMB_VFS_NEXT_FSET_NT_ACL(handle, fsp,
- security_info_sent, psd);
- unbecome_root();
- if (!NT_STATUS_IS_OK(status)) {
- TALLOC_FREE(frame);
- return status;
- }
+ TALLOC_FREE(frame);
+ return status;
+ }
+
+ status = set_underlying_acl(handle, fsp, psd, security_info_sent,
+ chown_needed);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ return status;
}
/* Get the full underlying sd, then hash. */
/* If we fail to get the ACL blob (for some reason) then this
* is not fatal, we just work based on the NT ACL only */
if (ret != 0) {
- if (DEBUGLEVEL >= 10) {
- DEBUG(10,("fset_nt_acl_xattr: storing xattr sd for file %s\n",
- fsp_str_dbg(fsp)));
- NDR_PRINT_DEBUG(security_descriptor,
- discard_const_p(struct security_descriptor, psd));
-
- DEBUG(10,("fset_nt_acl_xattr: storing has in xattr sd based on \n"));
- NDR_PRINT_DEBUG(security_descriptor,
- discard_const_p(struct security_descriptor, pdesc_next));
- }
- status = create_acl_blob(psd, &blob, XATTR_SD_HASH_TYPE_SHA256, hash);
- if (!NT_STATUS_IS_OK(status)) {
- DEBUG(10, ("fset_nt_acl_xattr: create_acl_blob failed\n"));
- TALLOC_FREE(frame);
- return status;
- }
-
- status = store_acl_blob_fsp(handle, fsp, &blob);
+ status = store_v3_blob(handle, fsp, psd, pdesc_next, hash);
TALLOC_FREE(frame);
return status;
become_root();
if (is_directory) {
- ret = SMB_VFS_NEXT_RMDIR(handle, final_component);
+ ret = SMB_VFS_NEXT_RMDIR(handle, &local_fname);
} else {
ret = SMB_VFS_NEXT_UNLINK(handle, &local_fname);
}
}
static int rmdir_acl_common(struct vfs_handle_struct *handle,
- const char *path)
+ const struct smb_filename *smb_fname)
{
int ret;
/* Try the normal rmdir first. */
- ret = SMB_VFS_NEXT_RMDIR(handle, path);
+ ret = SMB_VFS_NEXT_RMDIR(handle, smb_fname);
if (ret == 0) {
return 0;
}
/* Failed due to access denied,
see if we need to root override. */
return acl_common_remove_object(handle,
- path,
+ smb_fname->base_name,
true);
}
DEBUG(10,("rmdir_acl_common: unlink of %s failed %s\n",
- path,
+ smb_fname->base_name,
strerror(errno) ));
return -1;
}
}
static int chmod_acl_module_common(struct vfs_handle_struct *handle,
- const char *path, mode_t mode)
+ const struct smb_filename *smb_fname,
+ mode_t mode)
{
- if (lp_posix_pathnames()) {
+ if (smb_fname->flags & SMB_FILENAME_POSIX_PATH) {
/* Only allow this on POSIX pathnames. */
- return SMB_VFS_NEXT_CHMOD(handle, path, mode);
+ return SMB_VFS_NEXT_CHMOD(handle, smb_fname, mode);
}
return 0;
}
}
static int chmod_acl_acl_module_common(struct vfs_handle_struct *handle,
- const char *name, mode_t mode)
+ const struct smb_filename *smb_fname,
+ mode_t mode)
{
- if (lp_posix_pathnames()) {
+ if (smb_fname->flags & SMB_FILENAME_POSIX_PATH) {
/* Only allow this on POSIX pathnames. */
- return SMB_VFS_NEXT_CHMOD_ACL(handle, name, mode);
+ return SMB_VFS_NEXT_CHMOD_ACL(handle, smb_fname, mode);
}
return 0;
}