#include "include/types.h" #include #include "posix_acl.h" #include "UserPerm.h" #ifndef ACCESSPERMS #define ACCESSPERMS (S_IRWXU|S_IRWXG|S_IRWXO) #endif int posix_acl_check(const void *xattr, size_t size) { const acl_ea_header *header; if (size < sizeof(*header)) return -1; header = reinterpret_cast(xattr); ceph_le32 expected_version; expected_version = ACL_EA_VERSION; if (header->a_version != expected_version) return -1; const acl_ea_entry *entry = header->a_entries; size -= sizeof(*header); if (size % sizeof(*entry)) return -1; int count = size / sizeof(*entry); if (count == 0) return 0; int state = ACL_USER_OBJ; int needs_mask = 0; for (int i = 0; i < count; ++i) { __u16 tag = entry->e_tag; switch(tag) { case ACL_USER_OBJ: if (state == ACL_USER_OBJ) { state = ACL_USER; break; } return -1; case ACL_USER: if (state != ACL_USER) return -1; needs_mask = 1; break; case ACL_GROUP_OBJ: if (state == ACL_USER) { state = ACL_GROUP; break; } return -1; case ACL_GROUP: if (state != ACL_GROUP) return -1; needs_mask = 1; break; case ACL_MASK: if (state != ACL_GROUP) return -1; state = ACL_OTHER; break; case ACL_OTHER: if (state == ACL_OTHER || (state == ACL_GROUP && !needs_mask)) { state = 0; break; } // fall-thru default: return -1; } ++entry; } return state == 0 ? count : -1; } int posix_acl_equiv_mode(const void *xattr, size_t size, mode_t *mode_p) { if (posix_acl_check(xattr, size) < 0) return -EINVAL; int not_equiv = 0; mode_t mode = 0; const acl_ea_header *header = reinterpret_cast(xattr); const acl_ea_entry *entry = header->a_entries; int count = (size - sizeof(*header)) / sizeof(*entry); for (int i = 0; i < count; ++i) { __u16 tag = entry->e_tag; __u16 perm = entry->e_perm; switch(tag) { case ACL_USER_OBJ: mode |= (perm & S_IRWXO) << 6; break; case ACL_GROUP_OBJ: mode |= (perm & S_IRWXO) << 3; break; case ACL_OTHER: mode |= perm & S_IRWXO; break; case ACL_MASK: mode = (mode & ~S_IRWXG) | ((perm & S_IRWXO) << 3); /* fall through */ case ACL_USER: case ACL_GROUP: not_equiv = 1; break; default: return -EINVAL; } ++entry; } if (mode_p) *mode_p = (*mode_p & ~ACCESSPERMS) | mode; return not_equiv; } int posix_acl_inherit_mode(bufferptr& acl, mode_t *mode_p) { if (posix_acl_check(acl.c_str(), acl.length()) <= 0) return -EIO; acl_ea_entry *group_entry = NULL, *mask_entry = NULL; mode_t mode = *mode_p; int not_equiv = 0; acl_ea_header *header = reinterpret_cast(acl.c_str()); acl_ea_entry *entry = header->a_entries; int count = (acl.length() - sizeof(*header)) / sizeof(*entry); for (int i = 0; i < count; ++i) { __u16 tag = entry->e_tag; __u16 perm = entry->e_perm; switch(tag) { case ACL_USER_OBJ: perm &= (mode >> 6) | ~S_IRWXO; mode &= (perm << 6) | ~S_IRWXU; entry->e_perm = perm; break; case ACL_USER: case ACL_GROUP: not_equiv = 1; break; case ACL_GROUP_OBJ: group_entry = entry; break; case ACL_OTHER: perm &= mode | ~S_IRWXO; mode &= perm | ~S_IRWXO; entry->e_perm = perm; break; case ACL_MASK: mask_entry = entry; not_equiv = 1; break; default: return -EIO; } ++entry; } if (mask_entry) { __u16 perm = mask_entry->e_perm; perm &= (mode >> 3) | ~S_IRWXO; mode &= (perm << 3) | ~S_IRWXG; mask_entry->e_perm = perm; } else { if (!group_entry) return -EIO; __u16 perm = group_entry->e_perm; perm &= (mode >> 3) | ~S_IRWXO; mode &= (perm << 3) | ~S_IRWXG; group_entry->e_perm = perm; } *mode_p = (*mode_p & ~ACCESSPERMS) | mode; return not_equiv; } int posix_acl_access_chmod(bufferptr& acl, mode_t mode) { if (posix_acl_check(acl.c_str(), acl.length()) <= 0) return -EIO; acl_ea_entry *group_entry = NULL, *mask_entry = NULL; acl_ea_header *header = reinterpret_cast(acl.c_str()); acl_ea_entry *entry = header->a_entries; int count = (acl.length() - sizeof(*header)) / sizeof(*entry); for (int i = 0; i < count; ++i) { __u16 tag = entry->e_tag; switch(tag) { case ACL_USER_OBJ: entry->e_perm = (mode & S_IRWXU) >> 6; break; case ACL_GROUP_OBJ: group_entry = entry; break; case ACL_MASK: mask_entry = entry; break; case ACL_OTHER: entry->e_perm = mode & S_IRWXO; break; default: break; } ++entry; } if (mask_entry) { mask_entry->e_perm = (mode & S_IRWXG) >> 3; } else { if (!group_entry) return -EIO; group_entry->e_perm = (mode & S_IRWXG) >> 3; } return 0; } int posix_acl_permits(const bufferptr& acl, uid_t i_uid, gid_t i_gid, const UserPerm& perms, unsigned want) { if (posix_acl_check(acl.c_str(), acl.length()) < 0) return -EIO; const acl_ea_header *header = reinterpret_cast(acl.c_str()); const acl_ea_entry *entry = header->a_entries; const acl_ea_entry *next_entry; __u16 perm, tag; __u32 id; int group_found = 0; int idx; int count = (acl.length() - sizeof(*header)) / sizeof(*entry); for (idx = 0; idx < count; ++idx) { tag = entry->e_tag; perm = entry->e_perm; switch(tag) { case ACL_USER_OBJ: if (i_uid == perms.uid()) goto check_perm; break; case ACL_USER: id = entry->e_id; if (id == perms.uid()) goto check_mask; break; case ACL_GROUP_OBJ: /* fall through */ case ACL_GROUP: id = (tag == ACL_GROUP_OBJ) ? i_gid : entry->e_id; if (perms.gid_in_groups(id)) { group_found = 1; if ((perm & want) == want) goto check_mask; } break; case ACL_MASK: break; case ACL_OTHER: if (group_found) return -EACCES; else goto check_perm; break; default: return -EIO; } ++entry; } return -EIO; check_mask: next_entry = entry + 1; for (++idx; idx < count; ++idx) { tag = next_entry->e_tag; if (tag == ACL_MASK) { __u16 mask = next_entry->e_perm; if ((perm & mask & want) == want) return 0; return -EACCES; } ++next_entry; } check_perm: if ((perm & want) == want) return 0; return -EACCES; }