From abe22d0351955adb1ad7c304d45b9539d202aadb Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Thu, 30 Dec 2004 02:25:20 +0000 Subject: r4403: - added ACL inheritance in the pvfs backend. ACLs are now inherited on file and directory creation via ntcreatex. pvfs now passes the inheritance test in RAW-ACLS - cleaned up the error handling a bit in pvfs_open() (This used to be commit f4dfb63d5395a365961a21388639809fcd3112d0) --- source4/ntvfs/posix/pvfs_acl.c | 164 ++++++++++++++++++++++++++++++++++++- source4/ntvfs/posix/pvfs_open.c | 116 ++++++++++++++++---------- source4/ntvfs/posix/pvfs_resolve.c | 51 ++++++++++++ 3 files changed, 287 insertions(+), 44 deletions(-) (limited to 'source4/ntvfs/posix') 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;idacl->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; +} -- cgit