/* Copyright (c) 2010 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 . */ #ifndef _CONFIG_H #define _CONFIG_H #include "config.h" #endif #include "access-control.h" #include "xlator.h" #include "call-stub.h" #include "defaults.h" #include "iatt.h" /* Careful, this function erases the stub from frame->local. Dont call this if * a subsequent callback requires retaining access to the stub. This should be * called at the end of all access-control related operations, i.e. once the * frame will be handed off to the actual fop and the next callback that will * be called is the default callback. IOW, the function where call_resume is * called. * NOTE: this is required because FRAME_DESTROY tries to free frame->local if * it finds it to be non-NULL. */ call_stub_t * __get_frame_stub (call_frame_t *fr) { call_stub_t *st = NULL; GF_VALIDATE_OR_GOTO (ACTRL, fr, out); st = fr->local; fr->local = NULL; out: gf_log (ACTRL, GF_LOG_TRACE, "Returning %p", st); return st; } void ac_set_accesstype_str (int access, char *str, size_t len) { char *read = "read "; char *write = "write "; char *exec = "exec "; char *dontcare = "don'tcare "; GF_ASSERT (len > (strlen (read) + strlen (write) + strlen (exec) + strlen (dontcare))); GF_ASSERT (str); if (access & ACCTEST_READ) { snprintf (str, len, "%s", read); len -= strlen (read); } if (access & ACCTEST_WRITE) { snprintf (str, len, "%s", write); len -= strlen (write); } if (access & ACCTEST_EXEC) { snprintf (str, len, "%s", exec); len -= strlen (exec); } if (access & ACCTEST_DONTCARE) { snprintf (str, len, "%s", dontcare); len -= strlen (dontcare); } } int ac_test_owner_access (struct iatt *ia, uid_t uid, int accesstest) { int ret = -1; GF_VALIDATE_OR_GOTO (ACTRL, ia, out); /* First test permissions using the uid. */ if (ia->ia_uid != uid) { ret = -1; gf_log (ACTRL, GF_LOG_TRACE, "UID mismatch (orig: %d, user: %d)", ia->ia_uid, uid); goto out; } /* At this point we know, the uid matches that of the stat structure, so * if the caller does not care, we should return success. */ if (ac_test_dontcare (accesstest)) { ret = 0; gf_log (ACTRL, GF_LOG_TRACE, "Access test marked as don't care"); goto out; } if (ac_test_read (accesstest)) ret = IA_PROT_RUSR (ia->ia_prot); if (ac_test_write (accesstest)) ret = IA_PROT_WUSR (ia->ia_prot); if (ac_test_exec (accesstest)) ret = IA_PROT_XUSR (ia->ia_prot); /* For failed access test for owner, we need to return EACCES */ if (!ret) ret = -1; else ret = 0; out: if (0 == ret) gf_log (ACTRL, GF_LOG_TRACE, "Owner access allowed"); else gf_log (ACTRL, GF_LOG_TRACE, "Owner access not allowed"); return ret; } int ac_test_group_access (struct iatt *ia, gid_t gid, gid_t *auxgids, int auxcount, int accesstest) { int ret = -1; int testgid = -1; int x = 0; GF_VALIDATE_OR_GOTO (ACTRL, ia, out); /* First, determine which gid to test against. This will be determined * by first checking which of the gids given to us match the gid in the * stat. If none match, then we go to checking with others as the user. */ /* If we are only given the primary gid. Dont depend on @auxgids * being NULL since I know users of this function can pass statically * allocated arrays which cant be NULL and yet contain no valid gids. */ if ((ia->ia_gid != gid) && (auxcount == 0)) { gf_log (ACTRL, GF_LOG_DEBUG, "GID mismatch (orig: %d, user: %d)", ia->ia_gid, gid); ret = -1; goto out; } if (ia->ia_gid == gid) testgid = gid; else { for (; x < auxcount; ++x) { if (ia->ia_gid == auxgids[x]) { testgid = ia->ia_gid; break; } } } /* None of the gids match with the gid in the stat. */ if (testgid == -1) { gf_log (ACTRL, GF_LOG_DEBUG, "None of the gids match with gid " "on the stat"); ret = -1; goto out; } /* At this point, at least one gid matches that in the stat, now we must * check whether the caller is interested in the access check at all. */ if (ac_test_dontcare (accesstest)) { gf_log (ACTRL, GF_LOG_TRACE, "Access test marked as don't care"); ret = 0; goto out; } if (ac_test_read (accesstest)) ret = IA_PROT_RGRP (ia->ia_prot); if (ac_test_write (accesstest)) ret = IA_PROT_WGRP (ia->ia_prot); if (ac_test_exec (accesstest)) ret = IA_PROT_XGRP (ia->ia_prot); if (!ret) ret = -1; else ret = 0; out: if (0 == ret) gf_log (ACTRL, GF_LOG_TRACE, "Group access allowed"); else gf_log (ACTRL, GF_LOG_TRACE, "Group access not allowed"); return ret; } int ac_test_other_access (struct iatt *ia, int accesstest) { int ret = -1; GF_VALIDATE_OR_GOTO (ACTRL, ia, out); ret = 0; if (ac_test_read (accesstest)) ret = IA_PROT_ROTH (ia->ia_prot); if (ac_test_write (accesstest)) ret = IA_PROT_WOTH (ia->ia_prot); if (ac_test_exec (accesstest)) ret = IA_PROT_XOTH (ia->ia_prot); if (!ret) ret = -1; else ret = 0; out: if (0 == ret) gf_log (ACTRL, GF_LOG_TRACE, "Other access allowed"); else gf_log (ACTRL, GF_LOG_TRACE, "Other access not allowed"); return ret; } /* Returns -1 on a failed access test with @operrno set to the relevant error * number. */ int ac_test_access (struct iatt *ia, uid_t uid, gid_t gid, gid_t *auxgids, int auxcount, int accesstest, int testwho, int *operrno) { int ret = -1; char accesstest_str[32] = {0}; GF_VALIDATE_OR_GOTO (ACTRL, ia, out); GF_VALIDATE_OR_GOTO (ACTRL, operrno, out); ac_set_accesstype_str (accesstest, accesstest_str, sizeof (accesstest_str)); gf_log (ACTRL, GF_LOG_TRACE, "Testing for accesstypes %s", accesstest_str); if ((uid == 0) && (gid == 0)) { gf_log (ACTRL, GF_LOG_TRACE, "Root has access"); return 0; } if (ac_test_owner (testwho)) { gf_log (ACTRL, GF_LOG_TRACE, "Testing owner access"); ret = ac_test_owner_access (ia, uid, accesstest); } if (ret == 0) { goto out; } if (ac_test_group (testwho)) { gf_log (ACTRL, GF_LOG_TRACE, "Testing group access"); ret = ac_test_group_access (ia, gid, auxgids, auxcount, accesstest); } if (ret == 0) { goto out; } if (ac_test_other (testwho)) { gf_log (ACTRL, GF_LOG_TRACE, "Testing other access"); ret = ac_test_other_access (ia, accesstest); } out: if (ret == -1) { gf_log (ACTRL, GF_LOG_TRACE, "No access allowed"); *operrno = EPERM; } return ret; } int ac_loc_fill (loc_t *loc, inode_t *inode, inode_t *parent, char *path) { int ret = -EFAULT; GF_VALIDATE_OR_GOTO (ACTRL, loc, out); if (inode) { loc->inode = inode_ref (inode); loc->ino = inode->ino; } if (parent) loc->parent = inode_ref (parent); loc->path = gf_strdup (path); if (!loc->path) { goto loc_wipe; } loc->name = strrchr (loc->path, '/'); if (loc->name) { loc->name++; } else { gf_log (ACTRL, GF_LOG_ERROR, "path: %s, doesn't have '/'", loc->path); goto loc_wipe; } ret = 0; loc_wipe: if (ret < 0) { if (inode) gf_log (ACTRL, GF_LOG_ERROR, "location fill failed for " "inode: %s", uuid_utoa (inode->gfid)); loc_wipe (loc); } out: gf_log (ACTRL, GF_LOG_TRACE, "Returning %d", ret); return ret; } int ac_inode_loc_fill (inode_t *inode, loc_t *loc) { char *resolvedpath = NULL; inode_t *parent = NULL; int ret = -EFAULT; GF_VALIDATE_OR_GOTO (ACTRL, inode, out); GF_VALIDATE_OR_GOTO (ACTRL, loc, out); if ((inode) && (inode->ino == 1)) { goto ignore_parent; } parent = inode_parent (inode, 0, NULL); GF_VALIDATE_OR_GOTO (ACTRL, parent, err); ignore_parent: ret = inode_path (inode, NULL, &resolvedpath); if (ret < 0) { gf_log (ACTRL, GF_LOG_ERROR, "Unable to get path for inode: %s", uuid_utoa (inode->gfid)); goto err; } ret = ac_loc_fill (loc, inode, parent, resolvedpath); if (ret < 0) goto err; err: if (parent) inode_unref (parent); if (resolvedpath) GF_FREE (resolvedpath); out: gf_log (ACTRL, GF_LOG_TRACE, "Returning %d", ret); return ret; } int ac_parent_loc_fill (loc_t *parentloc, loc_t *childloc) { int ret = -1; GF_VALIDATE_OR_GOTO (ACTRL, parentloc, out); GF_VALIDATE_OR_GOTO (ACTRL, childloc, out); ret = ac_inode_loc_fill (childloc->parent, parentloc); out: gf_log (ACTRL, GF_LOG_TRACE, "Returning %d", ret); return ret; } int32_t ac_truncate_resume (call_frame_t *frame, xlator_t *this, loc_t *loc, off_t offset) { STACK_WIND (frame, default_truncate_cbk, FIRST_CHILD(this), FIRST_CHILD(this)->fops->truncate, loc, offset); return 0; } int32_t ac_truncate_stat_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, struct iatt *buf) { call_stub_t *stub = NULL; stub = __get_frame_stub (frame); if (op_ret == -1) goto out; op_ret = ac_test_access (buf, frame->root->uid, frame->root->gid, frame->root->groups, frame->root->ngrps, ACCTEST_WRITE, ACCTEST_ANY, &op_errno); if (op_ret == -1) goto out; call_resume (stub); out: if (op_ret < 0) { gf_log (this->name, GF_LOG_ERROR, "truncate failed with " "error: %s", strerror (op_errno)); STACK_UNWIND_STRICT (truncate, frame, -1, op_errno, NULL, NULL); if (stub) call_stub_destroy (stub); } return 0; } int32_t ac_truncate (call_frame_t *frame, xlator_t *this, loc_t *loc, off_t offset) { call_stub_t *stub = NULL; int ret = -EFAULT; if (__is_fuse_call (frame)) { ac_truncate_resume (frame, this, loc, offset); return 0; } stub = fop_truncate_stub (frame, ac_truncate_resume, loc, offset); if (!stub) { ret = -ENOMEM; goto out; } frame->local = stub; STACK_WIND (frame, ac_truncate_stat_cbk, FIRST_CHILD (this), FIRST_CHILD (this)->fops->stat, loc); ret = 0; out: if (ret < 0) { gf_log (this->name, GF_LOG_ERROR, "truncate failed with error: %s", strerror (-ret)); STACK_UNWIND_STRICT (truncate, frame, -1, -ret, NULL, NULL); } return 0; } int32_t ac_access_resume (call_frame_t *frame, xlator_t *this, loc_t *loc, int32_t mask) { STACK_WIND (frame, default_access_cbk, FIRST_CHILD(this), FIRST_CHILD(this)->fops->access, loc, mask); return 0; } int32_t ac_access_stat_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, struct iatt *buf) { call_stub_t *stub = NULL; int32_t mask = 0; int acctest = 0; stub = __get_frame_stub (frame); mask = stub->args.access.mask; /* If mask requests test for file existence then do not * return a failure with ENOENT, instead return a failed * access test. */ if (op_ret == -1) { if (mask & F_OK) op_errno = EACCES; else op_errno = errno; goto out; } if (R_OK & mask) acctest |= ACCTEST_READ; else if (W_OK & mask) acctest |= ACCTEST_WRITE; else if (X_OK & mask) acctest |= ACCTEST_EXEC; else acctest = 0; op_ret = ac_test_access (buf, frame->root->uid, frame->root->gid, frame->root->groups, frame->root->ngrps, acctest, ACCTEST_ANY, &op_errno); if (op_ret == -1) goto out; call_resume (stub); out: if (op_ret < 0) { gf_log (this->name, GF_LOG_ERROR, "access failed with error: %s", strerror (op_errno)); STACK_UNWIND_STRICT (access, frame, -1, op_errno); if (stub) call_stub_destroy (stub); } return 0; } int32_t ac_access (call_frame_t *frame, xlator_t *this, loc_t *loc, int32_t mask) { call_stub_t *stub = NULL; int ret = -EFAULT; if (__is_fuse_call (frame)) { ac_access_resume (frame, this, loc, mask); return 0; } stub = fop_access_stub (frame, ac_access_resume, loc, mask); if (!stub) { ret = -ENOMEM; goto out; } frame->local = stub; STACK_WIND (frame, ac_access_stat_cbk, FIRST_CHILD (this), FIRST_CHILD (this)->fops->stat, loc); ret = 0; out: if (ret < 0) { gf_log (this->name, GF_LOG_ERROR, "access failed with error: %s", strerror (-ret)); STACK_UNWIND_STRICT (access, frame, -1, -ret); } return 0; } int32_t ac_readlink_resume (call_frame_t *frame, xlator_t *this, loc_t *loc, size_t size) { STACK_WIND (frame, default_readlink_cbk, FIRST_CHILD(this), FIRST_CHILD(this)->fops->readlink, loc, size); return 0; } int32_t ac_readlink_stat_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, struct iatt *buf) { call_stub_t *stub = NULL; stub = __get_frame_stub (frame); if (op_ret == -1) goto out; op_ret = ac_test_access (buf, frame->root->uid, frame->root->gid, frame->root->groups, frame->root->ngrps, ACCTEST_READ, ACCTEST_ANY, &op_errno); if (op_ret == -1) goto out; call_resume (stub); out: if (op_ret < 0) { gf_log (this->name, GF_LOG_ERROR, "readlink failed with error: %s", strerror (op_errno)); STACK_UNWIND_STRICT (readlink, frame, -1, op_errno, NULL, NULL); if (stub) call_stub_destroy (stub); } return 0; } int32_t ac_readlink (call_frame_t *frame, xlator_t *this, loc_t *loc, size_t size) { call_stub_t *stub = NULL; int ret = -EFAULT; if (__is_fuse_call (frame)) { ac_readlink_resume (frame, this, loc, size); return 0; } stub = fop_readlink_stub (frame, ac_readlink_resume, loc, size); if (!stub) { ret = -ENOMEM; goto out; } frame->local = stub; STACK_WIND (frame, ac_readlink_stat_cbk, FIRST_CHILD (this), FIRST_CHILD (this)->fops->stat, loc); ret = 0; out: if (ret < 0) { gf_log (this->name, GF_LOG_ERROR, "readlink failed with error: %s", strerror (-ret)); STACK_UNWIND_STRICT (readlink, frame, -1, -ret, NULL, NULL); } return 0; } int ac_mknod_resume (call_frame_t *frame, xlator_t *this, loc_t *loc, mode_t mode, dev_t rdev, dict_t *params) { STACK_WIND (frame, default_mknod_cbk, FIRST_CHILD(this), FIRST_CHILD(this)->fops->mknod, loc, mode, rdev, params); return 0; } int32_t ac_mknod_stat_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, struct iatt *buf) { call_stub_t *stub = NULL; stub = __get_frame_stub (frame); if (op_ret == -1) goto out; op_ret = ac_test_access (buf, frame->root->uid, frame->root->gid, frame->root->groups, frame->root->ngrps, ACCTEST_WRITE, ACCTEST_ANY, &op_errno); if (op_ret == -1) { op_errno = EACCES; goto out; } call_resume (stub); out: if (op_ret < 0) { gf_log (this->name, GF_LOG_ERROR, "mknod failed with error: %s", strerror (op_errno)); STACK_UNWIND_STRICT (mknod, frame, -1, op_errno, NULL, NULL, NULL, NULL); if (stub) call_stub_destroy (stub); } return 0; } int ac_mknod (call_frame_t *frame, xlator_t *this, loc_t *loc, mode_t mode, dev_t rdev, dict_t *params) { call_stub_t *stub = NULL; int ret = -EFAULT; loc_t parentloc = {0, }; if (__is_fuse_call (frame)) { ac_mknod_resume (frame, this, loc, mode, rdev, params); return 0; } stub = fop_mknod_stub (frame, ac_mknod_resume, loc, mode, rdev, params); if (!stub) { ret = -ENOMEM; goto out; } frame->local = stub; ret = ac_parent_loc_fill (&parentloc, loc); if (ret < 0) goto out; STACK_WIND (frame, ac_mknod_stat_cbk, FIRST_CHILD (this), FIRST_CHILD (this)->fops->stat, &parentloc); loc_wipe (&parentloc); ret = 0; out: if (ret < 0) { /* Erase any stored frame before unwinding. */ stub = __get_frame_stub (frame); if (stub) call_stub_destroy (stub); gf_log (this->name, GF_LOG_ERROR, "mknod failed with error: %s", strerror (-ret)); STACK_UNWIND_STRICT (mknod, frame, -1, -ret, NULL, NULL, NULL, NULL); } return 0; } int ac_mkdir_resume (call_frame_t *frame, xlator_t *this, loc_t *loc, mode_t mode, dict_t *params) { STACK_WIND (frame, default_mkdir_cbk, FIRST_CHILD(this), FIRST_CHILD(this)->fops->mkdir, loc, mode, params); return 0; } int32_t ac_mkdir_stat_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, struct iatt *buf) { call_stub_t *stub = NULL; stub = __get_frame_stub (frame); if (op_ret == -1) goto out; op_ret = ac_test_access (buf, frame->root->uid, frame->root->gid, frame->root->groups, frame->root->ngrps, ACCTEST_WRITE, ACCTEST_ANY, &op_errno); if (op_ret == -1) { /* On a failed write test on parent dir, we need to return * EACCES, not EPERM that is returned by default by * ac_test_access. */ op_errno = EACCES; goto out; } call_resume (stub); out: if (op_ret < 0) { gf_log (this->name, GF_LOG_ERROR, "mkdir failed with error: %s", strerror (op_errno)); STACK_UNWIND_STRICT (mkdir, frame, -1, op_errno, NULL, NULL, NULL, NULL); if (stub) call_stub_destroy (stub); } return 0; } int ac_mkdir (call_frame_t *frame, xlator_t *this, loc_t *loc, mode_t mode, dict_t *params) { call_stub_t *stub = NULL; int ret = -EFAULT; loc_t parentloc = {0, }; if (__is_fuse_call (frame)) { ac_mkdir_resume (frame, this, loc, mode, params); return 0; } stub = fop_mkdir_stub (frame, ac_mkdir_resume, loc, mode, params); if (!stub) { ret = -ENOMEM; goto out; } frame->local = stub; ret = ac_parent_loc_fill (&parentloc, loc); if (ret < 0) goto out; STACK_WIND (frame, ac_mkdir_stat_cbk, FIRST_CHILD (this), FIRST_CHILD (this)->fops->stat, &parentloc); loc_wipe (&parentloc); ret = 0; out: if (ret < 0) { /* Erase the stored stub before unwinding. */ stub = __get_frame_stub (frame); if (stub) call_stub_destroy (stub); gf_log (this->name, GF_LOG_ERROR, "mkdir failed with error: %s", strerror (-ret)); STACK_UNWIND_STRICT (mkdir, frame, -1, -ret, NULL, NULL, NULL, NULL); } return 0; } int32_t ac_unlink_resume (call_frame_t *frame, xlator_t *this, loc_t *loc) { STACK_WIND (frame, default_unlink_cbk, FIRST_CHILD(this), FIRST_CHILD(this)->fops->unlink, loc); return 0; } int32_t ac_unlink_stat_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, struct iatt *buf) { call_stub_t *stub = NULL; stub = __get_frame_stub (frame); if (op_ret == -1) goto out; if (frame->root->uid != buf->ia_uid) { op_ret = -1; op_errno = EACCES; goto out; } call_resume (stub); out: if (op_ret < 0) { gf_log (this->name, GF_LOG_ERROR, "unlink failed with error: %s", strerror (op_errno)); STACK_UNWIND_STRICT (unlink, frame, -1, op_errno, NULL, NULL); if (stub) call_stub_destroy (stub); } return 0; } int32_t ac_unlink_parent_stat_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, struct iatt *buf) { call_stub_t *stub = NULL; stub = frame->local; if (op_ret == -1) goto out; if (buf->ia_prot.sticky == 0) { op_ret = ac_test_access (buf, frame->root->uid, frame->root->gid, frame->root->groups, frame->root->ngrps, ACCTEST_WRITE, ACCTEST_ANY, &op_errno); if (op_ret == -1) { op_errno = EACCES; goto out; } } else { if ((frame->root->uid == 0) || (frame->root->uid == buf->ia_uid)) goto access; STACK_WIND (frame, ac_unlink_stat_cbk, FIRST_CHILD (this), FIRST_CHILD (this)->fops->stat, &stub->args.unlink.loc); goto out; } access: stub = __get_frame_stub (frame); call_resume (stub); out: if (op_ret < 0) { stub = __get_frame_stub (frame); STACK_UNWIND_STRICT (unlink, frame, -1, op_errno, NULL, NULL); if (stub) call_stub_destroy (stub); } return 0; } int32_t ac_unlink (call_frame_t *frame, xlator_t *this, loc_t *loc) { call_stub_t *stub = NULL; int ret = -EFAULT; loc_t parentloc = {0, }; if (__is_fuse_call (frame)) { ac_unlink_resume (frame, this, loc); return 0; } stub = fop_unlink_stub (frame, ac_unlink_resume, loc); if (!stub) { ret = -ENOMEM; goto out; } frame->local = stub; ret = ac_parent_loc_fill (&parentloc, loc); if (ret < 0) goto out; STACK_WIND (frame, ac_unlink_parent_stat_cbk, FIRST_CHILD (this), FIRST_CHILD (this)->fops->stat, &parentloc); loc_wipe (&parentloc); ret = 0; out: if (ret < 0) { /* Erase the stored stub before unwinding. */ stub = __get_frame_stub (frame); if (stub) call_stub_destroy (stub); gf_log (this->name, GF_LOG_ERROR, "unlink failed with error: %s", strerror (-ret)); STACK_UNWIND_STRICT (unlink, frame, -1, -ret, NULL, NULL); } return 0; } int ac_rmdir_resume (call_frame_t *frame, xlator_t *this, loc_t *loc, int flags) { STACK_WIND (frame, default_rmdir_cbk, FIRST_CHILD(this), FIRST_CHILD(this)->fops->rmdir, loc, flags); return 0; } int32_t ac_rmdir_stat_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, struct iatt *buf) { call_stub_t *stub = NULL; stub = __get_frame_stub (frame); if (op_ret == -1) goto out; if (frame->root->uid != buf->ia_uid) { op_ret = -1; op_errno = EACCES; goto out; } call_resume (stub); out: if (op_ret < 0) { gf_log (this->name, GF_LOG_ERROR, "rmdir failed with error: %s", strerror (op_errno)); STACK_UNWIND_STRICT (rmdir, frame, -1, op_errno, NULL, NULL); if (stub) call_stub_destroy (stub); } return 0; } int32_t ac_rmdir_parent_stat_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, struct iatt *buf) { call_stub_t *stub = NULL; stub = frame->local; if (op_ret == -1) goto out; if (buf->ia_prot.sticky == 0) { op_ret = ac_test_access (buf, frame->root->uid, frame->root->gid, frame->root->groups, frame->root->ngrps, ACCTEST_WRITE, ACCTEST_ANY, &op_errno); if (op_ret == -1) { op_errno = EACCES; goto out; } } else { if ((frame->root->uid == 0) || (frame->root->uid == buf->ia_uid)) goto access; STACK_WIND (frame, ac_rmdir_stat_cbk, FIRST_CHILD (this), FIRST_CHILD (this)->fops->stat, &stub->args.rmdir.loc); goto out; } access: stub = __get_frame_stub (frame); call_resume (stub); out: if (op_ret < 0) { stub = __get_frame_stub (frame); STACK_UNWIND_STRICT (rmdir, frame, -1, op_errno, NULL, NULL); if (stub) call_stub_destroy (stub); } return 0; } int ac_rmdir (call_frame_t *frame, xlator_t *this, loc_t *loc, int flags) { call_stub_t *stub = NULL; int ret = -EFAULT; loc_t parentloc = {0, }; if (__is_fuse_call (frame)) { ac_rmdir_resume (frame, this, loc, flags); return 0; } stub = fop_rmdir_stub (frame, ac_rmdir_resume, loc, flags); if (!stub) { ret = -ENOMEM; goto out; } frame->local = stub; ret = ac_parent_loc_fill (&parentloc, loc); if (ret < 0) goto out; STACK_WIND (frame, ac_rmdir_parent_stat_cbk, FIRST_CHILD (this), FIRST_CHILD (this)->fops->stat, &parentloc); loc_wipe (&parentloc); ret = 0; out: if (ret < 0) { /* Erase the stored stub before unwinding. */ stub = __get_frame_stub (frame); if (stub) call_stub_destroy (stub); gf_log (this->name, GF_LOG_ERROR, "rmdir failed with error: %s", strerror (-ret)); STACK_UNWIND_STRICT (rmdir, frame, -1, -ret, NULL, NULL); } return 0; } int32_t ac_symlink_resume (call_frame_t *frame, xlator_t *this, const char *linkname, loc_t *loc, dict_t *params) { STACK_WIND (frame, default_symlink_cbk, FIRST_CHILD(this), FIRST_CHILD(this)->fops->symlink, linkname, loc, params); return 0; } int32_t ac_symlink_stat_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, struct iatt *buf) { call_stub_t *stub = NULL; stub = __get_frame_stub (frame); if (op_ret == -1) goto out; op_ret = ac_test_access (buf, frame->root->uid, frame->root->gid, frame->root->groups, frame->root->ngrps, ACCTEST_WRITE, ACCTEST_ANY, &op_errno); if (op_ret == -1) { op_errno = EACCES; goto out; } call_resume (stub); out: if (op_ret < 0) { gf_log (this->name, GF_LOG_ERROR, "symlink failed with error: %s", strerror (op_errno)); STACK_UNWIND_STRICT (symlink, frame, -1, op_errno, NULL, NULL, NULL, NULL); if (stub) call_stub_destroy (stub); } return 0; } int ac_symlink (call_frame_t *frame, xlator_t *this, const char *linkname, loc_t *loc, dict_t *params) { call_stub_t *stub = NULL; int ret = -EFAULT; loc_t parentloc = {0, }; if (__is_fuse_call (frame)) { ac_symlink_resume (frame, this, linkname, loc, params); return 0; } stub = fop_symlink_stub (frame, ac_symlink_resume, linkname, loc, params); if (!stub) { ret = -ENOMEM; goto out; } frame->local = stub; ret = ac_parent_loc_fill (&parentloc, loc); if (ret < 0) goto out; STACK_WIND (frame, ac_symlink_stat_cbk, FIRST_CHILD (this), FIRST_CHILD (this)->fops->stat, &parentloc); loc_wipe (&parentloc); ret = 0; out: if (ret < 0) { /* Erase the stored stub before unwinding. */ stub = __get_frame_stub (frame); if (stub) call_stub_destroy (stub); gf_log (this->name, GF_LOG_ERROR, "symlink failed with error: %s", strerror (-ret)); STACK_UNWIND_STRICT (symlink, frame, -1, -ret, NULL, NULL, NULL, NULL); } return 0; } int32_t ac_rename_resume (call_frame_t *frame, xlator_t *this, loc_t *oldloc, loc_t *newloc) { STACK_WIND (frame, default_rename_cbk, FIRST_CHILD(this), FIRST_CHILD(this)->fops->rename, oldloc, newloc); return 0; } int32_t ac_rename_dst_stat_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, struct iatt *buf) { call_stub_t *stub = NULL; stub = __get_frame_stub (frame); if ((op_ret == -1) && (op_errno == ENOENT)) goto access; if (op_ret == -1) goto out; if (frame->root->uid == buf->ia_uid) goto access; else { op_ret = -1; op_errno = EACCES; goto out; } access: call_resume (stub); out: if (op_ret < 0) { STACK_UNWIND_STRICT (rename, frame, -1, op_errno, NULL, NULL, NULL, NULL, NULL); if (stub) call_stub_destroy (stub); } return 0; } int32_t ac_rename_dst_parent_stat_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, struct iatt *buf) { call_stub_t *stub = NULL; stub = frame->local; if (op_ret == -1) goto out; if (buf->ia_prot.sticky == 0) { op_ret = ac_test_access (buf, frame->root->uid, frame->root->gid, frame->root->groups, frame->root->ngrps, ACCTEST_WRITE, ACCTEST_ANY, &op_errno); if (op_ret == -1) { op_errno = EACCES; goto out; } } else { if ((frame->root->uid == 0) || (frame->root->uid == buf->ia_uid)) goto access; STACK_WIND (frame, ac_rename_dst_stat_cbk, FIRST_CHILD (this), FIRST_CHILD (this)->fops->stat, &stub->args.rename.new); goto out; } access: stub = __get_frame_stub (frame); call_resume (stub); out: if (op_ret < 0) { gf_log (this->name, GF_LOG_ERROR, "rename failed with error: %s", strerror (op_errno)); stub = __get_frame_stub (frame); STACK_UNWIND_STRICT (rename, frame, -1, op_errno, NULL, NULL, NULL, NULL, NULL); if (stub) call_stub_destroy (stub); } return 0; } int32_t ac_rename_src_stat_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, struct iatt *buf) { call_stub_t *stub = NULL; loc_t parentloc = {0, }; stub = frame->local; if (op_ret == -1) goto out; if (buf->ia_uid != frame->root->uid) { op_ret = -1; op_errno = EACCES; goto out; } op_ret = ac_parent_loc_fill (&parentloc, &stub->args.rename.new); if (op_ret < 0) { op_errno = -EFAULT; goto out; } STACK_WIND (frame, ac_rename_dst_parent_stat_cbk, FIRST_CHILD (this), FIRST_CHILD (this)->fops->stat, &parentloc); loc_wipe (&parentloc); out: if (op_ret < 0) { /* Erase the stored stub before unwinding. */ stub = __get_frame_stub (frame); if (stub) call_stub_destroy (stub); STACK_UNWIND_STRICT (rename, frame, -1, op_errno, NULL, NULL, NULL, NULL, NULL); } return 0; } int32_t ac_rename_src_parent_stat_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, struct iatt *buf) { call_stub_t *stub = NULL; loc_t parentloc = {0, }; stub = frame->local; if (op_ret == -1) goto out; if (buf->ia_prot.sticky == 0) { op_ret = ac_test_access (buf, frame->root->uid, frame->root->gid, frame->root->groups, frame->root->ngrps, ACCTEST_WRITE, ACCTEST_ANY, &op_errno); if (op_ret == -1) { op_errno = EACCES; goto out; } } else { if ((buf->ia_uid == frame->root->uid) || (frame->root->uid ==0)) goto access; STACK_WIND (frame, ac_rename_src_stat_cbk, FIRST_CHILD (this), FIRST_CHILD (this)->fops->stat, &stub->args.rename.old); goto out; } access: op_ret = ac_parent_loc_fill (&parentloc, &stub->args.rename.new); if (op_ret < 0) { op_errno = -EFAULT; goto out; } STACK_WIND (frame, ac_rename_dst_parent_stat_cbk, FIRST_CHILD (this), FIRST_CHILD (this)->fops->stat, &parentloc); loc_wipe (&parentloc); out: if (op_ret < 0) { /* Erase the stored stub before unwinding. */ stub = __get_frame_stub (frame); if (stub) call_stub_destroy (stub); gf_log (this->name, GF_LOG_ERROR, "rename failed with error: %s", strerror (op_errno)); STACK_UNWIND_STRICT (rename, frame, -1, op_errno, NULL, NULL, NULL, NULL, NULL); } return 0; } int32_t ac_rename (call_frame_t *frame, xlator_t *this, loc_t *oldloc, loc_t *newloc) { call_stub_t *stub = NULL; int ret = -EFAULT; loc_t parentloc = {0, }; if (__is_fuse_call (frame)) { ac_rename_resume (frame, this, oldloc, newloc); return 0; } stub = fop_rename_stub (frame, ac_rename_resume, oldloc, newloc); if (!stub) { ret = -ENOMEM; goto out; } frame->local = stub; ret = ac_parent_loc_fill (&parentloc, oldloc); if (ret < 0) goto out; STACK_WIND (frame, ac_rename_src_parent_stat_cbk, FIRST_CHILD (this), FIRST_CHILD (this)->fops->stat, &parentloc); loc_wipe (&parentloc); ret = 0; out: if (ret < 0) { /* Erase the stored stub before unwinding. */ stub = __get_frame_stub (frame); if (stub) call_stub_destroy (stub); gf_log (this->name, GF_LOG_ERROR, "rename failed with error: %s", strerror (-ret)); STACK_UNWIND_STRICT (rename, frame, -1, -ret, NULL, NULL, NULL, NULL, NULL); } return 0; } int32_t ac_link_resume (call_frame_t *frame, xlator_t *this, loc_t *oldloc, loc_t *newloc) { STACK_WIND (frame, default_link_cbk, FIRST_CHILD(this), FIRST_CHILD(this)->fops->link, oldloc, newloc); return 0; } int32_t ac_link_stat_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, struct iatt *buf) { call_stub_t *stub = NULL; stub = __get_frame_stub (frame); if (op_ret == -1) goto out; op_ret = ac_test_access (buf, frame->root->uid, frame->root->gid, frame->root->groups, frame->root->ngrps, ACCTEST_WRITE, ACCTEST_ANY, &op_errno); if (op_ret == -1) { /* By default ac_test_access sets the op_errno to EPERM * but in the case of link, we need to return EACCES to meet * posix requirements when a write permission is not available * for the new directory. */ op_errno = EACCES; goto out; } call_resume (stub); out: if (op_ret < 0) { gf_log (this->name, GF_LOG_ERROR, "link failed with error: %s", strerror (op_errno)); STACK_UNWIND_STRICT (link, frame, -1, op_errno, NULL, NULL, NULL, NULL); if (stub) call_stub_destroy (stub); } return 0; } int32_t ac_link (call_frame_t *frame, xlator_t *this, loc_t *oldloc, loc_t *newloc) { call_stub_t *stub = NULL; int ret = -EFAULT; loc_t parentloc = {0, }; if (__is_fuse_call (frame)) { ac_link_resume (frame, this, oldloc, newloc); return 0; } stub = fop_link_stub (frame, ac_link_resume, oldloc, newloc); if (!stub) { ret = -ENOMEM; goto out; } frame->local = stub; ret = ac_parent_loc_fill (&parentloc, newloc); if (ret < 0) goto out; STACK_WIND (frame, ac_link_stat_cbk, FIRST_CHILD (this), FIRST_CHILD (this)->fops->stat, &parentloc); loc_wipe (&parentloc); ret = 0; out: if (ret < 0) { /* Erase the stored stub before unwinding. */ stub = __get_frame_stub (frame); if (stub) call_stub_destroy (stub); gf_log (this->name, GF_LOG_ERROR, "link failed with error: %s", strerror (-ret)); STACK_UNWIND_STRICT (link, frame, -1, -ret, NULL, NULL, NULL, NULL); } return 0; } int32_t ac_create_resume (call_frame_t *frame, xlator_t *this, loc_t *loc, int32_t flags, mode_t mode, fd_t *fd, dict_t *params) { STACK_WIND (frame, default_create_cbk, FIRST_CHILD(this), FIRST_CHILD(this)->fops->create, loc, flags, mode, fd, params); return 0; } int32_t ac_create_stat_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, struct iatt *buf) { call_stub_t *stub = NULL; stub = __get_frame_stub (frame); if (op_ret == -1) goto out; op_ret = ac_test_access (buf, frame->root->uid, frame->root->gid, frame->root->groups, frame->root->ngrps, ACCTEST_WRITE, ACCTEST_ANY, &op_errno); if (op_ret == -1) { op_errno = EACCES; goto out; } call_resume (stub); out: if (op_ret < 0) { gf_log (this->name, GF_LOG_ERROR, "create failed with error: %s", strerror (op_errno)); STACK_UNWIND_STRICT (create, frame, -1, op_errno, NULL, NULL, NULL, NULL, NULL); if (stub) call_stub_destroy (stub); } return 0; } int32_t ac_create (call_frame_t *frame, xlator_t *this, loc_t *loc, int32_t flags, mode_t mode, fd_t *fd, dict_t *params) { call_stub_t *stub = NULL; int ret = -EFAULT; loc_t parentloc = {0, }; if (__is_fuse_call (frame)) { ac_create_resume (frame, this, loc, flags, mode, fd, params); return 0; } stub = fop_create_stub (frame, ac_create_resume, loc, flags, mode, fd, params); if (!stub) { ret = -ENOMEM; goto out; } frame->local = stub; ret = ac_parent_loc_fill (&parentloc, loc); if (ret < 0) goto out; STACK_WIND (frame, ac_create_stat_cbk, FIRST_CHILD (this), FIRST_CHILD (this)->fops->stat, &parentloc); loc_wipe (&parentloc); ret = 0; out: if (ret < 0) { /* Erase the stored stub before unwinding. */ stub = __get_frame_stub (frame); if (stub) call_stub_destroy (stub); gf_log (this->name, GF_LOG_ERROR, "create failed with error: %s", strerror (-ret)); STACK_UNWIND_STRICT (create, frame, -1, -ret, NULL, NULL, NULL, NULL, NULL); } return 0; } int32_t ac_open_resume (call_frame_t *frame, xlator_t *this, loc_t *loc, int32_t flags, fd_t *fd, int32_t wbflags) { STACK_WIND (frame, default_open_cbk, FIRST_CHILD(this), FIRST_CHILD(this)->fops->open, loc, flags, fd, wbflags); return 0; } int32_t ac_open_create_stat_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, struct iatt *buf) { call_stub_t *stub = NULL; stub = __get_frame_stub (frame); if (op_ret == -1) goto out; op_ret = ac_test_access (buf, frame->root->uid, frame->root->gid, frame->root->groups, frame->root->ngrps, ACCTEST_WRITE, ACCTEST_ANY, &op_errno); if (op_ret == -1) { op_errno = EACCES; goto out; } call_resume (stub); out: if (op_ret < 0) { gf_log (this->name, GF_LOG_ERROR, "open failed with error: %s", strerror (op_errno)); STACK_UNWIND_STRICT (open, frame, -1, op_errno, NULL); if (stub) call_stub_destroy (stub); } return 0; } int ac_open_create (call_stub_t *stub) { int ret = -EFAULT; loc_t parentloc = {0, }; xlator_t *this = NULL; if (!stub) return ret; ret = ac_parent_loc_fill (&parentloc, &stub->args.open.loc); if (ret < 0) goto out; this = stub->frame->this; STACK_WIND (stub->frame, ac_open_create_stat_cbk, FIRST_CHILD (this), FIRST_CHILD (this)->fops->stat, &parentloc); loc_wipe (&parentloc); ret = 0; out: return ret; } int32_t ac_open_only_stat_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, struct iatt *buf) { call_stub_t *stub = NULL; int acctest = 0; int32_t flags = 0; stub = __get_frame_stub (frame); if (op_ret == -1) goto out; flags = stub->args.open.flags; /* The permissions we test for depend on how the open needs to be * performed. */ if ((flags & O_ACCMODE) == O_RDONLY) acctest = ACCTEST_READ; else if (((flags & O_ACCMODE) == O_RDWR) || ((flags & O_ACCMODE) == O_WRONLY)) acctest = ACCTEST_WRITE; op_ret = ac_test_access (buf, frame->root->uid, frame->root->gid, frame->root->groups, frame->root->ngrps, acctest, ACCTEST_ANY, &op_errno); if (op_ret == -1) goto out; call_resume (stub); out: if (op_ret < 0) { gf_log (this->name, GF_LOG_ERROR, "open failed with error: %s", strerror (op_errno)); STACK_UNWIND_STRICT (open, frame, -1, op_errno, NULL); if (stub) call_stub_destroy (stub); } return 0; } int ac_open_only (call_stub_t *stub) { int ret = -EFAULT; xlator_t *this = NULL; if (!stub) return ret; this = stub->frame->this; STACK_WIND (stub->frame, ac_open_only_stat_cbk, FIRST_CHILD (this), FIRST_CHILD (this)->fops->stat, &stub->args.open.loc); return 0; } int32_t ac_open (call_frame_t *frame, xlator_t *this, loc_t *loc, int32_t flags, fd_t *fd, int32_t wbflags) { call_stub_t *stub = NULL; int ret = -EFAULT; if (__is_fuse_call (frame)) { ret = ac_open_resume (frame, this, loc, flags, fd, wbflags); return 0; } stub = fop_open_stub (frame, ac_open_resume, loc, flags, fd, wbflags); if (!stub) { ret = -ENOMEM; goto out; } frame->local = stub; /* If we are not supposed to create the file then there is no need to * check the parent dir permissions. */ if (flags & O_CREAT) ret = ac_open_create (stub); else ret = ac_open_only (stub); out: if (ret < 0) { /* Erase the stored stub before unwinding. */ stub = __get_frame_stub (frame); if (stub) call_stub_destroy (stub); gf_log (this->name, GF_LOG_ERROR, "open failed with error: %s", strerror (-ret)); STACK_UNWIND_STRICT (open, frame, -1, -ret, NULL); } return 0; } int32_t ac_readv_resume (call_frame_t *frame, xlator_t *this, fd_t *fd, size_t size, off_t offset) { STACK_WIND (frame, default_readv_cbk, FIRST_CHILD (this), FIRST_CHILD (this)->fops->readv, fd, size, offset); return 0; } int32_t ac_readv_fstat_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, struct iatt *buf) { call_stub_t *stub = NULL; stub = __get_frame_stub (frame); if (op_ret == -1) goto out; op_ret = ac_test_access (buf, frame->root->uid, frame->root->gid, frame->root->groups, frame->root->ngrps, ACCTEST_READ, ACCTEST_ANY, &op_errno); if (op_ret == -1) { op_errno = EACCES; goto out; } call_resume (stub); out: if (op_ret < 0) { gf_log (this->name, GF_LOG_ERROR, "readv failed with error: %s", strerror (op_errno)); STACK_UNWIND_STRICT (readv, frame, -1, op_errno, NULL, 0, NULL, NULL); if (stub) call_stub_destroy (stub); } return 0; } int32_t ac_readv (call_frame_t *frame, xlator_t *this, fd_t *fd, size_t size, off_t offset) { call_stub_t *stub = NULL; int ret = -EFAULT; if (__is_fuse_call (frame)) { ret = ac_readv_resume (frame, this, fd, size, offset); return 0; } stub = fop_readv_stub (frame, ac_readv_resume, fd, size, offset); if (!stub) { ret = -ENOMEM; goto out; } frame->local = stub; STACK_WIND (frame, ac_readv_fstat_cbk, FIRST_CHILD (this), FIRST_CHILD (this)->fops->fstat, fd); ret = 0; out: if (ret < 0) { gf_log (this->name, GF_LOG_ERROR, "readv failed with error: %s", strerror (-ret)); STACK_UNWIND_STRICT (readv, frame, -1, -ret, NULL, 0, NULL, NULL); } return 0; } int32_t ac_writev_resume (call_frame_t *frame, xlator_t *this, fd_t *fd, struct iovec *vector, int32_t count, off_t offset, struct iobref *iobref) { STACK_WIND (frame, default_writev_cbk, FIRST_CHILD (this), FIRST_CHILD (this)->fops->writev, fd, vector, count, offset, iobref); return 0; } int32_t ac_writev_fstat_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, struct iatt *buf) { call_stub_t *stub = NULL; stub = __get_frame_stub (frame); if (op_ret == -1) goto out; op_ret = ac_test_access (buf, frame->root->uid, frame->root->gid, frame->root->groups, frame->root->ngrps, ACCTEST_WRITE, ACCTEST_ANY, &op_errno); if (op_ret == -1) { op_errno = EACCES; goto out; } call_resume (stub); out: if (op_ret < 0) { gf_log (this->name, GF_LOG_ERROR, "writev failed with error: %s", strerror (op_errno)); STACK_UNWIND_STRICT (writev, frame, -1, op_errno, NULL, NULL); if (stub) call_stub_destroy (stub); } return 0; } int32_t ac_writev (call_frame_t *frame, xlator_t *this, fd_t *fd, struct iovec *vector, int32_t count, off_t offset, struct iobref *iobref) { call_stub_t *stub = NULL; int ret = -EFAULT; if (__is_fuse_call (frame)) { ret = ac_writev_resume (frame, this, fd, vector, count, offset, iobref); return 0; } stub = fop_writev_stub (frame, ac_writev_resume, fd, vector, count, offset, iobref); if (!stub) { ret = -ENOMEM; goto out; } frame->local = stub; STACK_WIND (frame, ac_writev_fstat_cbk, FIRST_CHILD (this), FIRST_CHILD (this)->fops->fstat, fd); ret = 0; out: if (ret < 0) { gf_log (this->name, GF_LOG_ERROR, "writev failed with error: %s", strerror (-ret)); STACK_UNWIND_STRICT (writev, frame, -1, -ret, NULL, NULL); } return 0; } int32_t ac_opendir_resume (call_frame_t *frame, xlator_t *this, loc_t *loc, fd_t *fd) { STACK_WIND (frame, default_opendir_cbk, FIRST_CHILD(this), FIRST_CHILD(this)->fops->opendir, loc, fd); return 0; } int32_t ac_opendir_stat_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, struct iatt *buf) { call_stub_t *stub = NULL; stub = __get_frame_stub (frame); if (op_ret == -1) goto out; op_ret = ac_test_access (buf, frame->root->uid, frame->root->gid, frame->root->groups, frame->root->ngrps, ACCTEST_READ, ACCTEST_ANY, &op_errno); if (op_ret == -1) goto out; call_resume (stub); out: if (op_ret < 0) { gf_log (this->name, GF_LOG_ERROR, "opendir failed with error: %s", strerror (op_errno)); STACK_UNWIND_STRICT (opendir, frame, -1, op_errno, NULL); if (stub) call_stub_destroy (stub); } return 0; } int32_t ac_opendir (call_frame_t *frame, xlator_t *this, loc_t *loc, fd_t *fd) { call_stub_t *stub = NULL; int ret = -EFAULT; if (__is_fuse_call (frame)) { ret = ac_opendir_resume (frame, this, loc, fd); return 0; } stub = fop_opendir_stub (frame, ac_opendir_resume, loc, fd); if (!stub) { ret = -ENOMEM; goto out; } frame->local = stub; STACK_WIND (frame, ac_opendir_stat_cbk, FIRST_CHILD (this), FIRST_CHILD (this)->fops->stat, loc); ret = 0; out: if (ret < 0) { gf_log (this->name, GF_LOG_ERROR, "opendir failed with error: %s", strerror (-ret)); STACK_UNWIND_STRICT (opendir, frame, -1, -ret, NULL); } return 0; } int32_t ac_setattr_resume (call_frame_t *frame, xlator_t *this, loc_t *loc, struct iatt *buf, int32_t valid) { STACK_WIND (frame, default_setattr_cbk, FIRST_CHILD(this), FIRST_CHILD(this)->fops->setattr, loc, buf, valid); return 0; } int32_t ac_setattr_stat_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, struct iatt *buf) { call_stub_t *stub = NULL; int32_t valid = 0; struct iatt *setbuf = NULL; stub = __get_frame_stub (frame); if (op_ret == -1) goto out; if ((frame->root->uid != buf->ia_uid) && (frame->root->uid != 0)) { op_ret = ac_test_access (buf, frame->root->uid, frame->root->gid, frame->root->groups, frame->root->ngrps, ACCTEST_WRITE, ACCTEST_ANY, &op_errno); if (op_ret == -1) { gf_log (ACTRL, GF_LOG_DEBUG, "Don't have write permision"); goto out; } } valid = stub->args.setattr.valid; setbuf = &stub->args.setattr.stbuf; if (gf_attr_uid_set (valid) || gf_attr_gid_set (valid) || gf_attr_mode_set(valid)) { /* chown/chmod returns EPERM if the operation would change the * ownership/permissions, but the effective user ID is not the * super-user and the process is not an owner of the file. * Ref: posix-testsuite/chown/07.t */ if ((frame->root->uid != 0) && gf_attr_mode_set(valid)) { if (frame->root->uid != buf->ia_uid) { gf_log (ACTRL, GF_LOG_DEBUG, "Mismatch effective_uid %d file_uid %d", frame->root->uid, buf->ia_uid); op_ret = -1; op_errno = EPERM; goto out; } /* POSIX: If the calling process does not have appropriate privileges, and if the group ID of the file does not match the effective group ID or one of the supplementary group IDs and if the file is a regular file, bit S_ISGID (set-group-ID on execution) in the file's mode shall be cleared upon successful return from chmod().*/ if (setbuf->ia_prot.sgid == 1) { op_ret = ac_test_access (buf, 0, frame->root->gid, frame->root->groups, frame->root->ngrps, ACCTEST_DONTCARE, ACCTEST_GROUP, &op_errno); if (op_ret == -1) { gf_log (ACTRL, GF_LOG_DEBUG, "Reseting set_gid bit"); setbuf->ia_prot.sgid = 0; op_ret = 0; } } } if ((frame->root->uid != 0) && gf_attr_uid_set (valid)) { /*Only super user can change the owner, * for other users changing uid should match * owner uid*/ if (setbuf->ia_uid != buf->ia_uid) { gf_log (ACTRL, GF_LOG_DEBUG, "Mismatch set_uid %d file_uid %d", setbuf->ia_uid, buf->ia_uid); op_ret = -1; op_errno = EPERM; goto out; } } /* non-super-user can modify file group if he is owner of a * file and gid he is setting is in his groups list. * Ref: posix-testsuite/chown/00.t */ if ((frame->root->uid != 0) && (gf_attr_gid_set (valid))) { if (frame->root->uid != buf->ia_uid) { gf_log (ACTRL, GF_LOG_DEBUG, "Mismatch effective_uid %d file_uid %d", frame->root->uid, buf->ia_uid); op_ret = -1; op_errno = EPERM; goto out; } op_ret = ac_test_access (setbuf, 0, frame->root->gid, frame->root->groups, frame->root->ngrps, ACCTEST_DONTCARE, ACCTEST_GROUP, &op_errno); if (op_ret == -1) { gf_log (ACTRL, GF_LOG_DEBUG, "Set_gid %d is not in aux_gid_list", setbuf->ia_gid); goto out; } } } call_resume (stub); out: if (op_ret < 0) { gf_log (this->name, GF_LOG_ERROR, "setattr failed with error: %s", strerror (op_errno)); STACK_UNWIND_STRICT (setattr, frame, -1, op_errno, NULL, NULL); if (stub) call_stub_destroy (stub); } return 0; } int32_t ac_setattr (call_frame_t *frame, xlator_t *this, loc_t *loc, struct iatt *buf, int32_t valid) { call_stub_t *stub = NULL; int ret = -EFAULT; if (__is_fuse_call (frame)) { ret = ac_setattr_resume (frame, this, loc, buf, valid); return 0; } stub = fop_setattr_stub (frame, ac_setattr_resume, loc, buf, valid); if (!stub) { ret = -ENOMEM; goto out; } frame->local = stub; STACK_WIND (frame, ac_setattr_stat_cbk, FIRST_CHILD (this), FIRST_CHILD (this)->fops->stat, loc); ret = 0; out: if (ret < 0) { gf_log (this->name, GF_LOG_ERROR, "setattr failed with error: %s", strerror (-ret)); STACK_UNWIND_STRICT (setattr, frame, -1, -ret, NULL, NULL); } return 0; } int32_t ac_fsetattr_resume (call_frame_t *frame, xlator_t *this, fd_t *fd, struct iatt *buf, int32_t valid) { STACK_WIND (frame, default_fsetattr_cbk, FIRST_CHILD(this), FIRST_CHILD(this)->fops->fsetattr, fd, buf, valid); return 0; } int32_t ac_fsetattr_fstat_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, struct iatt *buf) { call_stub_t *stub = NULL; int32_t valid = 0; struct iatt *setbuf = NULL; stub = __get_frame_stub (frame); if (op_ret == -1) goto out; if ((frame->root->uid != buf->ia_uid) && (frame->root->uid != 0)) { op_ret = ac_test_access (buf, frame->root->uid, frame->root->gid, frame->root->groups, frame->root->ngrps, ACCTEST_WRITE, ACCTEST_ANY, &op_errno); if (op_ret == -1) { gf_log (ACTRL, GF_LOG_DEBUG, "Don't have write permision"); goto out; } } valid = stub->args.fsetattr.valid; setbuf = &stub->args.fsetattr.stbuf; if (gf_attr_uid_set (valid) || gf_attr_gid_set (valid) || gf_attr_mode_set(valid)) { /* chown/chmod returns EPERM if the operation would change the * ownership/permissions, but the effective user ID is not the * super-user and the process is not an owner of the file. * Ref: posix-testsuite/chown/07.t */ if ((frame->root->uid != 0) && gf_attr_mode_set(valid)) { if (frame->root->uid != buf->ia_uid) { gf_log (ACTRL, GF_LOG_DEBUG, "Mismatch effective_uid %d file_uid %d", frame->root->uid, buf->ia_uid); op_ret = -1; op_errno = EPERM; goto out; } /* POSIX: If the calling process does not have appropriate privileges, and if the group ID of the file does not match the effective group ID or one of the supplementary group IDs and if the file is a regular file, bit S_ISGID (set-group-ID on execution) in the file's mode shall be cleared upon successful return from chmod().*/ if (setbuf->ia_prot.sgid == 1) { op_ret = ac_test_access (buf, 0, frame->root->gid, frame->root->groups, frame->root->ngrps, ACCTEST_DONTCARE, ACCTEST_GROUP, &op_errno); if (op_ret == -1) { gf_log (ACTRL, GF_LOG_DEBUG, "Reseting set_gid bit"); setbuf->ia_prot.sgid = 0; op_ret = 0; } } } if ((frame->root->uid != 0) && gf_attr_uid_set (valid)) { /*Only super user can change the owner, * for other users changing uid should match * owner uid*/ if (setbuf->ia_uid != buf->ia_uid) { gf_log (ACTRL, GF_LOG_DEBUG, "Mismatch set_uid %d file_uid %d", setbuf->ia_uid, buf->ia_uid); op_ret = -1; op_errno = EPERM; goto out; } } /* non-super-user can modify file group if he is owner of a * file and gid he is setting is in his groups list. * Ref: posix-testsuite/chown/00.t */ if ((frame->root->uid != 0) && (gf_attr_gid_set (valid))) { if (frame->root->uid != buf->ia_uid) { gf_log (ACTRL, GF_LOG_DEBUG, "Mismatch effective_uid %d file_uid %d", frame->root->uid, buf->ia_uid); op_ret = -1; op_errno = EPERM; goto out; } op_ret = ac_test_access (setbuf, 0, frame->root->gid, frame->root->groups, frame->root->ngrps, ACCTEST_DONTCARE, ACCTEST_GROUP, &op_errno); if (op_ret == -1) { gf_log (ACTRL, GF_LOG_DEBUG, "Set_gid %d is not in aux_gid_list", setbuf->ia_gid); goto out; } } } call_resume (stub); out: if (op_ret < 0) { gf_log (this->name, GF_LOG_ERROR, "fsetattr failed with error: %s", strerror (op_errno)); STACK_UNWIND_STRICT (fsetattr, frame, -1, op_errno, NULL, NULL); if (stub) call_stub_destroy (stub); } return 0; } int32_t ac_fsetattr (call_frame_t *frame, xlator_t *this, fd_t *fd, struct iatt *buf, int32_t valid) { call_stub_t *stub = NULL; int ret = -EFAULT; if (__is_fuse_call (frame)) { ret = ac_fsetattr_resume (frame, this, fd, buf, valid); return 0; } stub = fop_fsetattr_stub (frame, ac_fsetattr_resume, fd, buf, valid); if (!stub) { ret = -ENOMEM; goto out; } frame->local = stub; STACK_WIND (frame, ac_fsetattr_fstat_cbk, FIRST_CHILD (this), FIRST_CHILD (this)->fops->fstat, fd); ret = 0; out: if (ret < 0) { gf_log (this->name, GF_LOG_ERROR, "fsetattr failed with error: %s", strerror (-ret)); STACK_UNWIND_STRICT (fsetattr, frame, -1, -ret, NULL, NULL); } return 0; } struct xlator_fops fops = { .truncate = ac_truncate, .access = ac_access, .readlink = ac_readlink, .mknod = ac_mknod, .mkdir = ac_mkdir, .unlink = ac_unlink, .rmdir = ac_rmdir, .symlink = ac_symlink, .rename = ac_rename, .link = ac_link, .create = ac_create, .open = ac_open, /* * Allow Writes and Reads to proceed without permission checks because: * a. We expect that when the fds are opened, thats when the perm checks happen * depending on the read/write mode used. * * b. In case of nfs clients, we expect the nfs clients to perform the checks * based on getattr/access nfs requests. * * Keep these functions around in case we ever run into a nfs client that * depends on nfs server to perform these checks. Till then, just remove the * references from here instead. .readv = ac_readv, .writev = ac_writev, */ .opendir = ac_opendir, .setattr = ac_setattr, .fsetattr = ac_fsetattr, }; int init (xlator_t *this) { int ret = -1; if (!this->children || this->children->next) { gf_log (this->name, GF_LOG_ERROR, "FATAL: access-control not configured with " "exactly one child"); goto out; } if (!this->parents) { gf_log (this->name, GF_LOG_WARNING, "dangling volume. check volfile "); } ret = 0; out: return ret; } void fini (xlator_t *this) { return; } struct xlator_cbks cbks = { };