*/
#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"
-
-#if defined(UID_WRAPPER)
-#if !defined(UID_WRAPPER_REPLACE) && !defined(UID_WRAPPER_NOT_REPLACE)
-#define UID_WRAPPER_REPLACE
-#include "../uid_wrapper/uid_wrapper.h"
-#endif
-#else
-#define uwrap_enabled() 0
-#endif
+#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);
+#define _MODULE_PROTO(init) extern NTSTATUS init(void);
+ STATIC_pvfs_acl_MODULES_PROTO;
init_module_fn static_init[] = { STATIC_pvfs_acl_MODULES };
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);
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);
ids = talloc(req, struct id_map);
NT_STATUS_HAVE_NO_MEMORY(ids);
- ids->unixid = NULL;
+ ZERO_STRUCT(ids->xid);
ids->sid = NULL;
ids->status = ID_UNKNOWN;
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;
}
+
if (secinfo_flags & SECINFO_GROUP) {
if (!(access_mask & SEC_STD_WRITE_OWNER)) {
return NT_STATUS_ACCESS_DENIED;
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;
}
}
sd->group_sid = new_sd->group_sid;
}
+
if (secinfo_flags & SECINFO_DACL) {
if (!(access_mask & SEC_STD_WRITE_DAC)) {
return NT_STATUS_ACCESS_DENIED;
}
sd->dacl = new_sd->dacl;
pvfs_translate_generic_bits(sd->dacl);
+ sd->type |= SEC_DESC_DACL_PRESENT;
}
+
if (secinfo_flags & SECINFO_SACL) {
if (!(access_mask & SEC_FLAG_SYSTEM_SECURITY)) {
return NT_STATUS_ACCESS_DENIED;
}
sd->sacl = new_sd->sacl;
pvfs_translate_generic_bits(sd->sacl);
+ sd->type |= SEC_DESC_SACL_PRESENT;
+ }
+
+ if (secinfo_flags & SECINFO_PROTECTED_DACL) {
+ if (new_sd->type & SEC_DESC_DACL_PROTECTED) {
+ sd->type |= SEC_DESC_DACL_PROTECTED;
+ } else {
+ sd->type &= ~SEC_DESC_DACL_PROTECTED;
+ }
+ }
+
+ if (secinfo_flags & SECINFO_PROTECTED_SACL) {
+ if (new_sd->type & SEC_DESC_SACL_PROTECTED) {
+ sd->type |= SEC_DESC_SACL_PROTECTED;
+ } else {
+ sd->type &= ~SEC_DESC_SACL_PROTECTED;
+ }
}
if (new_uid == old_uid) {
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;
+ uint32_t max_bits = 0;
struct security_token *token = req->session_info->security_token;
if (pvfs_read_only(pvfs, *access_mask)) {
return NT_STATUS_ACCESS_DENIED;
}
- 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 == 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 (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 (name == NULL) {
+ max_bits |= SEC_RIGHTS_FILE_ALL | SEC_STD_ALL;
+ } else if (uid == name->st.st_uid || uwrap_enabled()) {
+ /* use the IxUSR bits */
+ if ((name->st.st_mode & S_IWUSR)) {
+ max_bits |= SEC_RIGHTS_FILE_ALL | SEC_STD_ALL;
+ } else if ((name->st.st_mode & (S_IRUSR | S_IXUSR))) {
+ max_bits |= SEC_RIGHTS_FILE_READ | SEC_RIGHTS_FILE_EXECUTE | SEC_STD_ALL;
+ }
+ } else if (pvfs_group_member(pvfs, name->st.st_gid)) {
+ /* use the IxGRP bits */
+ if ((name->st.st_mode & S_IWGRP)) {
+ max_bits |= SEC_RIGHTS_FILE_ALL | SEC_STD_ALL;
+ } else if ((name->st.st_mode & (S_IRGRP | S_IXGRP))) {
+ max_bits |= SEC_RIGHTS_FILE_READ | SEC_RIGHTS_FILE_EXECUTE | SEC_STD_ALL;
+ }
+ } else {
+ /* use the IxOTH bits */
+ if ((name->st.st_mode & S_IWOTH)) {
+ max_bits |= SEC_RIGHTS_FILE_ALL | SEC_STD_ALL;
+ } else if ((name->st.st_mode & (S_IROTH | S_IXOTH))) {
+ max_bits |= SEC_RIGHTS_FILE_READ | SEC_RIGHTS_FILE_EXECUTE | SEC_STD_ALL;
+ }
}
if (*access_mask & SEC_FLAG_MAXIMUM_ALLOWED) {
}
if (*access_mask & ~max_bits) {
- DEBUG(0,(__location__ " denied access to '%s' - wanted 0x%08x but got 0x%08x (missing 0x%08x)\n",
+ DEBUG(5,(__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;
}
- 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);
+ status = se_access_check(sd, token, *access_mask, access_mask);
+ talloc_free(acl);
- if (pvfs->ntvfs->ctx->protocol != PROTOCOL_SMB2) {
+ /* if we used a NT acl, then allow access override if the
+ share allows for posix permission override
+ */
+ if (NT_STATUS_IS_OK(status)) {
+ name->allow_override = (pvfs->flags & PVFS_FLAG_PERM_OVERRIDE) != 0;
+ }
+
+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;
+ 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);
NT_STATUS_NOT_OK_RETURN(status);
- status = pvfs_access_check_simple(pvfs, req, parent, SEC_DIR_ADD_FILE);
- 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(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;
+ }
if (*sd == NULL) {
status = pvfs_acl_inherited_sd(pvfs, req, req, parent, container, sd);
*access_mask &= ~SEC_FLAG_MAXIMUM_ALLOWED;
}
- 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;
}
+ if (allow_delete) {
+ *access_mask |= SEC_STD_DELETE;
+ }
+
return NT_STATUS_OK;
}
return status;
}
- return pvfs_access_check_simple(pvfs, req, parent, access_mask);
+ status = pvfs_access_check_simple(pvfs, req, parent, access_mask);
+ if (NT_STATUS_IS_OK(status) && parent->allow_override) {
+ name->allow_override = true;
+ }
+ return status;
}
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 = 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 = ID_UNKNOWN;