summaryrefslogtreecommitdiffstats
path: root/src/client/posix_acl.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/posix_acl.cc')
-rw-r--r--src/client/posix_acl.cc288
1 files changed, 288 insertions, 0 deletions
diff --git a/src/client/posix_acl.cc b/src/client/posix_acl.cc
new file mode 100644
index 000000000..909bb0c2b
--- /dev/null
+++ b/src/client/posix_acl.cc
@@ -0,0 +1,288 @@
+#include "include/compat.h"
+#include "include/types.h"
+#include "include/fs_types.h"
+#include <sys/stat.h>
+#include "posix_acl.h"
+#include "UserPerm.h"
+
+int posix_acl_check(const void *xattr, size_t size)
+{
+ const acl_ea_header *header;
+ if (size < sizeof(*header))
+ return -1;
+ header = reinterpret_cast<const acl_ea_header*>(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 -CEPHFS_EINVAL;
+
+ int not_equiv = 0;
+ mode_t mode = 0;
+
+ const acl_ea_header *header = reinterpret_cast<const acl_ea_header*>(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 -CEPHFS_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 -CEPHFS_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_ea_header*>(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 -CEPHFS_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 -CEPHFS_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 -CEPHFS_EIO;
+
+ acl_ea_entry *group_entry = NULL, *mask_entry = NULL;
+
+ acl_ea_header *header = reinterpret_cast<acl_ea_header*>(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 -CEPHFS_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 -CEPHFS_EIO;
+
+ const acl_ea_header *header = reinterpret_cast<const acl_ea_header*>(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 -CEPHFS_EACCES;
+ else
+ goto check_perm;
+ break;
+ default:
+ return -CEPHFS_EIO;
+ }
+ ++entry;
+ }
+ return -CEPHFS_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 -CEPHFS_EACCES;
+ }
+ ++next_entry;
+ }
+check_perm:
+ if ((perm & want) == want)
+ return 0;
+ return -CEPHFS_EACCES;
+}