#include "MetaSession.h"
#include "MetaRequest.h"
#include "ObjecterWriteback.h"
+#include "posix_acl.h"
#include "include/assert.h"
#include "include/stat.h"
user_id = cct->_conf->client_mount_uid;
group_id = cct->_conf->client_mount_gid;
+ acl_type = NO_ACL;
+ if (cct->_conf->client_acl_type == "posix_acl")
+ acl_type = POSIX_ACL;
+
lru.lru_set_max(cct->_conf->client_cache_size);
lru.lru_set_midpoint(cct->_conf->client_cache_mid);
}
}
#endif
+ unsigned want = 0;
+ if ((flags & O_ACCMODE) == O_WRONLY)
+ want = MAY_WRITE;
+ else if ((flags & O_ACCMODE) == O_RDWR)
+ want = MAY_READ | MAY_WRITE;
+ else if ((flags & O_ACCMODE) == O_RDONLY)
+ want = MAY_READ;
+
+ int ret = _posix_acl_permission(in, uid, gid, sgids, sgid_count, want);
+ if (ret != -EAGAIN)
+ return ret;
// check permissions before doing anything else
- int ret = 0;
- if (uid != 0 && !in->check_mode(uid, gid, sgids, sgid_count, flags)) {
+ if (uid == 0 || in->check_mode(uid, gid, sgids, sgid_count, want))
+ ret = 0;
+ else
ret = -EACCES;
- }
+
if (sgids)
free(sgids);
return ret;
return res;
}
-int Client::_setattr(Inode *in, struct stat *attr, int mask, int uid, int gid,
- InodeRef *inp)
+int Client::_do_setattr(Inode *in, struct stat *attr, int mask, int uid, int gid,
+ InodeRef *inp)
{
int issued = in->caps_issued();
return res;
}
+int Client::_setattr(Inode *in, struct stat *attr, int mask, int uid, int gid,
+ InodeRef *inp)
+{
+ int ret = _do_setattr(in, attr, mask, uid, gid, inp);
+ if (ret < 0)
+ return ret;
+ if (mask & CEPH_SETATTR_MODE)
+ ret = _posix_acl_chmod(in, attr->st_mode, uid, gid);
+ return ret;
+}
+
int Client::setattr(const char *relpath, struct stat *attr, int mask)
{
Mutex::Locker lock(client_lock);
goto out;
}
+ if (acl_type == NO_ACL && !strncmp(name, "system.", 7)) {
+ r = -EOPNOTSUPP;
+ goto out;
+ }
+
r = _getattr(in, CEPH_STAT_CAP_XATTR, uid, gid, in->xattr_version == 0);
if (r == 0) {
string n(name);
return _listxattr(in, names, size, uid, gid);
}
-int Client::_setxattr(Inode *in, const char *name, const void *value,
- size_t size, int flags, int uid, int gid)
+int Client::_do_setxattr(Inode *in, const char *name, const void *value,
+ size_t size, int flags, int uid, int gid)
{
- if (in->snapid != CEPH_NOSNAP) {
- return -EROFS;
- }
-
- // same xattrs supported by kernel client
- if (strncmp(name, "user.", 5) &&
- strncmp(name, "security.", 9) &&
- strncmp(name, "trusted.", 8) &&
- strncmp(name, "ceph.", 5))
- return -EOPNOTSUPP;
-
- const VXattr *vxattr = _match_vxattr(in, name);
- if (vxattr && vxattr->readonly)
- return -EOPNOTSUPP;
int xattr_flags = 0;
if (!value)
return res;
}
+int Client::_setxattr(Inode *in, const char *name, const void *value,
+ size_t size, int flags, int uid, int gid)
+{
+ if (in->snapid != CEPH_NOSNAP) {
+ return -EROFS;
+ }
+
+ bool posix_acl_xattr = false;
+ if (acl_type == POSIX_ACL)
+ posix_acl_xattr = !strncmp(name, "system.", 7);
+
+ if (strncmp(name, "user.", 5) &&
+ strncmp(name, "security.", 9) &&
+ strncmp(name, "trusted.", 8) &&
+ strncmp(name, "ceph.", 5) &&
+ !posix_acl_xattr)
+ return -EOPNOTSUPP;
+
+ if (posix_acl_xattr) {
+ if (!strcmp(name, ACL_EA_ACCESS)) {
+ mode_t new_mode = in->mode;
+ if (value) {
+ int ret = posix_acl_equiv_mode(value, size, &new_mode);
+ if (ret < 0)
+ return ret;
+ if (ret == 0) {
+ value = NULL;
+ size = 0;
+ }
+ if (new_mode != in->mode) {
+ struct stat attr;
+ attr.st_mode = new_mode;
+ ret = _do_setattr(in, &attr, CEPH_SETATTR_MODE, uid, gid, NULL);
+ if (ret < 0)
+ return ret;
+ }
+ }
+ } else if (!strcmp(name, ACL_EA_DEFAULT)) {
+ if (value) {
+ if (!S_ISDIR(in->mode))
+ return -EACCES;
+ if (!posix_acl_check(value, size))
+ return -EINVAL;
+ }
+ } else {
+ return -EOPNOTSUPP;
+ }
+ } else {
+ const VXattr *vxattr = _match_vxattr(in, name);
+ if (vxattr && vxattr->readonly)
+ return -EOPNOTSUPP;
+ }
+
+ return _do_setxattr(in, name, value, size, flags, uid, gid);
+}
+
int Client::check_data_pool_exist(string name, string value, const OSDMap *osdmap)
{
string tmp;
// same xattrs supported by kernel client
if (strncmp(name, "user.", 5) &&
+ strncmp(name, "system.", 7) &&
strncmp(name, "security.", 9) &&
strncmp(name, "trusted.", 8) &&
strncmp(name, "ceph.", 5))
path.push_dentry(name);
req->set_filepath(path);
req->set_inode(dir);
- req->head.args.mknod.mode = mode;
req->head.args.mknod.rdev = rdev;
req->dentry_drop = CEPH_CAP_FILE_SHARED;
req->dentry_unless = CEPH_CAP_FILE_EXCL;
+ bufferlist xattrs_bl;
+ int res = _posix_acl_create(dir, &mode, xattrs_bl, uid, gid);
+ if (res < 0)
+ goto fail;
+ req->head.args.mknod.mode = mode;
+ if (xattrs_bl.length() > 0)
+ req->set_data(xattrs_bl);
+
Dentry *de;
- int res = get_or_create(dir, name, &de);
+ res = get_or_create(dir, name, &de);
if (res < 0)
goto fail;
req->set_dentry(de);
req->set_filepath(path);
req->set_inode(dir);
req->head.args.open.flags = flags | O_CREAT;
- req->head.args.open.mode = mode;
req->head.args.open.stripe_unit = stripe_unit;
req->head.args.open.stripe_count = stripe_count;
req->dentry_drop = CEPH_CAP_FILE_SHARED;
req->dentry_unless = CEPH_CAP_FILE_EXCL;
- bufferlist extra_bl;
- inodeno_t created_ino;
+ mode |= S_IFREG;
+ bufferlist xattrs_bl;
+ int res = _posix_acl_create(dir, &mode, xattrs_bl, uid, gid);
+ if (res < 0)
+ goto fail;
+ req->head.args.open.mode = mode;
+ if (xattrs_bl.length() > 0)
+ req->set_data(xattrs_bl);
Dentry *de;
- int res = get_or_create(dir, name, &de);
+ res = get_or_create(dir, name, &de);
if (res < 0)
goto fail;
req->set_dentry(de);
path.push_dentry(name);
req->set_filepath(path);
req->set_inode(dir);
- req->head.args.mkdir.mode = mode;
req->dentry_drop = CEPH_CAP_FILE_SHARED;
req->dentry_unless = CEPH_CAP_FILE_EXCL;
+ mode |= S_IFDIR;
+ bufferlist xattrs_bl;
+ int res = _posix_acl_create(dir, &mode, xattrs_bl, uid, gid);
+ if (res < 0)
+ goto fail;
+ req->head.args.mkdir.mode = mode;
+ if (xattrs_bl.length() > 0)
+ req->set_data(xattrs_bl);
+
Dentry *de;
- int res = get_or_create(dir, name, &de);
+ res = get_or_create(dir, name, &de);
if (res < 0)
goto fail;
req->set_dentry(de);
return 0;
}
+int Client::_posix_acl_permission(Inode *in, int uid, int gid,
+ gid_t *sgids, int sgid_count, unsigned want)
+{
+ if (acl_type == POSIX_ACL) {
+ int r = _getattr(in, CEPH_STAT_CAP_XATTR | CEPH_STAT_CAP_MODE,
+ uid, gid, in->xattr_version == 0);
+ if (r < 0)
+ return r;
+
+ if (in->xattrs.count(ACL_EA_ACCESS)) {
+ const bufferptr& access_acl = in->xattrs[ACL_EA_ACCESS];
+ if (in->mode & S_IRWXG)
+ return posix_acl_permits(access_acl, in->uid, in->gid,
+ uid, gid, sgids, sgid_count, want);
+ }
+ }
+ return -EAGAIN;
+}
+
+int Client::_posix_acl_chmod(Inode *in, mode_t mode, int uid, int gid)
+{
+ if (acl_type == NO_ACL)
+ return 0;
+
+ int r = _getattr(in, CEPH_STAT_CAP_XATTR, uid, gid, in->xattr_version == 0);
+ if (r < 0)
+ goto out;
+
+ if (acl_type == POSIX_ACL) {
+ if (in->xattrs.count(ACL_EA_ACCESS)) {
+ const bufferptr& access_acl = in->xattrs[ACL_EA_ACCESS];
+ bufferptr acl(access_acl.c_str(), access_acl.length());
+ r = posix_acl_access_chmod(acl, mode);
+ if (r < 0)
+ goto out;
+ r = _do_setxattr(in, ACL_EA_ACCESS, acl.c_str(), acl.length(), 0, uid, gid);
+ } else {
+ r = 0;
+ }
+ }
+out:
+ ldout(cct, 10) << __func__ << " ino " << in->ino << " result=" << r << dendl;
+ return r;
+}
+
+int Client::_posix_acl_create(Inode *dir, mode_t *mode, bufferlist& xattrs_bl,
+ int uid, int gid)
+{
+ if (acl_type == NO_ACL)
+ return 0;
+
+ if (S_ISLNK(*mode))
+ return 0;
+
+ int r = _getattr(dir, CEPH_STAT_CAP_XATTR, uid, gid, dir->xattr_version == 0);
+ if (r < 0)
+ goto out;
+
+ if (acl_type == POSIX_ACL) {
+ if (dir->xattrs.count(ACL_EA_DEFAULT)) {
+ map<string, bufferptr> xattrs;
+
+ const bufferptr& default_acl = dir->xattrs[ACL_EA_DEFAULT];
+ bufferptr acl(default_acl.c_str(), default_acl.length());
+ r = posix_acl_inherit_mode(acl, mode);
+ if (r < 0)
+ goto out;
+
+ if (r > 0) {
+ r = posix_acl_equiv_mode(acl.c_str(), acl.length(), mode);
+ if (r < 0)
+ goto out;
+ if (r > 0)
+ xattrs[ACL_EA_ACCESS] = acl;
+ }
+
+ if (S_ISDIR(*mode))
+ xattrs[ACL_EA_DEFAULT] = dir->xattrs[ACL_EA_DEFAULT];
+
+ r = xattrs.size();
+ if (r > 0)
+ ::encode(xattrs, xattrs_bl);
+ } else {
+ r = 0;
+ }
+ }
+out:
+ ldout(cct, 10) << __func__ << " dir ino " << dir->ino << " result=" << r << dendl;
+ return r;
+}
+
void Client::set_filer_flags(int flags)
{
Mutex::Locker l(client_lock);
static const char* keys[] = {
"client_cache_size",
"client_cache_mid",
+ "client_acl_type",
NULL
};
return keys;
lru.lru_set_max(cct->_conf->client_cache_size);
lru.lru_set_midpoint(cct->_conf->client_cache_mid);
}
+ if (changed.count("client_acl_type")) {
+ acl_type = NO_ACL;
+ if (cct->_conf->client_acl_type == "posix_acl")
+ acl_type = POSIX_ACL;
+ }
}
void intrusive_ptr_add_ref(Inode *in)
client_t whoami;
int user_id, group_id;
+ int acl_type;
int get_uid() {
if (user_id >= 0)
int _rmdir(Inode *dir, const char *name, int uid=-1, int gid=-1);
int _symlink(Inode *dir, const char *name, const char *target, int uid=-1, int gid=-1, InodeRef *inp = 0);
int _mknod(Inode *dir, const char *name, mode_t mode, dev_t rdev, int uid=-1, int gid=-1, InodeRef *inp = 0);
+ int _do_setattr(Inode *in, struct stat *attr, int mask, int uid, int gid, InodeRef *inp);
int _setattr(Inode *in, struct stat *attr, int mask, int uid=-1, int gid=-1, InodeRef *inp = 0);
int _setattr(InodeRef &in, struct stat *attr, int mask, int uid=-1, int gid=-1, InodeRef *inp = 0) {
return _setattr(in.get(), attr, mask, uid, gid, inp);
int _readlink(Inode *in, char *buf, size_t size);
int _getxattr(Inode *in, const char *name, void *value, size_t len, int uid=-1, int gid=-1);
int _listxattr(Inode *in, char *names, size_t len, int uid=-1, int gid=-1);
+ int _do_setxattr(Inode *in, const char *name, const void *value, size_t len, int flags, int uid, int gid);
int _setxattr(Inode *in, const char *name, const void *value, size_t len, int flags, int uid=-1, int gid=-1);
int _removexattr(Inode *in, const char *nm, int uid=-1, int gid=-1);
int _open(Inode *in, int flags, mode_t mode, Fh **fhp, int uid=-1, int gid=-1);
int get_or_create(Inode *dir, const char* name,
Dentry **pdn, bool expect_null=false);
+ enum {
+ NO_ACL = 0,
+ POSIX_ACL,
+ };
+
+ enum {
+ MAY_EXEC = 1,
+ MAY_WRITE = 2,
+ MAY_READ = 4,
+ };
+
int check_permissions(Inode *in, int flags, int uid, int gid);
int check_data_pool_exist(string name, string value, const OSDMap *osdmap);
void _encode_filelocks(Inode *in, bufferlist& bl);
void _release_filelocks(Fh *fh);
void _update_lock_state(struct flock *fl, uint64_t owner, ceph_lock_state_t *lock_state);
+
+ int _posix_acl_create(Inode *dir, mode_t *mode, bufferlist& xattrs_bl, int uid, int gid);
+ int _posix_acl_chmod(Inode *in, mode_t mode, int uid, int gid);
+ int _posix_acl_permission(Inode *in, int uid, int gid,
+ gid_t *sgids, int sgid_count, unsigned want);
public:
int mount(const std::string &mount_root, bool require_mds=false);
void unmount();
--- /dev/null
+#include "include/types.h"
+#include <sys/stat.h>
+#include "posix_acl.h"
+
+int posix_acl_check(const void *xattr, size_t size)
+{
+ const acl_ea_header *header;
+ if (size < sizeof(*header))
+ return 0;
+ header = reinterpret_cast<const acl_ea_header*>(xattr);
+ ceph_le32 expected_version;
+ expected_version = ACL_EA_VERSION;
+ if (header->a_version != expected_version)
+ return 0;
+
+ const acl_ea_entry *entry = header->a_entries;
+ size -= sizeof(*header);
+ if (size % sizeof(*entry))
+ return 0;
+
+ int count = size / sizeof(*entry);
+ 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 0;
+ case ACL_USER:
+ if (state != ACL_USER)
+ return 0;
+ needs_mask = 1;
+ break;
+ case ACL_GROUP_OBJ:
+ if (state == ACL_USER) {
+ state = ACL_GROUP;
+ break;
+ }
+ return 0;
+ case ACL_GROUP:
+ if (state != ACL_GROUP)
+ return 0;
+ needs_mask = 1;
+ break;
+ case ACL_MASK:
+ if (state != ACL_GROUP)
+ return 0;
+ state = ACL_OTHER;
+ break;
+ case ACL_OTHER:
+ if (state == ACL_OTHER ||
+ (state == ACL_GROUP && !needs_mask)) {
+ state = 0;
+ break;
+ }
+ // fall-thru
+ default:
+ return 0;
+ }
+ ++entry;
+ }
+
+ return state == 0;
+}
+
+int posix_acl_equiv_mode(const void *xattr, size_t size, mode_t *mode_p)
+{
+ if (!posix_acl_check(xattr, size))
+ return -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 -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()))
+ 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_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 -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()))
+ return -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 -EIO;
+ group_entry->e_perm = (mode & S_IRWXG) >> 3;
+ }
+ return 0;
+}
+
+static int in_grouplist(gid_t gid, gid_t *sgids, int sgid_count)
+{
+ for (int i = 0; i < sgid_count; i++) {
+ if (sgids[i] == gid)
+ return 1;
+ }
+ return 0;
+}
+
+int posix_acl_permits(const bufferptr& acl, uid_t i_uid, gid_t i_gid,
+ uid_t uid, gid_t gid, gid_t *sgids, int sgid_count,
+ unsigned want)
+{
+ if (!posix_acl_check(acl.c_str(), acl.length()))
+ return -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 == uid)
+ goto check_perm;
+ break;
+ case ACL_USER:
+ id = entry->e_id;
+ if (id == 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 (id == gid || in_grouplist(id, sgids, sgid_count)) {
+ 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;
+}