*/
#include "includes.h"
+#include "system/passwd.h"
#include "auth/auth.h"
#include "vfs_posix.h"
#include "librpc/gen_ndr/xattr.h"
#include "libcli/security/security.h"
#include "param/param.h"
-
+#include "../lib/util/unix_privs.h"
+#include "lib/util/samba_modules.h"
/* the list of currently registered ACL backends */
static struct pvfs_acl_backend {
return NULL;
}
-NTSTATUS pvfs_acl_init(struct loadparm_context *lp_ctx)
+NTSTATUS pvfs_acl_init(void)
{
static bool initialized = false;
- extern NTSTATUS pvfs_acl_nfs4_init(void);
- extern NTSTATUS pvfs_acl_xattr_init(void);
- init_module_fn static_init[] = { STATIC_pvfs_acl_MODULES };
- init_module_fn *shared_init;
+#define _MODULE_PROTO(init) extern NTSTATUS init(void);
+ STATIC_pvfs_acl_MODULES_PROTO;
+ samba_init_module_fn static_init[] = { STATIC_pvfs_acl_MODULES };
+ samba_init_module_fn *shared_init;
if (initialized) return NT_STATUS_OK;
initialized = true;
- shared_init = load_samba_modules(NULL, lp_ctx, "pvfs_acl");
+ shared_init = load_samba_modules(NULL, "pvfs_acl");
run_init_functions(static_init);
run_init_functions(shared_init);
NTSTATUS status;
struct security_ace ace;
mode_t mode;
- struct id_mapping *ids;
+ struct id_map *ids;
struct composite_context *ctx;
*psd = security_descriptor_initialise(req);
}
sd = *psd;
- ids = talloc_zero_array(sd, struct id_mapping, 2);
+ ids = talloc_zero_array(sd, struct id_map, 2);
NT_STATUS_HAVE_NO_MEMORY(ids);
- ids[0].unixid = talloc(ids, struct unixid);
- NT_STATUS_HAVE_NO_MEMORY(ids[0].unixid);
-
- ids[0].unixid->id = name->st.st_uid;
- ids[0].unixid->type = ID_TYPE_UID;
+ ids[0].xid.id = name->st.st_uid;
+ ids[0].xid.type = ID_TYPE_UID;
ids[0].sid = NULL;
- ids[1].unixid = talloc(ids, struct unixid);
- NT_STATUS_HAVE_NO_MEMORY(ids[1].unixid);
-
- ids[1].unixid->id = name->st.st_gid;
- ids[1].unixid->type = ID_TYPE_GID;
+ ids[1].xid.id = name->st.st_gid;
+ ids[1].xid.type = ID_TYPE_GID;
ids[1].sid = NULL;
ctx = wbc_xids_to_sids_send(pvfs->wbc_ctx, ids, 2, ids);
gid_t old_gid = -1;
uid_t new_uid = -1;
gid_t new_gid = -1;
- struct id_mapping *ids;
+ struct id_map *ids;
struct composite_context *ctx;
if (pvfs->acl_ops != NULL) {
return status;
}
- ids = talloc(req, struct id_mapping);
+ ids = talloc(req, struct id_map);
NT_STATUS_HAVE_NO_MEMORY(ids);
- ids->unixid = NULL;
+ ZERO_STRUCT(ids->xid);
ids->sid = NULL;
- ids->status = NT_STATUS_NONE_MAPPED;
+ ids->status = ID_UNKNOWN;
new_sd = info->set_secdesc.in.sd;
orig_sd = *sd;
status = wbc_sids_to_xids_recv(ctx, &ids);
NT_STATUS_NOT_OK_RETURN(status);
- if (ids->unixid->type == ID_TYPE_BOTH ||
- ids->unixid->type == ID_TYPE_UID) {
- new_uid = ids->unixid->id;
+ if (ids->xid.type == ID_TYPE_BOTH ||
+ ids->xid.type == ID_TYPE_UID) {
+ new_uid = ids->xid.id;
}
}
sd->owner_sid = new_sd->owner_sid;
status = wbc_sids_to_xids_recv(ctx, &ids);
NT_STATUS_NOT_OK_RETURN(status);
- if (ids->unixid->type == ID_TYPE_BOTH ||
- ids->unixid->type == ID_TYPE_GID) {
- new_gid = ids->unixid->id;
+ if (ids->xid.type == ID_TYPE_BOTH ||
+ ids->xid.type == ID_TYPE_GID) {
+ new_gid = ids->xid.id;
}
}
} else {
ret = fchown(fd, new_uid, new_gid);
}
- if (errno == EPERM && uwrap_enabled()) {
- ret = 0;
+ if (errno == EPERM) {
+ if (uwrap_enabled()) {
+ ret = 0;
+ } else {
+ /* try again as root if we have SEC_PRIV_RESTORE or
+ SEC_PRIV_TAKE_OWNERSHIP */
+ if (security_token_has_privilege(req->session_info->security_token,
+ SEC_PRIV_RESTORE) ||
+ security_token_has_privilege(req->session_info->security_token,
+ SEC_PRIV_TAKE_OWNERSHIP)) {
+ void *privs;
+ privs = root_privileges();
+ if (fd == -1) {
+ ret = chown(name->full_name, new_uid, new_gid);
+ } else {
+ ret = fchown(fd, new_uid, new_gid);
+ }
+ talloc_free(privs);
+ }
+ }
}
if (ret == -1) {
return pvfs_map_errno(pvfs, errno);
If name is NULL then treat as a new file creation
*/
-NTSTATUS pvfs_access_check_unix(struct pvfs_state *pvfs,
- struct ntvfs_request *req,
- struct pvfs_filename *name,
- uint32_t *access_mask)
+static NTSTATUS pvfs_access_check_unix(struct pvfs_state *pvfs,
+ struct ntvfs_request *req,
+ struct pvfs_filename *name,
+ uint32_t *access_mask)
{
uid_t uid = geteuid();
uint32_t max_bits = SEC_RIGHTS_FILE_READ | SEC_FILE_ALL;
return NT_STATUS_ACCESS_DENIED;
}
- if (pvfs->ntvfs->ctx->protocol != PROTOCOL_SMB2) {
+ if (pvfs->ntvfs->ctx->protocol < PROTOCOL_SMB2_02) {
/* on SMB, this bit is always granted, even if not
asked for */
*access_mask |= SEC_FILE_READ_ATTRIBUTE;
struct xattr_NTACL *acl;
NTSTATUS status;
struct security_descriptor *sd;
+ bool allow_delete = false;
/* on SMB2 a blank access mask is always denied */
- if (pvfs->ntvfs->ctx->protocol == PROTOCOL_SMB2 &&
+ if (pvfs->ntvfs->ctx->protocol >= PROTOCOL_SMB2_02 &&
*access_mask == 0) {
return NT_STATUS_ACCESS_DENIED;
}
return NT_STATUS_ACCESS_DENIED;
}
+ if (*access_mask & SEC_FLAG_MAXIMUM_ALLOWED ||
+ *access_mask & SEC_STD_DELETE) {
+ status = pvfs_access_check_parent(pvfs, req,
+ name, SEC_DIR_DELETE_CHILD);
+ if (NT_STATUS_IS_OK(status)) {
+ allow_delete = true;
+ *access_mask &= ~SEC_STD_DELETE;
+ }
+ }
+
acl = talloc(req, struct xattr_NTACL);
if (acl == NULL) {
return NT_STATUS_NO_MEMORY;
/* expand the generic access bits to file specific bits */
*access_mask = pvfs_translate_mask(*access_mask);
- if (pvfs->ntvfs->ctx->protocol != PROTOCOL_SMB2) {
+ if (pvfs->ntvfs->ctx->protocol < PROTOCOL_SMB2_02) {
*access_mask &= ~SEC_FILE_READ_ATTRIBUTE;
}
status = pvfs_acl_load(pvfs, name, -1, acl);
if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
talloc_free(acl);
- return pvfs_access_check_unix(pvfs, req, name, access_mask);
+ status = pvfs_access_check_unix(pvfs, req, name, access_mask);
+ goto done;
}
if (!NT_STATUS_IS_OK(status)) {
return status;
}
/* check the acl against the required access mask */
- status = sec_access_check(sd, token, *access_mask, access_mask);
-
- if (pvfs->ntvfs->ctx->protocol != PROTOCOL_SMB2) {
+ status = se_access_check(sd, token, *access_mask, access_mask);
+ talloc_free(acl);
+done:
+ if (pvfs->ntvfs->ctx->protocol < PROTOCOL_SMB2_02) {
/* on SMB, this bit is always granted, even if not
asked for */
*access_mask |= SEC_FILE_READ_ATTRIBUTE;
}
- talloc_free(acl);
-
+ if (allow_delete) {
+ *access_mask |= SEC_STD_DELETE;
+ }
+
return status;
}
{
struct pvfs_filename *parent;
NTSTATUS status;
- struct security_token *token = req->session_info->security_token;
+ uint32_t parent_mask;
+ bool allow_delete = false;
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;
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ if (container) {
+ parent_mask = SEC_DIR_ADD_SUBDIR;
+ } else {
+ parent_mask = SEC_DIR_ADD_FILE;
+ }
+ if (*access_mask & SEC_FLAG_MAXIMUM_ALLOWED ||
+ *access_mask & SEC_STD_DELETE) {
+ parent_mask |= SEC_DIR_DELETE_CHILD;
}
- status = pvfs_access_check_simple(pvfs, req, parent, SEC_DIR_ADD_FILE);
- if (!NT_STATUS_IS_OK(status)) {
+ status = pvfs_access_check(pvfs, req, parent, &parent_mask);
+ if (NT_STATUS_IS_OK(status)) {
+ if (parent_mask & SEC_DIR_DELETE_CHILD) {
+ allow_delete = true;
+ }
+ } else if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+ /*
+ * on ACCESS_DENIED we get the rejected bits
+ * remove the non critical SEC_DIR_DELETE_CHILD
+ * and check if something else was rejected.
+ */
+ parent_mask &= ~SEC_DIR_DELETE_CHILD;
+ if (parent_mask != 0) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ status = NT_STATUS_OK;
+ } else {
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 (*access_mask & SEC_FLAG_MAXIMUM_ALLOWED) {
+ *access_mask |= SEC_RIGHTS_FILE_ALL;
+ *access_mask &= ~SEC_FLAG_MAXIMUM_ALLOWED;
}
- if (*sd == NULL) {
- return pvfs_access_check_unix(pvfs, req, NULL, access_mask);
+ if (pvfs->ntvfs->ctx->protocol < PROTOCOL_SMB2_02) {
+ /* on SMB, this bit is always granted, even if not
+ asked for */
+ *access_mask |= SEC_FILE_READ_ATTRIBUTE;
}
- return sec_access_check(*sd, token, *access_mask, access_mask);
+
+ if (allow_delete) {
+ *access_mask |= SEC_STD_DELETE;
+ }
+
+ return NT_STATUS_OK;
}
/*
struct xattr_NTACL *acl;
NTSTATUS status;
struct security_descriptor *parent_sd, *sd;
- struct id_mapping *ids;
+ struct id_map *ids;
struct composite_context *ctx;
TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
sd = security_descriptor_initialise(req);
NT_STATUS_HAVE_NO_MEMORY_AND_FREE(sd, tmp_ctx);
- ids = talloc_array(sd, struct id_mapping, 2);
+ ids = talloc_array(sd, struct id_map, 2);
NT_STATUS_HAVE_NO_MEMORY_AND_FREE(ids, tmp_ctx);
- ids[0].unixid = talloc(ids, struct unixid);
- 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].xid.id = geteuid();
+ ids[0].xid.type = ID_TYPE_UID;
ids[0].sid = NULL;
- ids[0].status = NT_STATUS_NONE_MAPPED;
+ ids[0].status = ID_UNKNOWN;
- ids[1].unixid = talloc(ids, struct unixid);
- 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].xid.id = getegid();
+ ids[1].xid.type = ID_TYPE_GID;
ids[1].sid = NULL;
- ids[1].status = NT_STATUS_NONE_MAPPED;
+ ids[1].status = ID_UNKNOWN;
ctx = wbc_xids_to_sids_send(pvfs->wbc_ctx, ids, 2, ids);
NT_STATUS_HAVE_NO_MEMORY_AND_FREE(ctx, tmp_ctx);