summaryrefslogtreecommitdiff
path: root/source4/ntvfs/posix
diff options
context:
space:
mode:
Diffstat (limited to 'source4/ntvfs/posix')
-rw-r--r--source4/ntvfs/posix/pvfs_acl.c164
-rw-r--r--source4/ntvfs/posix/pvfs_open.c116
-rw-r--r--source4/ntvfs/posix/pvfs_resolve.c51
3 files changed, 287 insertions, 44 deletions
diff --git a/source4/ntvfs/posix/pvfs_acl.c b/source4/ntvfs/posix/pvfs_acl.c
index 970cddf6f7..c2309b92ea 100644
--- a/source4/ntvfs/posix/pvfs_acl.c
+++ b/source4/ntvfs/posix/pvfs_acl.c
@@ -106,7 +106,11 @@ static NTSTATUS pvfs_default_acl(struct pvfs_state *pvfs,
ace.access_mask = 0;
if (mode & S_IRUSR) {
- ace.access_mask |= SEC_RIGHTS_FILE_READ | SEC_FILE_EXECUTE;
+ if (mode & S_IWUSR) {
+ ace.access_mask |= SEC_RIGHTS_FILE_ALL;
+ } else {
+ ace.access_mask |= SEC_RIGHTS_FILE_READ | SEC_FILE_EXECUTE;
+ }
}
if (mode & S_IWUSR) {
ace.access_mask |= SEC_RIGHTS_FILE_WRITE | SEC_STD_DELETE;
@@ -123,6 +127,7 @@ static NTSTATUS pvfs_default_acl(struct pvfs_state *pvfs,
ace.access_mask |= SEC_RIGHTS_FILE_READ | SEC_FILE_EXECUTE;
}
if (mode & S_IWGRP) {
+ /* note that delete is not granted - this matches posix behaviour */
ace.access_mask |= SEC_RIGHTS_FILE_WRITE;
}
if (ace.access_mask) {
@@ -368,3 +373,160 @@ NTSTATUS pvfs_access_check_simple(struct pvfs_state *pvfs,
{
return pvfs_access_check(pvfs, req, name, &access_needed);
}
+
+
+/*
+ determine if an ACE is inheritable
+*/
+static BOOL pvfs_inheritable_ace(struct pvfs_state *pvfs,
+ const struct security_ace *ace,
+ BOOL container)
+{
+ if (!container) {
+ return (ace->flags & SEC_ACE_FLAG_OBJECT_INHERIT) != 0;
+ }
+
+ if (ace->flags & SEC_ACE_FLAG_CONTAINER_INHERIT) {
+ return True;
+ }
+
+ if ((ace->flags & SEC_ACE_FLAG_OBJECT_INHERIT) &&
+ !(ace->flags & SEC_ACE_FLAG_NO_PROPAGATE_INHERIT)) {
+ return True;
+ }
+
+ return False;
+}
+
+/*
+ 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,
+ BOOL container)
+{
+ int i;
+
+ for (i=0;i<parent_sd->dacl->num_aces;i++) {
+ struct security_ace ace = parent_sd->dacl->aces[i];
+ NTSTATUS status;
+
+ if (!pvfs_inheritable_ace(pvfs, &ace, container)) {
+ continue;
+ }
+
+ /* 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;
+ }
+ }
+
+ status = security_descriptor_dacl_add(sd, &ace);
+ 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,
+ struct smbsrv_request *req,
+ struct pvfs_filename *name,
+ int fd)
+{
+ struct xattr_NTACL *acl;
+ NTSTATUS status;
+ struct pvfs_filename *parent;
+ struct security_descriptor *parent_sd, *sd;
+ BOOL container;
+
+ /* form the parents path */
+ status = pvfs_resolve_parent(pvfs, req, name, &parent);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ acl = talloc_p(req, struct xattr_NTACL);
+ 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;
+ }
+
+ status = sidmap_uid_to_sid(pvfs->sidmap, sd, name->st.st_uid, &sd->owner_sid);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ status = sidmap_gid_to_sid(pvfs->sidmap, sd, name->st.st_gid, &sd->group_sid);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ sd->type |= SEC_DESC_DACL_PRESENT;
+
+ container = (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) ? True:False;
+
+ /* 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;
+}
diff --git a/source4/ntvfs/posix/pvfs_open.c b/source4/ntvfs/posix/pvfs_open.c
index 34052fc44a..1695d8e1d9 100644
--- a/source4/ntvfs/posix/pvfs_open.c
+++ b/source4/ntvfs/posix/pvfs_open.c
@@ -95,6 +95,43 @@ static int pvfs_dir_fnum_destructor(void *p)
return 0;
}
+/*
+ setup any EAs and the ACL on newly created files/directories
+*/
+static NTSTATUS pvfs_open_setup_eas_acl(struct pvfs_state *pvfs,
+ struct smbsrv_request *req,
+ struct pvfs_filename *name,
+ int fd, int fnum,
+ union smb_open *io)
+{
+ NTSTATUS status;
+
+ /* setup any EAs that were asked for */
+ if (io->ntcreatex.in.ea_list) {
+ status = pvfs_setfileinfo_ea_set(pvfs, name, fd,
+ io->ntcreatex.in.ea_list->num_eas,
+ io->ntcreatex.in.ea_list->eas);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+
+ /* setup an initial sec_desc if requested */
+ if (io->ntcreatex.in.sec_desc) {
+ union smb_setfileinfo set;
+
+ set.set_secdesc.file.fnum = fnum;
+ set.set_secdesc.in.secinfo_flags = SECINFO_DACL;
+ set.set_secdesc.in.sd = io->ntcreatex.in.sec_desc;
+
+ status = pvfs_acl_set(pvfs, req, name, fd, &set);
+ } else {
+ /* otherwise setup an inherited acl from the parent */
+ status = pvfs_acl_inherit(pvfs, req, name, fd);
+ }
+
+ return status;
+}
/*
open a directory
@@ -162,6 +199,7 @@ static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs,
/* check the security descriptor */
status = pvfs_access_check(pvfs, req, name, &access_mask);
if (!NT_STATUS_IS_OK(status)) {
+ idr_remove(pvfs->idtree_fnum, fnum);
return status;
}
}
@@ -200,6 +238,7 @@ static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs,
uint32_t attrib = io->generic.in.file_attr | FILE_ATTRIBUTE_DIRECTORY;
mode_t mode = pvfs_fileperms(pvfs, attrib);
if (mkdir(name->full_name, mode) == -1) {
+ idr_remove(pvfs->idtree_fnum, fnum);
return pvfs_map_errno(pvfs,errno);
}
@@ -207,14 +246,21 @@ static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs,
status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname, 0, &name);
if (!NT_STATUS_IS_OK(status)) {
- return status;
+ goto cleanup_delete;
}
+
+ status = pvfs_open_setup_eas_acl(pvfs, req, name, -1, fnum, io);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto cleanup_delete;
+ }
+
create_action = NTCREATEX_ACTION_CREATED;
} else {
create_action = NTCREATEX_ACTION_EXISTED;
}
if (!name->exists) {
+ idr_remove(pvfs->idtree_fnum, fnum);
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
}
@@ -236,6 +282,11 @@ static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs,
io->generic.out.is_directory = 1;
return NT_STATUS_OK;
+
+cleanup_delete:
+ idr_remove(pvfs->idtree_fnum, fnum);
+ rmdir(name->full_name);
+ return status;
}
/*
@@ -462,53 +513,25 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
name->dos.attrib = attrib;
status = pvfs_dosattrib_save(pvfs, name, fd);
if (!NT_STATUS_IS_OK(status)) {
- idr_remove(pvfs->idtree_fnum, fnum);
- close(fd);
- return status;
+ goto cleanup_delete;
}
- /* setup any EAs that were asked for */
- if (io->ntcreatex.in.ea_list) {
- status = pvfs_setfileinfo_ea_set(pvfs, name, fd,
- io->ntcreatex.in.ea_list->num_eas,
- io->ntcreatex.in.ea_list->eas);
- if (!NT_STATUS_IS_OK(status)) {
- idr_remove(pvfs->idtree_fnum, fnum);
- close(fd);
- return status;
- }
- }
-
- /* setup an initial sec_desc is required */
- if (io->ntcreatex.in.sec_desc) {
- union smb_setfileinfo set;
-
- set.set_secdesc.file.fnum = fnum;
- set.set_secdesc.in.secinfo_flags = SECINFO_DACL;
- set.set_secdesc.in.sd = io->ntcreatex.in.sec_desc;
- status = pvfs_acl_set(pvfs, req, name, fd, &set);
- if (!NT_STATUS_IS_OK(status)) {
- idr_remove(pvfs->idtree_fnum, fnum);
- close(fd);
- return status;
- }
+ status = pvfs_open_setup_eas_acl(pvfs, req, name, fd, fnum, io);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto cleanup_delete;
}
/* form the lock context used for byte range locking and
opendb locking */
status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
if (!NT_STATUS_IS_OK(status)) {
- idr_remove(pvfs->idtree_fnum, fnum);
- close(fd);
- return status;
+ goto cleanup_delete;
}
status = pvfs_brl_locking_key(name, f->handle, &f->handle->brl_locking_key);
if (!NT_STATUS_IS_OK(status)) {
- idr_remove(pvfs->idtree_fnum, fnum);
- close(fd);
- return status;
+ goto cleanup_delete;
}
/* grab a lock on the open file record */
@@ -518,16 +541,17 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
name->full_name));
/* we were supposed to do a blocking lock, so something
is badly wrong! */
- idr_remove(pvfs->idtree_fnum, fnum);
- close(fd);
- return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ status = NT_STATUS_INTERNAL_DB_CORRUPTION;
+ goto cleanup_delete;
}
status = odb_open_file(lck, f->handle, name->stream_id,
share_access, create_options, access_mask);
talloc_free(lck);
if (!NT_STATUS_IS_OK(status)) {
- /* bad news, we must have hit a race */
+ /* bad news, we must have hit a race - we don't delete the file
+ here as the most likely scenario is that someone else created
+ the file at the same time */
idr_remove(pvfs->idtree_fnum, fnum);
close(fd);
return status;
@@ -583,6 +607,12 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
talloc_steal(pvfs, f);
return NT_STATUS_OK;
+
+cleanup_delete:
+ idr_remove(pvfs->idtree_fnum, fnum);
+ close(fd);
+ unlink(name->full_name);
+ return status;
}
@@ -846,6 +876,10 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
return pvfs_open_directory(pvfs, req, name, io);
}
+ /* FILE_ATTRIBUTE_DIRECTORY is ignored if the above test for directory
+ open doesn't match */
+ io->generic.in.file_attr &= ~FILE_ATTRIBUTE_DIRECTORY;
+
create_options = io->generic.in.create_options;
share_access = io->generic.in.share_access;
access_mask = io->generic.in.access_mask;
@@ -894,10 +928,6 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
return NT_STATUS_INVALID_PARAMETER;
}
- if (io->generic.in.file_attr & FILE_ATTRIBUTE_DIRECTORY) {
- return NT_STATUS_INVALID_PARAMETER;
- }
-
/* handle creating a new file separately */
if (!name->exists) {
status = pvfs_create_file(pvfs, req, name, io);
diff --git a/source4/ntvfs/posix/pvfs_resolve.c b/source4/ntvfs/posix/pvfs_resolve.c
index 7e7f49d0af..4ad3476795 100644
--- a/source4/ntvfs/posix/pvfs_resolve.c
+++ b/source4/ntvfs/posix/pvfs_resolve.c
@@ -579,3 +579,54 @@ NTSTATUS pvfs_resolve_name_fd(struct pvfs_state *pvfs, int fd,
return pvfs_fill_dos_info(pvfs, name, fd);
}
+
+
+/*
+ resolve the parent of a given name
+*/
+NTSTATUS pvfs_resolve_parent(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
+ const struct pvfs_filename *child,
+ struct pvfs_filename **name)
+{
+ NTSTATUS status;
+ char *p;
+
+ *name = talloc_p(mem_ctx, struct pvfs_filename);
+ if (*name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*name)->full_name = talloc_strdup(*name, child->full_name);
+ if ((*name)->full_name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ p = strrchr_m((*name)->full_name, '/');
+ if (p == NULL) {
+ return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
+ }
+
+ /* this handles the root directory */
+ if (p == (*name)->full_name) {
+ p[1] = 0;
+ } else {
+ p[0] = 0;
+ }
+
+ if (stat((*name)->full_name, &(*name)->st) == -1) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ (*name)->exists = True;
+ (*name)->stream_exists = True;
+ (*name)->has_wildcard = False;
+ /* we can't get the correct 'original_name', but for the purposes
+ of this call this is close enough */
+ (*name)->original_name = talloc_reference(*name, child->original_name);
+ (*name)->stream_name = NULL;
+ (*name)->stream_id = 0;
+
+ status = pvfs_fill_dos_info(pvfs, *name, -1);
+
+ return status;
+}