/* Copyright (c) 2011 Gluster, Inc. This file is part of GlusterFS. GlusterFS is free software; you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. GlusterFS is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ #include #include "xlator.h" #include "glusterfs.h" #include "posix-acl.h" #include "posix-acl-xattr.h" #define UINT64(ptr) ((uint64_t)((long)(ptr))) #define PTR(num) ((void *)((long)(num))) int whitelisted_xattr (const char *key) { if (!key) return 0; if (strcmp (POSIX_ACL_ACCESS_XATTR, key) == 0) return 1; if (strcmp (POSIX_ACL_DEFAULT_XATTR, key) == 0) return 1; return 0; } int frame_is_user (call_frame_t *frame, uid_t uid) { return (frame->root->uid == uid); } int frame_in_group (call_frame_t *frame, gid_t gid) { int i = 0; if (frame->root->gid == gid) return 1; for (i = 0; i < frame->root->ngrps; i++) if (frame->root->groups[i] == gid) return 1; return 0; } mode_t posix_acl_access_set_mode (struct posix_acl *acl, struct posix_acl_ctx *ctx) { struct posix_ace *ace = NULL; struct posix_ace *group_ce = NULL; struct posix_ace *mask_ce = NULL; int count = 0; int i = 0; mode_t mode = 0; int mask = 0; count = acl->count; ace = acl->entries; for (i = 0; i < count; i++) { switch (ace->tag) { case POSIX_ACL_USER_OBJ: mask |= S_IRWXU; mode |= (ace->perm << 6); break; case POSIX_ACL_GROUP_OBJ: group_ce = ace; break; case POSIX_ACL_MASK: mask_ce = ace; break; case POSIX_ACL_OTHER: mask |= S_IRWXO; mode |= (ace->perm); break; } ace++; } if (mask_ce) { mask |= S_IRWXG; mode |= (mask_ce->perm << 3); } else { if (!group_ce) goto out; mask |= S_IRWXG; mode |= (group_ce->perm << 3); } out: ctx->perm = (ctx->perm & ~mask) | mode; return mode; } static int sticky_permits (call_frame_t *frame, inode_t *parent, inode_t *inode) { struct posix_acl_ctx *par = NULL; struct posix_acl_ctx *ctx = NULL; par = posix_acl_ctx_get (parent, frame->this); ctx = posix_acl_ctx_get (inode, frame->this); if (frame_is_user (frame, 0)) return 1; if (!(par->perm & S_ISVTX)) return 1; if (frame_is_user (frame, par->uid)) return 1; if (frame_is_user (frame, ctx->uid)) return 1; return 0; } static int acl_permits (call_frame_t *frame, inode_t *inode, int want) { int verdict = 0; int ret = 0; struct posix_acl *acl = NULL; struct posix_ace *ace = NULL; struct posix_acl_ctx *ctx = NULL; struct posix_acl_conf *conf = NULL; int i = 0; int perm = 0; int found = 0; int acl_present = 0; conf = frame->this->private; ctx = posix_acl_ctx_get (inode, frame->this); if (!ctx) goto red; if (frame->root->uid == 0) goto green; ret = posix_acl_get (inode, frame->this, &acl, NULL); if (!acl) { acl = posix_acl_ref (frame->this, conf->minimal_acl); } ace = acl->entries; if (acl->count > 3) acl_present = 1; for (i = 0; i < acl->count; i++) { switch (ace->tag) { case POSIX_ACL_USER_OBJ: perm = ((ctx->perm & S_IRWXU) >> 6); if (frame_is_user (frame, ctx->uid)) goto perm_check; break; case POSIX_ACL_USER: perm = ace->perm; if (frame_is_user (frame, ace->id)) goto mask_check; break; case POSIX_ACL_GROUP_OBJ: if (acl_present) perm = ace->perm; else perm = ((ctx->perm & S_IRWXG) >> 3); if (frame_in_group (frame, ctx->gid)) { found = 1; if ((perm & want) == want) goto mask_check; } break; case POSIX_ACL_GROUP: perm = ace->perm; if (frame_in_group (frame, ace->id)) { found = 1; if ((perm & want) == want) goto mask_check; } break; case POSIX_ACL_MASK: break; case POSIX_ACL_OTHER: perm = (ctx->perm & S_IRWXO); if (!found) goto perm_check; /* fall through */ default: goto red; } ace++; } mask_check: ace = acl->entries; for (i = 0; i < acl->count; i++, ace++) { if (ace->tag != POSIX_ACL_MASK) continue; if ((ace->perm & perm & want) == want) { verdict = ace->perm & perm; goto green; } goto red; } perm_check: if ((perm & want) == want) { verdict = perm & want; goto green; } else { goto red; } green: if (!want) verdict = 1; if (!verdict) verdict = want; goto out; red: verdict = 0; out: if (acl) posix_acl_unref (frame->this, acl); return verdict; } struct posix_acl_ctx * posix_acl_ctx_get (inode_t *inode, xlator_t *this) { struct posix_acl_ctx *ctx = NULL; uint64_t int_ctx = 0; int ret = 0; ret = inode_ctx_get (inode, this, &int_ctx); if ((ret == 0) && (int_ctx)) return PTR(int_ctx); ctx = CALLOC (1, sizeof (*ctx)); if (!ctx) return NULL; ret = inode_ctx_put (inode, this, UINT64 (ctx)); return ctx; } int __posix_acl_set (inode_t *inode, xlator_t *this, struct posix_acl *acl_access, struct posix_acl *acl_default) { int ret = 0; struct posix_acl_ctx *ctx = NULL; ctx = posix_acl_ctx_get (inode, this); if (!ctx) goto out; ctx->acl_access = acl_access; ctx->acl_default = acl_default; out: return ret; } int __posix_acl_get (inode_t *inode, xlator_t *this, struct posix_acl **acl_access_p, struct posix_acl **acl_default_p) { int ret = 0; struct posix_acl_ctx *ctx = NULL; ctx = posix_acl_ctx_get (inode, this); if (!ctx) goto out; if (acl_access_p) *acl_access_p = ctx->acl_access; if (acl_default_p) *acl_default_p = ctx->acl_default; out: return ret; } struct posix_acl * posix_acl_new (xlator_t *this, int entrycnt) { struct posix_acl *acl = NULL; struct posix_ace *ace = NULL; acl = CALLOC (1, sizeof (*acl) + (entrycnt * sizeof (*ace))); if (!acl) return NULL; acl->count = entrycnt; posix_acl_ref (this, acl); return acl; } void posix_acl_destroy (xlator_t *this, struct posix_acl *acl) { FREE (acl); return; } struct posix_acl * posix_acl_ref (xlator_t *this, struct posix_acl *acl) { struct posix_acl_conf *conf = NULL; conf = this->private; LOCK(&conf->acl_lock); { acl->refcnt++; } UNLOCK(&conf->acl_lock); return acl; } struct posix_acl * posix_acl_dup (xlator_t *this, struct posix_acl *acl) { struct posix_acl_conf *conf = NULL; struct posix_acl *dup = NULL; conf = this->private; dup = posix_acl_new (this, acl->count); if (!dup) return NULL; memcpy (dup->entries, acl->entries, sizeof (struct posix_ace) * acl->count); return dup; } void posix_acl_unref (xlator_t *this, struct posix_acl *acl) { struct posix_acl_conf *conf = NULL; int refcnt = 0; conf = this->private; LOCK(&conf->acl_lock); { refcnt = --acl->refcnt; } UNLOCK(&conf->acl_lock); if (!refcnt) posix_acl_destroy (this, acl); } int posix_acl_set (inode_t *inode, xlator_t *this, struct posix_acl *acl_access, struct posix_acl *acl_default) { int ret = 0; int oldret = 0; struct posix_acl *old_access = NULL; struct posix_acl *old_default = NULL; struct posix_acl_conf *conf = NULL; conf = this->private; LOCK(&conf->acl_lock); { oldret = __posix_acl_get (inode, this, &old_access, &old_default); if (acl_access) acl_access->refcnt++; if (acl_default) acl_default->refcnt++; ret = __posix_acl_set (inode, this, acl_access, acl_default); } UNLOCK(&conf->acl_lock); if (oldret == 0) { if (old_access) posix_acl_unref (this, old_access); if (old_default) posix_acl_unref (this, old_default); } return ret; } int posix_acl_get (inode_t *inode, xlator_t *this, struct posix_acl **acl_access_p, struct posix_acl **acl_default_p) { struct posix_acl_conf *conf = NULL; struct posix_acl *acl_access = NULL; struct posix_acl *acl_default = NULL; int ret = 0; conf = this->private; LOCK(&conf->acl_lock); { ret = __posix_acl_get (inode, this, &acl_access, &acl_default); if (ret != 0) goto unlock; if (acl_access && acl_access_p) acl_access->refcnt++; if (acl_default && acl_default_p) acl_default->refcnt++; } unlock: UNLOCK(&conf->acl_lock); if (acl_access_p) *acl_access_p = acl_access; if (acl_default_p) *acl_default_p = acl_default; return ret; } mode_t posix_acl_inherit_mode (struct posix_acl *acl, mode_t modein) { struct posix_ace *ace = NULL; int count = 0; int i = 0; mode_t newmode = 0; mode_t mode = 0; struct posix_ace *mask_ce = NULL; struct posix_ace *group_ce = NULL; newmode = mode = modein; count = acl->count; ace = acl->entries; for (i = 0; i < count; i++) { switch (ace->tag) { case POSIX_ACL_USER_OBJ: ace->perm &= (mode >> 6) | ~S_IRWXO; mode &= (ace->perm << 6) | ~S_IRWXU; break; case POSIX_ACL_GROUP_OBJ: group_ce = ace; break; case POSIX_ACL_MASK: mask_ce = ace; break; case POSIX_ACL_OTHER: ace->perm &= (mode) | ~S_IRWXO; mode &= (ace->perm) | ~S_IRWXO; break; } ace++; } if (mask_ce) { mask_ce->perm &= (mode >> 3) | ~S_IRWXO; mode &= (mask_ce->perm << 3) | ~S_IRWXG; } else { group_ce->perm &= (mode >> 3) | ~S_IRWXO; mode &= (group_ce->perm << 3) | ~S_IRWXG; } newmode = ((modein & S_IFMT) | (mode & (S_IRWXU|S_IRWXG|S_IRWXO))); return newmode; } mode_t posix_acl_inherit (xlator_t *this, loc_t *loc, dict_t *params, mode_t mode, int is_dir) { int ret = 0; struct posix_acl *par_default = NULL; struct posix_acl *acl_default = NULL; struct posix_acl *acl_access = NULL; struct posix_acl_ctx *ctx = NULL; char *xattr_default = NULL; char *xattr_access = NULL; int size_default = 0; int size_access = 0; mode_t retmode = 0; retmode = mode; ret = posix_acl_get (loc->parent, this, NULL, &par_default); if (!par_default) goto out; ctx = posix_acl_ctx_get (loc->inode, this); acl_access = posix_acl_dup (this, par_default); if (!acl_access) goto out; retmode = posix_acl_inherit_mode (acl_access, mode); ctx->perm = retmode; size_access = posix_acl_to_xattr (this, acl_access, NULL, 0); xattr_access = CALLOC (1, size_access); if (!xattr_access) { gf_log (this->name, GF_LOG_ERROR, "out of memory"); ret = -1; goto out; } posix_acl_to_xattr (this, acl_access, xattr_access, size_access); ret = dict_set_bin (params, POSIX_ACL_ACCESS_XATTR, xattr_access, size_access); if (ret) { gf_log (this->name, GF_LOG_ERROR, "out of memory"); ret = -1; goto out; } if (!is_dir) goto set; acl_default = posix_acl_ref (this, par_default); size_default = posix_acl_to_xattr (this, acl_default, NULL, 0); xattr_default = CALLOC (1, size_default); if (!xattr_default) { gf_log (this->name, GF_LOG_ERROR, "out of memory"); ret = -1; goto out; } posix_acl_to_xattr (this, acl_default, xattr_default, size_default); ret = dict_set_bin (params, POSIX_ACL_DEFAULT_XATTR, xattr_default, size_default); if (ret) { gf_log (this->name, GF_LOG_ERROR, "out of memory"); ret = -1; goto out; } set: ret = posix_acl_set (loc->inode, this, acl_access, acl_default); if (ret != 0) goto out; out: if (par_default) posix_acl_unref (this, par_default); if (acl_access) posix_acl_unref (this, acl_access); if (acl_default) posix_acl_unref (this, acl_default); return retmode; } mode_t posix_acl_inherit_dir (xlator_t *this, loc_t *loc, dict_t *params, mode_t mode) { mode_t retmode = 0; retmode = posix_acl_inherit (this, loc, params, mode, 1); return retmode; } mode_t posix_acl_inherit_file (xlator_t *this, loc_t *loc, dict_t *params, mode_t mode) { mode_t retmode = 0; retmode = posix_acl_inherit (this, loc, params, mode, 0); return retmode; } int posix_acl_ctx_update (inode_t *inode, xlator_t *this, struct iatt *buf) { struct posix_acl_ctx *ctx = NULL; int ret = 0; ctx = posix_acl_ctx_get (inode, this); if (!ctx) { ret = -1; goto out; } LOCK(&inode->lock); { ctx->uid = buf->ia_uid; ctx->gid = buf->ia_gid; ctx->perm = st_mode_from_ia (buf->ia_prot, buf->ia_type); } UNLOCK(&inode->lock); out: return ret; } int posix_acl_lookup_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int op_ret, int op_errno, inode_t *inode, struct iatt *buf, dict_t *xattr, struct iatt *postparent) { struct posix_acl *acl_access = NULL; struct posix_acl *acl_default = NULL; struct posix_acl *old_access = NULL; struct posix_acl *old_default = NULL; data_t *data = NULL; int ret = 0; dict_t *my_xattr = NULL; if (op_ret != 0) goto unwind; ret = posix_acl_get (inode, this, &old_access, &old_default); data = dict_get (xattr, POSIX_ACL_ACCESS_XATTR); if (!data) goto acl_default; if (old_access && posix_acl_matches_xattr (this, old_access, data->data, data->len)) { acl_access = posix_acl_ref (this, old_access); } else { acl_access = posix_acl_from_xattr (this, data->data, data->len); } acl_default: data = dict_get (xattr, POSIX_ACL_DEFAULT_XATTR); if (!data) goto acl_set; if (old_default && posix_acl_matches_xattr (this, old_default, data->data, data->len)) { acl_default = posix_acl_ref (this, old_default); } else { acl_default = posix_acl_from_xattr (this, data->data, data->len); } acl_set: posix_acl_ctx_update (inode, this, buf); ret = posix_acl_set (inode, this, acl_access, acl_default); unwind: my_xattr = frame->local; frame->local = NULL; STACK_UNWIND_STRICT (lookup, frame, op_ret, op_errno, inode, buf, xattr, postparent); if (acl_access) posix_acl_unref (this, acl_access); if (acl_default) posix_acl_unref (this, acl_default); if (old_access) posix_acl_unref (this, old_access); if (old_default) posix_acl_unref (this, old_default); if (my_xattr) dict_unref (my_xattr); return 0; } int posix_acl_lookup (call_frame_t *frame, xlator_t *this, loc_t *loc, dict_t *xattr) { int ret = 0; dict_t *my_xattr = NULL; if (!loc->parent) /* lookup of / is always permitted */ goto green; if (acl_permits (frame, loc->parent, POSIX_ACL_EXECUTE)) goto green; else goto red; green: if (xattr) { my_xattr = dict_ref (xattr); } else { my_xattr = dict_new (); } ret = dict_set_int8 (my_xattr, POSIX_ACL_ACCESS_XATTR, 0); ret = dict_set_int8 (my_xattr, POSIX_ACL_DEFAULT_XATTR, 0); frame->local = my_xattr; STACK_WIND (frame, posix_acl_lookup_cbk, FIRST_CHILD (this), FIRST_CHILD (this)->fops->lookup, loc, my_xattr); return 0; red: STACK_UNWIND_STRICT (lookup, frame, -1, EACCES, NULL, NULL, NULL, NULL); return 0; } int posix_acl_access (call_frame_t *frame, xlator_t *this, loc_t *loc, int mask) { int op_ret = 0; int op_errno = 0; int perm = 0; int mode = 0; if (mask & R_OK) perm |= POSIX_ACL_READ; if (mask & W_OK) perm |= POSIX_ACL_WRITE; if (mask & X_OK) perm |= POSIX_ACL_EXECUTE; if (!perm) { op_ret = -1; op_errno = EINVAL; goto unwind; } mode = acl_permits (frame, loc->inode, perm); if (mode) { op_ret = 0; op_errno = 0; } else { op_ret = -1; op_errno = EACCES; } unwind: if (__is_fuse_call (frame)) STACK_UNWIND_STRICT (access, frame, op_ret, op_errno); else STACK_UNWIND_STRICT (access, frame, 0, mode); return 0; } int posix_acl_truncate_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int op_ret, int op_errno, struct iatt *prebuf, struct iatt *postbuf) { STACK_UNWIND_STRICT (truncate, frame, op_ret, op_errno, prebuf, postbuf); return 0; } int posix_acl_truncate (call_frame_t *frame, xlator_t *this, loc_t *loc, off_t off) { if (acl_permits (frame, loc->inode, POSIX_ACL_WRITE)) goto green; else goto red; green: STACK_WIND (frame, posix_acl_truncate_cbk, FIRST_CHILD(this), FIRST_CHILD(this)->fops->truncate, loc, off); return 0; red: STACK_UNWIND_STRICT (truncate, frame, -1, EACCES, NULL, NULL); return 0; } int posix_acl_open_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int op_ret, int op_errno, fd_t *fd) { STACK_UNWIND_STRICT (open, frame, op_ret, op_errno, fd); return 0; } int posix_acl_open (call_frame_t *frame, xlator_t *this, loc_t *loc, int flags, fd_t *fd, int wbflags) { int perm = 0; switch (flags & O_ACCMODE) { case O_RDONLY: perm = POSIX_ACL_READ; break; case O_WRONLY: perm = POSIX_ACL_WRITE; break; case O_RDWR: perm = POSIX_ACL_READ|POSIX_ACL_WRITE; break; } if (acl_permits (frame, loc->inode, perm)) goto green; else goto red; green: STACK_WIND (frame, posix_acl_open_cbk, FIRST_CHILD(this), FIRST_CHILD(this)->fops->open, loc, flags, fd, wbflags); return 0; red: STACK_UNWIND_STRICT (open, frame, -1, EACCES, NULL); return 0; } int posix_acl_opendir_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int op_ret, int op_errno, fd_t *fd) { STACK_UNWIND_STRICT (opendir, frame, op_ret, op_errno, fd); return 0; } int posix_acl_opendir (call_frame_t *frame, xlator_t *this, loc_t *loc, fd_t *fd) { if (acl_permits (frame, loc->inode, POSIX_ACL_READ)) goto green; else goto red; green: STACK_WIND (frame, posix_acl_opendir_cbk, FIRST_CHILD(this), FIRST_CHILD(this)->fops->opendir, loc, fd); return 0; red: STACK_UNWIND_STRICT (opendir, frame, -1, EACCES, NULL); return 0; } int posix_acl_mkdir_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int op_ret, int op_errno, inode_t *inode, struct iatt *buf, struct iatt *preparent, struct iatt *postparent) { if (op_ret != 0) goto unwind; posix_acl_ctx_update (inode, this, buf); unwind: STACK_UNWIND_STRICT (mkdir, frame, op_ret, op_errno, inode, buf, preparent, postparent); return 0; } int posix_acl_mkdir (call_frame_t *frame, xlator_t *this, loc_t *loc, mode_t mode, dict_t *params) { mode_t newmode = 0; newmode = mode; if (acl_permits (frame, loc->parent, POSIX_ACL_WRITE|POSIX_ACL_EXECUTE)) goto green; else goto red; green: newmode = posix_acl_inherit_dir (this, loc, params, mode); STACK_WIND (frame, posix_acl_mkdir_cbk, FIRST_CHILD(this), FIRST_CHILD(this)->fops->mkdir, loc, newmode, params); return 0; red: STACK_UNWIND_STRICT (mkdir, frame, -1, EACCES, NULL, NULL, NULL, NULL); return 0; } int posix_acl_mknod_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int op_ret, int op_errno, inode_t *inode, struct iatt *buf, struct iatt *preparent, struct iatt *postparent) { if (op_ret != 0) goto unwind; posix_acl_ctx_update (inode, this, buf); unwind: STACK_UNWIND_STRICT (mknod, frame, op_ret, op_errno, inode, buf, preparent, postparent); return 0; } int posix_acl_mknod (call_frame_t *frame, xlator_t *this, loc_t *loc, mode_t mode, dev_t rdev, dict_t *params) { mode_t newmode = 0; newmode = mode; if (acl_permits (frame, loc->parent, POSIX_ACL_WRITE|POSIX_ACL_EXECUTE)) goto green; else goto red; green: newmode = posix_acl_inherit_file (this, loc, params, mode); STACK_WIND (frame, posix_acl_mknod_cbk, FIRST_CHILD(this), FIRST_CHILD(this)->fops->mknod, loc, newmode, rdev, params); return 0; red: STACK_UNWIND_STRICT (mknod, frame, -1, EACCES, NULL, NULL, NULL, NULL); return 0; } int posix_acl_create_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int op_ret, int op_errno, fd_t *fd, inode_t *inode, struct iatt *buf, struct iatt *preparent, struct iatt *postparent) { if (op_ret != 0) goto unwind; posix_acl_ctx_update (inode, this, buf); unwind: STACK_UNWIND_STRICT (create, frame, op_ret, op_errno, fd, inode, buf, preparent, postparent); return 0; } int posix_acl_create (call_frame_t *frame, xlator_t *this, loc_t *loc, int flags, mode_t mode, fd_t *fd, dict_t *params) { mode_t newmode = 0; newmode = mode; if (acl_permits (frame, loc->parent, POSIX_ACL_WRITE|POSIX_ACL_EXECUTE)) goto green; else goto red; green: newmode = posix_acl_inherit_file (this, loc, params, mode); STACK_WIND (frame, posix_acl_create_cbk, FIRST_CHILD(this), FIRST_CHILD(this)->fops->create, loc, flags, newmode, fd, params); return 0; red: STACK_UNWIND_STRICT (create, frame, -1, EACCES, NULL, NULL, NULL, NULL, NULL); return 0; } int posix_acl_symlink_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int op_ret, int op_errno, inode_t *inode, struct iatt *buf, struct iatt *preparent, struct iatt *postparent) { if (op_ret != 0) goto unwind; posix_acl_ctx_update (inode, this, buf); unwind: STACK_UNWIND_STRICT (symlink, frame, op_ret, op_errno, inode, buf, preparent, postparent); return 0; } int posix_acl_symlink (call_frame_t *frame, xlator_t *this, const char *linkname, loc_t *loc, dict_t *params) { if (acl_permits (frame, loc->parent, POSIX_ACL_WRITE|POSIX_ACL_EXECUTE)) goto green; else goto red; green: STACK_WIND (frame, posix_acl_symlink_cbk, FIRST_CHILD(this), FIRST_CHILD(this)->fops->symlink, linkname, loc, params); return 0; red: STACK_UNWIND_STRICT (symlink, frame, -1, EACCES, NULL, NULL, NULL, NULL); return 0; } int posix_acl_unlink_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int op_ret, int op_errno, struct iatt *preparent, struct iatt *postparent) { if (op_ret != 0) goto unwind; unwind: STACK_UNWIND_STRICT (unlink, frame, op_ret, op_errno, preparent, postparent); return 0; } int posix_acl_unlink (call_frame_t *frame, xlator_t *this, loc_t *loc) { if (!sticky_permits (frame, loc->parent, loc->inode)) goto red; if (acl_permits (frame, loc->parent, POSIX_ACL_WRITE|POSIX_ACL_EXECUTE)) goto green; else goto red; green: STACK_WIND (frame, posix_acl_unlink_cbk, FIRST_CHILD(this), FIRST_CHILD(this)->fops->unlink, loc); return 0; red: STACK_UNWIND_STRICT (unlink, frame, -1, EACCES, NULL, NULL); return 0; } int posix_acl_rmdir_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int op_ret, int op_errno, struct iatt *preparent, struct iatt *postparent) { if (op_ret != 0) goto unwind; unwind: STACK_UNWIND_STRICT (rmdir, frame, op_ret, op_errno, preparent, postparent); return 0; } int posix_acl_rmdir (call_frame_t *frame, xlator_t *this, loc_t *loc, int flags) { if (!sticky_permits (frame, loc->parent, loc->inode)) goto red; if (acl_permits (frame, loc->parent, POSIX_ACL_WRITE|POSIX_ACL_EXECUTE)) goto green; else goto red; green: STACK_WIND (frame, posix_acl_rmdir_cbk, FIRST_CHILD(this), FIRST_CHILD(this)->fops->rmdir, loc, flags); return 0; red: STACK_UNWIND_STRICT (rmdir, frame, -1, EACCES, NULL, NULL); return 0; } int posix_acl_rename_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int op_ret, int op_errno, struct iatt *buf, struct iatt *preoldparent, struct iatt *postoldparent, struct iatt *prenewparent, struct iatt *postnewparent) { if (op_ret != 0) goto unwind; unwind: STACK_UNWIND_STRICT (rename, frame, op_ret, op_errno, buf, preoldparent, postoldparent, prenewparent, postnewparent); return 0; } int posix_acl_rename (call_frame_t *frame, xlator_t *this, loc_t *old, loc_t *new) { if (!acl_permits (frame, old->parent, POSIX_ACL_WRITE)) goto red; if (!acl_permits (frame, new->parent, POSIX_ACL_WRITE)) goto red; if (!sticky_permits (frame, old->parent, old->inode)) goto red; if (new->inode) { if (!sticky_permits (frame, new->parent, new->inode)) goto red; } STACK_WIND (frame, posix_acl_rename_cbk, FIRST_CHILD(this), FIRST_CHILD(this)->fops->rename, old, new); return 0; red: STACK_UNWIND_STRICT (rename, frame, -1, EACCES, NULL, NULL, NULL, NULL, NULL); return 0; } int posix_acl_link_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int op_ret, int op_errno, inode_t *inode, struct iatt *buf, struct iatt *preparent, struct iatt *postparent) { if (op_ret != 0) goto unwind; unwind: STACK_UNWIND_STRICT (link, frame, op_ret, op_errno, inode, buf, preparent, postparent); return 0; } int posix_acl_link (call_frame_t *frame, xlator_t *this, loc_t *old, loc_t *new) { struct posix_acl_ctx *ctx = NULL; int op_errno = 0; ctx = posix_acl_ctx_get (old->inode, this); if (!ctx) { op_errno = EIO; goto red; } if (!acl_permits (frame, new->parent, POSIX_ACL_WRITE)) { op_errno = EACCES; goto red; } STACK_WIND (frame, posix_acl_link_cbk, FIRST_CHILD(this), FIRST_CHILD(this)->fops->link, old, new); return 0; red: STACK_UNWIND_STRICT (link, frame, -1, op_errno, NULL, NULL, NULL, NULL); return 0; } int posix_acl_readdir_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int op_ret, int op_errno, gf_dirent_t *entries) { if (op_ret != 0) goto unwind; unwind: STACK_UNWIND_STRICT (readdir, frame, op_ret, op_errno, entries); return 0; } int posix_acl_readdir (call_frame_t *frame, xlator_t *this, fd_t *fd, size_t size, off_t offset) { if (acl_permits (frame, fd->inode, POSIX_ACL_READ)) goto green; else goto red; green: STACK_WIND (frame, posix_acl_readdir_cbk, FIRST_CHILD(this), FIRST_CHILD(this)->fops->readdir, fd, size, offset); return 0; red: STACK_UNWIND_STRICT (readdir, frame, -1, EACCES, NULL); return 0; } int posix_acl_readdirp_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int op_ret, int op_errno, gf_dirent_t *entries) { if (op_ret != 0) goto unwind; unwind: STACK_UNWIND_STRICT (readdirp, frame, op_ret, op_errno, entries); return 0; } int posix_acl_readdirp (call_frame_t *frame, xlator_t *this, fd_t *fd, size_t size, off_t offset) { if (acl_permits (frame, fd->inode, POSIX_ACL_READ)) goto green; else goto red; green: STACK_WIND (frame, posix_acl_readdirp_cbk, FIRST_CHILD(this), FIRST_CHILD(this)->fops->readdirp, fd, size, offset); return 0; red: STACK_UNWIND_STRICT (readdirp, frame, -1, EACCES, NULL); return 0; } int setattr_scrutiny (call_frame_t *frame, inode_t *inode, struct iatt *buf, int valid) { struct posix_acl_ctx *ctx = NULL; if (frame->root->uid == 0) return 0; ctx = posix_acl_ctx_get (inode, frame->this); if (!ctx) return EIO; if (valid & GF_SET_ATTR_MODE) { /* The effective UID of the calling process must match the owner of the file, or the process must be privileged */ if (!frame_is_user (frame, ctx->uid)) return EPERM; /* If the calling process is not privileged (Linux: does not have the CAP_FSETID capability), and the group of the file does not match the effective group ID of the process or one of its supplementary group IDs, the S_ISGID bit will be turned off, but this will not cause an error to be returned. */ if (!frame_in_group (frame, ctx->gid)) buf->ia_prot.sgid = 0; } if (valid & (GF_SET_ATTR_ATIME | GF_SET_ATTR_MTIME)) { /* Changing timestamps is permitted when: either the process has appropri? ate privileges, or the effective user ID equals the user ID of the file, or times is NULL and the process has write permission for the file. */ if (!frame_is_user (frame, ctx->uid) && !acl_permits (frame, inode, POSIX_ACL_WRITE)) return EACCES; } if (valid & GF_SET_ATTR_UID) { if ((frame->root->uid != 0) && (buf->ia_uid != ctx->uid)) return EPERM; } if (valid & GF_SET_ATTR_GID) { if (!frame_is_user (frame, ctx->uid)) return EPERM; if (!frame_in_group (frame, buf->ia_gid)) return EPERM; } return 0; } int posix_acl_setattr_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int op_ret, int op_errno, struct iatt *prebuf, struct iatt *postbuf) { inode_t *inode = NULL; inode = frame->local; frame->local = NULL; if (op_ret != 0) goto unwind; posix_acl_ctx_update (inode, this, postbuf); unwind: STACK_UNWIND_STRICT (setattr, frame, op_ret, op_errno, prebuf, postbuf); return 0; } int posix_acl_setattr (call_frame_t *frame, xlator_t *this, loc_t *loc, struct iatt *buf, int valid) { int op_errno = 0; op_errno = setattr_scrutiny (frame, loc->inode, buf, valid); if (op_errno) goto red; frame->local = loc->inode; STACK_WIND (frame, posix_acl_setattr_cbk, FIRST_CHILD(this), FIRST_CHILD(this)->fops->setattr, loc, buf, valid); return 0; red: STACK_UNWIND_STRICT (setattr, frame, -1, op_errno, NULL, NULL); return 0; } int posix_acl_fsetattr_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int op_ret, int op_errno, struct iatt *prebuf, struct iatt *postbuf) { inode_t *inode = NULL; inode = frame->local; frame->local = NULL; if (op_ret != 0) goto unwind; posix_acl_ctx_update (inode, this, postbuf); unwind: STACK_UNWIND_STRICT (fsetattr, frame, op_ret, op_errno, prebuf, postbuf); return 0; } int posix_acl_fsetattr (call_frame_t *frame, xlator_t *this, fd_t *fd, struct iatt *buf, int valid) { int op_errno = 0; op_errno = setattr_scrutiny (frame, fd->inode, buf, valid); if (op_errno) goto red; frame->local = fd->inode; STACK_WIND (frame, posix_acl_fsetattr_cbk, FIRST_CHILD(this), FIRST_CHILD(this)->fops->fsetattr, fd, buf, valid); return 0; red: STACK_UNWIND_STRICT (fsetattr, frame, -1, EACCES, NULL, NULL); return 0; } int setxattr_scrutiny (call_frame_t *frame, inode_t *inode, dict_t *xattr) { struct posix_acl_ctx *ctx = NULL; int found = 0; if (frame->root->uid == 0) return 0; ctx = posix_acl_ctx_get (inode, frame->this); if (!ctx) return EIO; if (dict_get (xattr, POSIX_ACL_ACCESS_XATTR)) { found = 1; if (!frame_is_user (frame, ctx->uid)) return EPERM; } if (dict_get (xattr, POSIX_ACL_DEFAULT_XATTR)) { found = 1; if (!frame_is_user (frame, ctx->uid)) return EPERM; } if (!found && !acl_permits (frame, inode, POSIX_ACL_WRITE)) return EACCES; return 0; } struct posix_acl * posix_acl_xattr_update (xlator_t *this, inode_t *inode, dict_t *xattr, char *name, struct posix_acl *old) { struct posix_acl *acl = NULL; data_t *data = NULL; data = dict_get (xattr, name); if (data) { acl = posix_acl_from_xattr (this, data->data, data->len); } if (!acl && old) acl = posix_acl_ref (this, old); return acl; } int posix_acl_setxattr_update (xlator_t *this, inode_t *inode, dict_t *xattr) { struct posix_acl *acl_access = NULL; struct posix_acl *acl_default = NULL; struct posix_acl *old_access = NULL; struct posix_acl *old_default = NULL; struct posix_acl_ctx *ctx = NULL; int ret = 0; mode_t mode = 0; ctx = posix_acl_ctx_get (inode, this); if (!ctx) return -1; ret = posix_acl_get (inode, this, &old_access, &old_default); acl_access = posix_acl_xattr_update (this, inode, xattr, POSIX_ACL_ACCESS_XATTR, old_access); acl_default = posix_acl_xattr_update (this, inode, xattr, POSIX_ACL_DEFAULT_XATTR, old_default); ret = posix_acl_set (inode, this, acl_access, acl_default); if (acl_access && acl_access != old_access) { mode = posix_acl_access_set_mode (acl_access, ctx); } if (acl_access) posix_acl_unref (this, acl_access); if (acl_default) posix_acl_unref (this, acl_default); if (old_access) posix_acl_unref (this, old_access); if (old_default) posix_acl_unref (this, old_default); return 0; } int posix_acl_setxattr_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int op_ret, int op_errno) { STACK_UNWIND_STRICT (setxattr, frame, op_ret, op_errno); return 0; } int posix_acl_setxattr (call_frame_t *frame, xlator_t *this, loc_t *loc, dict_t *xattr, int flags) { int op_errno = 0; op_errno = setxattr_scrutiny (frame, loc->inode, xattr); if (op_errno != 0) goto red; posix_acl_setxattr_update (this, loc->inode, xattr); STACK_WIND (frame, posix_acl_setxattr_cbk, FIRST_CHILD(this), FIRST_CHILD(this)->fops->setxattr, loc, xattr, flags); return 0; red: STACK_UNWIND_STRICT (setxattr, frame, -1, op_errno); return 0; } int posix_acl_fsetxattr_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int op_ret, int op_errno) { STACK_UNWIND_STRICT (fsetxattr, frame, op_ret, op_errno); return 0; } int posix_acl_fsetxattr (call_frame_t *frame, xlator_t *this, fd_t *fd, dict_t *xattr, int flags) { int op_errno = 0; op_errno = setxattr_scrutiny (frame, fd->inode, xattr); if (op_errno != 0) goto red; posix_acl_setxattr_update (this, fd->inode, xattr); STACK_WIND (frame, posix_acl_fsetxattr_cbk, FIRST_CHILD(this), FIRST_CHILD(this)->fops->fsetxattr, fd, xattr, flags); return 0; red: STACK_UNWIND_STRICT (fsetxattr, frame, -1, op_errno); return 0; } int posix_acl_getxattr_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int op_ret, int op_errno, dict_t *xattr) { STACK_UNWIND_STRICT (getxattr, frame, op_ret, op_errno, xattr); return 0; } int posix_acl_getxattr (call_frame_t *frame, xlator_t *this, loc_t *loc, const char *name) { if (whitelisted_xattr (name)) goto green; if (acl_permits (frame, loc->inode, POSIX_ACL_READ)) goto green; else goto red; green: STACK_WIND (frame, posix_acl_getxattr_cbk, FIRST_CHILD(this), FIRST_CHILD(this)->fops->getxattr, loc, name); return 0; red: STACK_UNWIND_STRICT (getxattr, frame, -1, EACCES, NULL); return 0; } int posix_acl_fgetxattr_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int op_ret, int op_errno, dict_t *xattr) { STACK_UNWIND_STRICT (fgetxattr, frame, op_ret, op_errno, xattr); return 0; } int posix_acl_fgetxattr (call_frame_t *frame, xlator_t *this, fd_t *fd, const char *name) { if (whitelisted_xattr (name)) goto green; if (acl_permits (frame, fd->inode, POSIX_ACL_READ)) goto green; else goto red; green: STACK_WIND (frame, posix_acl_fgetxattr_cbk, FIRST_CHILD(this), FIRST_CHILD(this)->fops->fgetxattr, fd, name); return 0; red: STACK_UNWIND_STRICT (fgetxattr, frame, -1, EACCES, NULL); return 0; } int posix_acl_removexattr_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int op_ret, int op_errno) { STACK_UNWIND_STRICT (removexattr, frame, op_ret, op_errno); return 0; } int posix_acl_removexattr (call_frame_t *frame, xlator_t *this, loc_t *loc, const char *name) { struct posix_acl_ctx *ctx = NULL; int op_errno = EACCES; if (frame_is_user (frame, 0)) goto green; ctx = posix_acl_ctx_get (loc->inode, this); if (!ctx) { op_errno = EIO; goto red; } if (whitelisted_xattr (name)) { if (!frame_is_user (frame, ctx->uid)) { op_errno = EPERM; goto red; } } if (acl_permits (frame, loc->inode, POSIX_ACL_WRITE)) goto green; else goto red; green: STACK_WIND (frame, posix_acl_removexattr_cbk, FIRST_CHILD(this), FIRST_CHILD(this)->fops->removexattr, loc, name); return 0; red: STACK_UNWIND_STRICT (removexattr, frame, -1, op_errno); return 0; } int posix_acl_forget (xlator_t *this, inode_t *inode) { struct posix_acl_ctx *ctx = NULL; ctx = posix_acl_ctx_get (inode, this); if (!ctx) goto out; if (ctx->acl_access) posix_acl_unref (this, ctx->acl_access); if (ctx->acl_default) posix_acl_unref (this, ctx->acl_default); FREE (ctx); out: return 0; } int init (xlator_t *this) { struct posix_acl_conf *conf = NULL; struct posix_acl *minacl = NULL; struct posix_ace *minace = NULL; conf = CALLOC (1, sizeof (*conf)); if (!conf) { gf_log (this->name, GF_LOG_ERROR, "out of memory"); return -1; } LOCK_INIT (&conf->acl_lock); this->private = conf; minacl = posix_acl_new (this, 3); if (!minacl) return -1; minace = minacl->entries; minace[0].tag = POSIX_ACL_USER_OBJ; minace[1].tag = POSIX_ACL_GROUP_OBJ; minace[2].tag = POSIX_ACL_OTHER; conf->minimal_acl = minacl; return 0; } int fini (xlator_t *this) { return 0; } struct xlator_fops fops = { .lookup = posix_acl_lookup, .open = posix_acl_open, .access = posix_acl_access, .truncate = posix_acl_truncate, .mkdir = posix_acl_mkdir, .mknod = posix_acl_mknod, .create = posix_acl_create, .symlink = posix_acl_symlink, .unlink = posix_acl_unlink, .rmdir = posix_acl_rmdir, .rename = posix_acl_rename, .link = posix_acl_link, .opendir = posix_acl_opendir, .readdir = posix_acl_readdir, .readdirp = posix_acl_readdirp, .setattr = posix_acl_setattr, .fsetattr = posix_acl_fsetattr, .setxattr = posix_acl_setxattr, .fsetxattr = posix_acl_fsetxattr, .getxattr = posix_acl_getxattr, .fgetxattr = posix_acl_fgetxattr, .removexattr = posix_acl_removexattr, }; struct xlator_cbks cbks = { .forget = posix_acl_forget };