From 0463d698835053af680db4f388c732d2557f7c8a Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Sat, 17 Oct 2009 21:17:29 +1100 Subject: s4-pvfs: change the handling of access checking on create Previously when a file was created, we produces the resulting access mask based on an ACL check against the parent. This change means we now calculate the inherited ACL much earlier, and produce the resulting access mask from that ACL, or the user supplied ACL. --- source4/ntvfs/posix/pvfs_acl.c | 149 +++++++++++++++++++++++---------- source4/ntvfs/posix/pvfs_open.c | 22 ++--- source4/ntvfs/posix/pvfs_resolve.c | 11 ++- source4/ntvfs/posix/pvfs_setfileinfo.c | 2 +- 4 files changed, 124 insertions(+), 60 deletions(-) (limited to 'source4/ntvfs/posix') diff --git a/source4/ntvfs/posix/pvfs_acl.c b/source4/ntvfs/posix/pvfs_acl.c index 26515cfe1a..842aced6f2 100644 --- a/source4/ntvfs/posix/pvfs_acl.c +++ b/source4/ntvfs/posix/pvfs_acl.c @@ -485,6 +485,8 @@ static bool pvfs_group_member(struct pvfs_state *pvfs, gid_t gid) doing this saves on building a full security descriptor for the common case of access check on files with no specific NT ACL + + If name is NULL then treat as a new file creation */ NTSTATUS pvfs_access_check_unix(struct pvfs_state *pvfs, struct ntvfs_request *req, @@ -499,13 +501,14 @@ NTSTATUS pvfs_access_check_unix(struct pvfs_state *pvfs, return NT_STATUS_ACCESS_DENIED; } - if (uid == name->st.st_uid) { + if (name == NULL || uid == name->st.st_uid) { max_bits |= SEC_STD_ALL; } else if (security_token_has_privilege(token, SEC_PRIV_RESTORE)) { max_bits |= SEC_STD_DELETE; } - if ((name->st.st_mode & S_IWOTH) || + if (name == NULL || + (name->st.st_mode & S_IWOTH) || ((name->st.st_mode & S_IWGRP) && pvfs_group_member(pvfs, name->st.st_gid))) { max_bits |= SEC_STD_ALL; @@ -540,7 +543,7 @@ NTSTATUS pvfs_access_check_unix(struct pvfs_state *pvfs, if (*access_mask & ~max_bits) { DEBUG(0,(__location__ " denied access to '%s' - wanted 0x%08x but got 0x%08x (missing 0x%08x)\n", - name->full_name, *access_mask, max_bits, *access_mask & ~max_bits)); + name?name->full_name:"(new file)", *access_mask, max_bits, *access_mask & ~max_bits)); return NT_STATUS_ACCESS_DENIED; } @@ -643,26 +646,47 @@ NTSTATUS pvfs_access_check_simple(struct pvfs_state *pvfs, NTSTATUS pvfs_access_check_create(struct pvfs_state *pvfs, struct ntvfs_request *req, struct pvfs_filename *name, - uint32_t *access_mask) + uint32_t *access_mask, + bool container, + struct security_descriptor **sd) { struct pvfs_filename *parent; NTSTATUS status; + struct security_token *token = req->session_info->security_token; + + if (pvfs_read_only(pvfs, *access_mask)) { + return NT_STATUS_ACCESS_DENIED; + } status = pvfs_resolve_parent(pvfs, req, name, &parent); if (!NT_STATUS_IS_OK(status)) { return status; } - status = pvfs_access_check(pvfs, req, parent, access_mask); + status = pvfs_access_check_simple(pvfs, req, parent, SEC_DIR_ADD_FILE); if (!NT_STATUS_IS_OK(status)) { return status; } - if (! ((*access_mask) & SEC_DIR_ADD_FILE)) { - return pvfs_access_check_simple(pvfs, req, parent, SEC_DIR_ADD_FILE); + if (*sd == NULL) { + status = pvfs_acl_inherited_sd(pvfs, req, req, parent, container, sd); } - return status; + talloc_free(parent); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + /* expand the generic access bits to file specific bits */ + *access_mask = pvfs_translate_mask(*access_mask); + if (pvfs->ntvfs->ctx->protocol != PROTOCOL_SMB2) { + *access_mask &= ~SEC_FILE_READ_ATTRIBUTE; + } + + if (*sd == NULL) { + return pvfs_access_check_unix(pvfs, req, NULL, access_mask); + } + return sec_access_check(*sd, token, *access_mask, access_mask); } /* @@ -790,47 +814,42 @@ static NTSTATUS pvfs_acl_inherit_aces(struct pvfs_state *pvfs, /* - 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 + calculate the ACL on a new file/directory based on the inherited ACL + from the parent. If there is no inherited ACL then return a NULL + ACL, which means the default ACL should be used */ -NTSTATUS pvfs_acl_inherit(struct pvfs_state *pvfs, - struct ntvfs_request *req, - struct pvfs_filename *name, - int fd) +NTSTATUS pvfs_acl_inherited_sd(struct pvfs_state *pvfs, + TALLOC_CTX *mem_ctx, + struct ntvfs_request *req, + struct pvfs_filename *parent, + bool container, + struct security_descriptor **ret_sd) { struct xattr_NTACL *acl; NTSTATUS status; - struct pvfs_filename *parent; struct security_descriptor *parent_sd, *sd; - bool container; struct id_mapping *ids; struct composite_context *ctx; + TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx); - /* form the parents path */ - status = pvfs_resolve_parent(pvfs, req, name, &parent); - if (!NT_STATUS_IS_OK(status)) { - return status; - } + *ret_sd = NULL; acl = talloc(req, struct xattr_NTACL); - if (acl == NULL) { - return NT_STATUS_NO_MEMORY; - } + NT_STATUS_HAVE_NO_MEMORY_AND_FREE(acl, tmp_ctx); status = pvfs_acl_load(pvfs, parent, -1, acl); if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) { + talloc_free(tmp_ctx); return NT_STATUS_OK; } - if (!NT_STATUS_IS_OK(status)) { - return status; - } + NT_STATUS_NOT_OK_RETURN_AND_FREE(status, tmp_ctx); switch (acl->version) { case 1: parent_sd = acl->info.sd; break; default: + talloc_free(tmp_ctx); return NT_STATUS_INVALID_ACL; } @@ -838,61 +857,99 @@ NTSTATUS pvfs_acl_inherit(struct pvfs_state *pvfs, parent_sd->dacl == NULL || parent_sd->dacl->num_aces == 0) { /* go with the default ACL */ + talloc_free(tmp_ctx); return NT_STATUS_OK; } /* create the new sd */ sd = security_descriptor_initialise(req); - if (sd == NULL) { - return NT_STATUS_NO_MEMORY; - } + NT_STATUS_HAVE_NO_MEMORY_AND_FREE(sd, tmp_ctx); ids = talloc_array(sd, struct id_mapping, 2); - NT_STATUS_HAVE_NO_MEMORY(ids); + NT_STATUS_HAVE_NO_MEMORY_AND_FREE(ids, tmp_ctx); ids[0].unixid = talloc(ids, struct unixid); - NT_STATUS_HAVE_NO_MEMORY(ids[0].unixid); - ids[0].unixid->id = name->st.st_uid; + NT_STATUS_HAVE_NO_MEMORY_AND_FREE(ids[0].unixid, tmp_ctx); + ids[0].unixid->id = geteuid(); ids[0].unixid->type = ID_TYPE_UID; ids[0].sid = NULL; ids[0].status = NT_STATUS_NONE_MAPPED; ids[1].unixid = talloc(ids, struct unixid); - NT_STATUS_HAVE_NO_MEMORY(ids[1].unixid); - ids[1].unixid->id = name->st.st_gid; + NT_STATUS_HAVE_NO_MEMORY_AND_FREE(ids[1].unixid, tmp_ctx); + ids[1].unixid->id = getegid(); ids[1].unixid->type = ID_TYPE_GID; ids[1].sid = NULL; ids[1].status = NT_STATUS_NONE_MAPPED; ctx = wbc_xids_to_sids_send(pvfs->wbc_ctx, ids, 2, ids); - NT_STATUS_HAVE_NO_MEMORY(ctx); + NT_STATUS_HAVE_NO_MEMORY_AND_FREE(ctx, tmp_ctx); status = wbc_xids_to_sids_recv(ctx, &ids); - NT_STATUS_NOT_OK_RETURN(status); + NT_STATUS_NOT_OK_RETURN_AND_FREE(status, tmp_ctx); sd->owner_sid = talloc_steal(sd, ids[0].sid); sd->group_sid = talloc_steal(sd, ids[1].sid); 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; - } + NT_STATUS_NOT_OK_RETURN_AND_FREE(status, tmp_ctx); /* if there is nothing to inherit then we fallback to the default acl */ if (sd->dacl == NULL || sd->dacl->num_aces == 0) { + talloc_free(tmp_ctx); return NT_STATUS_OK; } - acl->info.sd = sd; + *ret_sd = talloc_steal(mem_ctx, sd); + + talloc_free(tmp_ctx); + 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 ntvfs_request *req, + struct pvfs_filename *name, + int fd) +{ + struct xattr_NTACL acl; + NTSTATUS status; + struct security_descriptor *sd; + struct pvfs_filename *parent; + bool container; + + /* form the parents path */ + status = pvfs_resolve_parent(pvfs, req, name, &parent); + NT_STATUS_NOT_OK_RETURN(status); + + container = (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) ? true:false; + + status = pvfs_acl_inherited_sd(pvfs, req, req, parent, container, &sd); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(parent); + return status; + } + + if (sd == NULL) { + return NT_STATUS_OK; + } + + acl.version = 1; + acl.info.sd = sd; + + status = pvfs_acl_save(pvfs, name, fd, &acl); + talloc_free(sd); + talloc_free(parent); - 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 e8f1c0c4c8..59bd67b08d 100644 --- a/source4/ntvfs/posix/pvfs_open.c +++ b/source4/ntvfs/posix/pvfs_open.c @@ -103,10 +103,10 @@ static NTSTATUS pvfs_open_setup_eas_acl(struct pvfs_state *pvfs, struct ntvfs_request *req, struct pvfs_filename *name, int fd, struct pvfs_file *f, - union smb_open *io) + union smb_open *io, + struct security_descriptor *sd) { NTSTATUS status; - struct security_descriptor *sd; /* setup any EAs that were asked for */ if (io->ntcreatex.in.ea_list) { @@ -118,7 +118,6 @@ static NTSTATUS pvfs_open_setup_eas_acl(struct pvfs_state *pvfs, } } - sd = io->ntcreatex.in.sec_desc; /* setup an initial sec_desc if requested */ if (sd && (sd->type & SEC_DESC_DACL_PRESENT)) { union smb_setfileinfo set; @@ -134,9 +133,6 @@ static NTSTATUS pvfs_open_setup_eas_acl(struct pvfs_state *pvfs, set.set_secdesc.in.sd = sd; status = pvfs_acl_set(pvfs, req, name, fd, SEC_STD_WRITE_DAC, &set); - } else { - /* otherwise setup an inherited acl from the parent */ - status = pvfs_acl_inherit(pvfs, req, name, fd); } return status; @@ -185,6 +181,7 @@ static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs, uint32_t create_options; uint32_t share_access; bool forced; + struct security_descriptor *sd = NULL; create_options = io->generic.in.create_options; share_access = io->generic.in.share_access; @@ -251,8 +248,9 @@ static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs, if (name->exists) { /* check the security descriptor */ status = pvfs_access_check(pvfs, req, name, &access_mask); - } else { - status = pvfs_access_check_create(pvfs, req, name, &access_mask); + } else { + sd = io->ntcreatex.in.sec_desc; + status = pvfs_access_check_create(pvfs, req, name, &access_mask, true, &sd); } NT_STATUS_NOT_OK_RETURN(status); @@ -352,7 +350,7 @@ static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs, goto cleanup_delete; } - status = pvfs_open_setup_eas_acl(pvfs, req, name, -1, f, io); + status = pvfs_open_setup_eas_acl(pvfs, req, name, -1, f, io, sd); if (!NT_STATUS_IS_OK(status)) { goto cleanup_delete; } @@ -616,6 +614,7 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs, struct pvfs_filename *parent; uint32_t oplock_level = OPLOCK_NONE, oplock_granted; bool allow_level_II_oplock = false; + struct security_descriptor *sd = NULL; if (io->ntcreatex.in.file_attr & ~FILE_ATTRIBUTE_ALL_MASK) { return NT_STATUS_INVALID_PARAMETER; @@ -630,7 +629,8 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs, return NT_STATUS_CANNOT_DELETE; } - status = pvfs_access_check_create(pvfs, req, name, &access_mask); + sd = io->ntcreatex.in.sec_desc; + status = pvfs_access_check_create(pvfs, req, name, &access_mask, false, &sd); NT_STATUS_NOT_OK_RETURN(status); /* check that the parent isn't opened with delete on close set */ @@ -698,7 +698,7 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs, } - status = pvfs_open_setup_eas_acl(pvfs, req, name, fd, f, io); + status = pvfs_open_setup_eas_acl(pvfs, req, name, fd, f, io, sd); if (!NT_STATUS_IS_OK(status)) { goto cleanup_delete; } diff --git a/source4/ntvfs/posix/pvfs_resolve.c b/source4/ntvfs/posix/pvfs_resolve.c index 8e8da72e1f..8c5806d93f 100644 --- a/source4/ntvfs/posix/pvfs_resolve.c +++ b/source4/ntvfs/posix/pvfs_resolve.c @@ -498,13 +498,14 @@ static NTSTATUS pvfs_reduce_name(TALLOC_CTX *mem_ctx, TODO: ../ collapsing, and outside share checking */ -NTSTATUS pvfs_resolve_name(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx, +NTSTATUS pvfs_resolve_name(struct pvfs_state *pvfs, + struct ntvfs_request *req, const char *cifs_name, uint_t flags, struct pvfs_filename **name) { NTSTATUS status; - *name = talloc(mem_ctx, struct pvfs_filename); + *name = talloc(req, struct pvfs_filename); if (*name == NULL) { return NT_STATUS_NO_MEMORY; } @@ -516,6 +517,12 @@ NTSTATUS pvfs_resolve_name(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx, flags &= ~PVFS_RESOLVE_STREAMS; } + /* SMB2 doesn't allow a leading slash */ + if (req->ctx->protocol == PROTOCOL_SMB2 && + *cifs_name == '\\') { + return NT_STATUS_INVALID_PARAMETER; + } + /* do the basic conversion to a unix formatted path, also checking for allowable characters */ status = pvfs_unix_path(pvfs, cifs_name, flags, *name); diff --git a/source4/ntvfs/posix/pvfs_setfileinfo.c b/source4/ntvfs/posix/pvfs_setfileinfo.c index 9fe02a8e17..244548382c 100644 --- a/source4/ntvfs/posix/pvfs_setfileinfo.c +++ b/source4/ntvfs/posix/pvfs_setfileinfo.c @@ -168,7 +168,7 @@ static NTSTATUS pvfs_setfileinfo_rename(struct pvfs_state *pvfs, } /* resolve the new name */ - status = pvfs_resolve_name(pvfs, name, new_name, 0, &name2); + status = pvfs_resolve_name(pvfs, req, new_name, 0, &name2); if (!NT_STATUS_IS_OK(status)) { return status; } -- cgit