struct xattr_NTACL *acl)
{
struct security_descriptor *sd;
- int i;
- struct security_ace ace;
NTSTATUS status;
- const char *sid_names[] = {
- SID_BUILTIN_ADMINISTRATORS,
- SID_CREATOR_OWNER,
- SID_CREATOR_GROUP,
- SID_WORLD
- };
- uint32_t access_masks[4];
+ struct security_ace ace;
mode_t mode;
sd = security_descriptor_initialise(req);
sd->type |= SEC_DESC_DACL_PRESENT;
+ mode = name->st.st_mode;
+
/*
- we provide 4 ACEs
- - Administrator
+ we provide up to 4 ACEs
- Owner
- Group
- Everyone
+ - Administrator
*/
- access_masks[0] = SEC_RIGHTS_FILE_ALL;
- access_masks[1] = 0;
- access_masks[2] = 0;
- access_masks[3] = 0;
- mode = name->st.st_mode;
+
+ /* setup owner ACE */
+ ace.type = SEC_ACE_TYPE_ACCESS_ALLOWED;
+ ace.flags = 0;
+ ace.trustee = *sd->owner_sid;
+ ace.access_mask = 0;
if (mode & S_IRUSR) {
- access_masks[1] |=
- SEC_FILE_READ_DATA |
- SEC_FILE_READ_EA |
- SEC_FILE_READ_ATTRIBUTE |
- SEC_FILE_EXECUTE |
- SEC_STD_SYNCHRONIZE |
- SEC_STD_READ_CONTROL;
+ 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) {
- access_masks[1] |=
- SEC_FILE_WRITE_DATA |
- SEC_FILE_APPEND_DATA |
- SEC_FILE_WRITE_EA |
- SEC_FILE_WRITE_ATTRIBUTE |
- SEC_STD_DELETE;
+ ace.access_mask |= SEC_RIGHTS_FILE_WRITE | SEC_STD_DELETE;
+ }
+ if (ace.access_mask) {
+ security_descriptor_dacl_add(sd, &ace);
}
+
+ /* setup group ACE */
+ ace.trustee = *sd->group_sid;
+ ace.access_mask = 0;
if (mode & S_IRGRP) {
- access_masks[2] |=
- SEC_FILE_READ_DATA |
- SEC_FILE_READ_EA |
- SEC_FILE_READ_ATTRIBUTE |
- SEC_FILE_EXECUTE |
- SEC_STD_SYNCHRONIZE |
- SEC_STD_READ_CONTROL;
+ ace.access_mask |= SEC_RIGHTS_FILE_READ | SEC_FILE_EXECUTE;
}
if (mode & S_IWGRP) {
- access_masks[2] |=
- SEC_FILE_WRITE_DATA |
- SEC_FILE_APPEND_DATA |
- SEC_FILE_WRITE_EA |
- SEC_FILE_WRITE_ATTRIBUTE;
+ /* note that delete is not granted - this matches posix behaviour */
+ ace.access_mask |= SEC_RIGHTS_FILE_WRITE;
+ }
+ if (ace.access_mask) {
+ security_descriptor_dacl_add(sd, &ace);
}
+ /* setup other ACE */
+ ace.trustee = *dom_sid_parse_talloc(req, SID_WORLD);
+ ace.access_mask = 0;
if (mode & S_IROTH) {
- access_masks[3] |=
- SEC_FILE_READ_DATA |
- SEC_FILE_READ_EA |
- SEC_FILE_READ_ATTRIBUTE |
- SEC_FILE_EXECUTE |
- SEC_STD_SYNCHRONIZE |
- SEC_STD_READ_CONTROL;
+ ace.access_mask |= SEC_RIGHTS_FILE_READ | SEC_FILE_EXECUTE;
}
if (mode & S_IWOTH) {
- access_masks[3] |=
- SEC_FILE_WRITE_DATA |
- SEC_FILE_APPEND_DATA |
- SEC_FILE_WRITE_EA |
- SEC_FILE_WRITE_ATTRIBUTE;
+ ace.access_mask |= SEC_RIGHTS_FILE_WRITE;
}
-
- ace.type = SEC_ACE_TYPE_ACCESS_ALLOWED;
- ace.flags = 0;
-
- for (i=0;i<ARRAY_SIZE(sid_names);i++) {
- struct dom_sid *sid;
-
- ace.access_mask = access_masks[i];
-
- sid = dom_sid_parse_talloc(sd, sid_names[i]);
- if (sid == NULL) {
- return NT_STATUS_NO_MEMORY;
- }
- ace.trustee = *sid;
-
- status = security_descriptor_dacl_add(sd, &ace);
- if (!NT_STATUS_IS_OK(status)) {
- return status;
- }
+ if (ace.access_mask) {
+ security_descriptor_dacl_add(sd, &ace);
}
+
+ /* setup system ACE */
+ ace.trustee = *dom_sid_parse_talloc(req, SID_NT_SYSTEM);
+ ace.access_mask = SEC_RIGHTS_FILE_ALL;
+ security_descriptor_dacl_add(sd, &ace);
acl->version = 1;
acl->info.sd = sd;
NTSTATUS pvfs_acl_set(struct pvfs_state *pvfs,
struct smbsrv_request *req,
struct pvfs_filename *name, int fd,
+ uint32_t access_mask,
union smb_setfileinfo *info)
{
struct xattr_NTACL *acl;
uint32_t secinfo_flags = info->set_secdesc.in.secinfo_flags;
struct security_descriptor *new_sd, *sd;
NTSTATUS status;
+ uid_t uid = -1;
+ gid_t gid = -1;
acl = talloc_p(req, struct xattr_NTACL);
if (acl == NULL) {
new_sd = info->set_secdesc.in.sd;
+ uid = name->st.st_uid;
+ gid = name->st.st_gid;
+
/* only set the elements that have been specified */
- if (secinfo_flags & SECINFO_OWNER) {
+ if ((secinfo_flags & SECINFO_OWNER) &&
+ !dom_sid_equal(sd->owner_sid, new_sd->owner_sid)) {
+ if (!(access_mask & SEC_STD_WRITE_OWNER)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
sd->owner_sid = new_sd->owner_sid;
+ status = sidmap_sid_to_unixuid(pvfs->sidmap, sd->owner_sid, &uid);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
}
- if (secinfo_flags & SECINFO_GROUP) {
+ if ((secinfo_flags & SECINFO_GROUP) &&
+ !dom_sid_equal(sd->group_sid, new_sd->group_sid)) {
sd->group_sid = new_sd->group_sid;
+ status = sidmap_sid_to_unixgid(pvfs->sidmap, sd->owner_sid, &gid);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
}
if (secinfo_flags & SECINFO_DACL) {
sd->dacl = new_sd->dacl;
}
if (secinfo_flags & SECINFO_SACL) {
sd->sacl = new_sd->sacl;
+ if (!(access_mask & SEC_FLAG_SYSTEM_SECURITY)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
pvfs_translate_generic_bits(sd->sacl);
}
+ if (uid != -1 || gid != -1) {
+ int ret;
+ if (fd == -1) {
+ ret = chown(name->full_name, uid, gid);
+ } else {
+ ret = fchown(fd, uid, gid);
+ }
+ if (ret == -1) {
+ return pvfs_map_errno(pvfs, errno);
+ }
+ }
+
status = pvfs_acl_save(pvfs, name, fd, acl);
return status;
return NT_STATUS_ACCESS_DENIED;
}
+ *access_mask |= SEC_FILE_READ_ATTRIBUTE;
+
return NT_STATUS_OK;
}
struct pvfs_filename *name,
uint32_t *access_mask)
{
- struct nt_user_token *token = req->session->session_info->nt_user_token;
+ struct security_token *token = req->session->session_info->security_token;
struct xattr_NTACL *acl;
NTSTATUS status;
struct security_descriptor *sd;
return status;
}
+
+
+/*
+ a simplified interface to access check, designed for calls that
+ do not take or return an access check mask
+*/
+NTSTATUS pvfs_access_check_simple(struct pvfs_state *pvfs,
+ struct smbsrv_request *req,
+ struct pvfs_filename *name,
+ uint32_t access_needed)
+{
+ if (access_needed == 0) {
+ return NT_STATUS_OK;
+ }
+ return pvfs_access_check(pvfs, req, name, &access_needed);
+}
+
+/*
+ access check for creating a new file/directory
+*/
+NTSTATUS pvfs_access_check_create(struct pvfs_state *pvfs,
+ struct smbsrv_request *req,
+ struct pvfs_filename *name)
+{
+ struct pvfs_filename *parent;
+ NTSTATUS status;
+
+ status = pvfs_resolve_parent(pvfs, req, name, &parent);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return pvfs_access_check_simple(pvfs, req, parent, SEC_DIR_ADD_FILE);
+}
+
+
+/*
+ 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;i<parent_sd->dacl->num_aces;i++) {
+ struct security_ace ace = parent_sd->dacl->aces[i];
+ NTSTATUS status;
+ const struct dom_sid *creator = NULL, *new_id = NULL;
+ uint32_t orig_flags;
+
+ if (!pvfs_inheritable_ace(pvfs, &ace, container)) {
+ continue;
+ }
+
+ orig_flags = ace.flags;
+
+ /* 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;
+ }
+ }
+
+ /* the CREATOR sids are special when inherited */
+ if (dom_sid_equal(&ace.trustee, pvfs->sid_cache.creator_owner)) {
+ creator = pvfs->sid_cache.creator_owner;
+ new_id = sd->owner_sid;
+ } else if (dom_sid_equal(&ace.trustee, pvfs->sid_cache.creator_group)) {
+ creator = pvfs->sid_cache.creator_group;
+ new_id = sd->group_sid;
+ } else {
+ new_id = &ace.trustee;
+ }
+
+ if (creator && container &&
+ (ace.flags & SEC_ACE_FLAG_CONTAINER_INHERIT)) {
+ uint32_t flags = ace.flags;
+
+ ace.trustee = *new_id;
+ ace.flags = 0;
+ status = security_descriptor_dacl_add(sd, &ace);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ ace.trustee = *creator;
+ ace.flags = flags | SEC_ACE_FLAG_INHERIT_ONLY;
+ status = security_descriptor_dacl_add(sd, &ace);
+ } else if (container &&
+ !(orig_flags & SEC_ACE_FLAG_NO_PROPAGATE_INHERIT)) {
+ status = security_descriptor_dacl_add(sd, &ace);
+ } else {
+ ace.trustee = *new_id;
+ 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;
+}