backends = talloc_realloc(talloc_autofree_context(), backends, struct pvfs_acl_backend, num_backends+1);
NT_STATUS_HAVE_NO_MEMORY(backends);
- new_ops = talloc_memdup(backends, ops, sizeof(*ops));
+ new_ops = (struct pvfs_acl_ops *)talloc_memdup(backends, ops, sizeof(*ops));
new_ops->name = talloc_strdup(new_ops, ops->name);
backends[num_backends].ops = new_ops;
} else {
ret = fchown(fd, new_uid, new_gid);
}
+ if (errno == EPERM && uwrap_enabled()) {
+ ret = 0;
+ }
if (ret == -1) {
return pvfs_map_errno(pvfs, errno);
}
return false;
}
+/*
+ see if we are a member of the appropriate unix group
+ */
+static bool pvfs_group_member(struct pvfs_state *pvfs, gid_t gid)
+{
+ int i, ngroups;
+ gid_t *groups;
+ if (getegid() == gid) {
+ return true;
+ }
+ ngroups = getgroups(0, NULL);
+ if (ngroups == 0) {
+ return false;
+ }
+ groups = talloc_array(pvfs, gid_t, ngroups);
+ if (groups == NULL) {
+ return false;
+ }
+ if (getgroups(ngroups, groups) != ngroups) {
+ talloc_free(groups);
+ return false;
+ }
+ for (i=0; i<ngroups; i++) {
+ if (groups[i] == gid) break;
+ }
+ talloc_free(groups);
+ return i < ngroups;
+}
+
/*
default access check function based on unix permissions
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,
{
uid_t uid = geteuid();
uint32_t max_bits = SEC_RIGHTS_FILE_READ | SEC_FILE_ALL;
+ struct security_token *token = req->session_info->security_token;
if (pvfs_read_only(pvfs, *access_mask)) {
return NT_STATUS_ACCESS_DENIED;
}
- /* owner and root get extra permissions */
- if (uid == 0) {
- max_bits |= SEC_STD_ALL | SEC_FLAG_SYSTEM_SECURITY;
- } else 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 (*access_mask == SEC_FLAG_MAXIMUM_ALLOWED) {
- *access_mask = max_bits;
- return NT_STATUS_OK;
+ 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;
}
- if (uid != 0 && (*access_mask & SEC_FLAG_SYSTEM_SECURITY)) {
- return NT_STATUS_ACCESS_DENIED;
+ if (uwrap_enabled()) {
+ /* when running with the uid wrapper, files will be created
+ owned by the ruid, but we may have a different simulated
+ euid. We need to force the permission bits as though the
+ files owner matches the euid */
+ max_bits |= SEC_STD_ALL;
+ }
+
+ if (*access_mask & SEC_FLAG_MAXIMUM_ALLOWED) {
+ *access_mask |= max_bits;
+ *access_mask &= ~SEC_FLAG_MAXIMUM_ALLOWED;
+ }
+
+ if ((*access_mask & SEC_FLAG_SYSTEM_SECURITY) &&
+ security_token_has_privilege(token, SEC_PRIV_SECURITY)) {
+ max_bits |= SEC_FLAG_SYSTEM_SECURITY;
+ }
+
+ if (((*access_mask & ~max_bits) & SEC_RIGHTS_PRIV_RESTORE) &&
+ security_token_has_privilege(token, SEC_PRIV_RESTORE)) {
+ max_bits |= ~(SEC_RIGHTS_PRIV_RESTORE);
+ }
+ if (((*access_mask & ~max_bits) & SEC_RIGHTS_PRIV_BACKUP) &&
+ security_token_has_privilege(token, SEC_PRIV_BACKUP)) {
+ max_bits |= ~(SEC_RIGHTS_PRIV_BACKUP);
}
if (*access_mask & ~max_bits) {
+ DEBUG(0,(__location__ " denied access to '%s' - wanted 0x%08x but got 0x%08x (missing 0x%08x)\n",
+ name?name->full_name:"(new file)", *access_mask, max_bits, *access_mask & ~max_bits));
return NT_STATUS_ACCESS_DENIED;
}
NTSTATUS status;
struct security_descriptor *sd;
+ /* on SMB2 a blank access mask is always denied */
+ if (pvfs->ntvfs->ctx->protocol == PROTOCOL_SMB2 &&
+ *access_mask == 0) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
if (pvfs_read_only(pvfs, *access_mask)) {
return NT_STATUS_ACCESS_DENIED;
}
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);
}
/*
/*
- 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;
}
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;
}