/* prelude_perm_check() * * this really just marks the spot where we would want to do * permission checking, it will be replaced by a couple of states that * actually perform this task later */ static PINT_sm_action prelude_perm_check( struct PINT_smcb *smcb, job_status_s *js_p) { struct PINT_server_op *s_op = PINT_sm_frame(smcb, PINT_FRAME_CURRENT); PVFS_object_attr *obj_attr = NULL; PVFS_ds_attributes *ds_attr = NULL; PVFS_uid translated_uid = s_op->req->credentials.uid; PVFS_gid translated_gid = s_op->req->credentials.gid; PVFS_fs_id fsid = PVFS_FS_ID_NULL; int squashed_flag = 0; int skip_acl_flag = 0; /* moved gossip server debug output to end of state, so we can report * resulting status value. */ /* first we translate the dspace attributes into a more convenient server use-able format. i.e. a PVFS_object_attr */ ds_attr = &s_op->ds_attr; obj_attr = &s_op->attr; PVFS_ds_attr_to_object_attr(ds_attr, obj_attr); s_op->attr.mask = PVFS_ATTR_COMMON_ALL; /* Set the target object attribute pointer.. used later by the acl check */ s_op->target_object_attr = obj_attr; if (s_op->target_fs_id != PVFS_FS_ID_NULL) { /* * if we are exporting a volume readonly, disallow any operation that modifies * the state of the file-system. */ if (permit_operation( s_op->target_fs_id, s_op->access_type, s_op->addr) < 0) { js_p->error_code = -PVFS_EROFS; return SM_ACTION_COMPLETE; } else { /* Translate the uid and gid's in case we need to do some squashing based on the export and the client address */ if (translate_ids(fsid, s_op->req->credentials.uid, s_op->req->credentials.gid, &translated_uid, &translated_gid, s_op->addr) == 1) { squashed_flag = 1; s_op->req->credentials.uid = translated_uid; s_op->req->credentials.gid = translated_gid; /* in the case of a setattr, translate the ids as well right here */ if (s_op->req->op == PVFS_SERV_SETATTR) { s_op->req->u.setattr.attr.owner = translated_uid; s_op->req->u.setattr.attr.group = translated_gid; } else if (s_op->req->op == PVFS_SERV_MKDIR) { s_op->req->u.mkdir.attr.owner = translated_uid; s_op->req->u.mkdir.attr.group = translated_gid; } } } } /* anything else we treat as a real error */ if (js_p->error_code) { js_p->error_code = -PVFS_ERROR_CODE(-js_p->error_code); return SM_ACTION_COMPLETE; } gossip_debug( GOSSIP_PERMISSIONS_DEBUG, "PVFS operation \"%s\" got " "attr mask %d\n\t(attr_uid_valid? %s, attr_owner = " "%d, credentials_uid = %d)\n\t(attr_gid_valid? %s, attr_group = " "%d, credentials.gid = %d)\n", PINT_map_server_op_to_string(s_op->req->op), s_op->attr.mask, ((s_op->attr.mask & PVFS_ATTR_COMMON_UID) ? "yes" : "no"), s_op->attr.owner, translated_uid, ((s_op->attr.mask & PVFS_ATTR_COMMON_GID) ? "yes" : "no"), s_op->attr.group, translated_gid); switch(PINT_server_req_get_perms(s_op->req)) { case PINT_SERVER_CHECK_WRITE: js_p->error_code = PINT_check_mode( &(s_op->attr), translated_uid, translated_gid, PINT_ACCESS_WRITABLE); break; case PINT_SERVER_CHECK_READ: js_p->error_code = PINT_check_mode( &(s_op->attr), translated_uid, translated_gid, PINT_ACCESS_READABLE); break; case PINT_SERVER_CHECK_CRDIRENT: /* must also check executable after writable */ js_p->error_code = PINT_check_mode( &(s_op->attr), translated_uid, translated_gid, PINT_ACCESS_WRITABLE); if(js_p->error_code == 0) { js_p->error_code = PINT_check_mode( &(s_op->attr), translated_uid, translated_gid, PINT_ACCESS_EXECUTABLE); } break; case PINT_SERVER_CHECK_ATTR: /* let datafiles pass through the attr check */ if (s_op->attr.objtype == PVFS_TYPE_DATAFILE) { js_p->error_code = 0; } /* for now we'll assume extended attribs are treated * the same as regular attribs as far as permissions */ else if (s_op->req->op == PVFS_SERV_GETATTR || s_op->req->op == PVFS_SERV_GETEATTR || s_op->req->op == PVFS_SERV_LISTEATTR) { /* getting or listing attributes is always ok -- permission * is checked on the parent directory at read time */ js_p->error_code = 0; } else /* setattr, seteattr, seteattr_list */ { if(s_op->attr.perms == 0 && s_op->attr.objtype == PVFS_TYPE_SYMLINK) { /* if the object is of type symlink but has empty perms, * then it must be a newly created symlink object that * does not have its true attributes set yet. Let this * operation through. */ js_p->error_code = 0; } else if(s_op->attr.owner == translated_uid || translated_uid == 0) { /* owner of file and root can always set attributes (see * iozone, which does a setattr as part of truncating a * file with permission mask set to 0 */ js_p->error_code = 0; } else { /* normal setattr requires write permissions on existing * objects */ js_p->error_code = PINT_check_mode( &(s_op->attr), translated_uid, translated_gid, PINT_ACCESS_WRITABLE); } } break; case PINT_SERVER_CHECK_NONE: if(squashed_flag && PINT_server_req_get_access_type(s_op->req) == PINT_SERVER_REQ_MODIFY && ((s_op->req->op == PVFS_SERV_IO) || (s_op->req->op == PVFS_SERV_SMALL_IO) || (s_op->req->op == PVFS_SERV_TRUNCATE))) { /* special case: * If we have been squashed, deny write permission to the * file system. At the datafile level we don't have enough * attribute information to figure out if the nobody/guest * user has permission to write or not, so we disallow all * writes to be safe. Not perfect semantics, but better * than being too permissive. */ skip_acl_flag = 1; js_p->error_code = -PVFS_EACCES; } else { js_p->error_code = 0; } break; case PINT_SERVER_CHECK_INVALID: js_p->error_code = -PVFS_EINVAL; break; } gossip_debug( GOSSIP_PERMISSIONS_DEBUG, "Final permission check for \"%s\" set " "error code to %d\n", PINT_map_server_op_to_string(s_op->req->op), js_p->error_code); gossip_debug(GOSSIP_SERVER_DEBUG, "(%p) %s (prelude sm) state: perm_check (status = %d)\n", s_op, PINT_map_server_op_to_string(s_op->req->op), js_p->error_code); /* If regular checks fail, we need to run acl checks */ if (js_p->error_code == -PVFS_EACCES && !skip_acl_flag) js_p->error_code = PRELUDE_RUN_ACL_CHECKS; return SM_ACTION_COMPLETE; }
/** * PVFSIOStore::Access: permission check * * @param path the path we are checking * @param mode the mode to check * @return PLFS_SUCCESS or PLFS_E* on error */ plfs_error_t PVFSIOStore::Access(const char* path, int mode) { char *cpath; PVFS_object_ref ref; PVFS_credentials creds; int nev, pev; PVFS_sysresp_getattr rep; PVFS_uid auid; PVFS_gid agid; PVFS_permissions aperms; cpath = pvfsios_dedup_slash(path); if (cpath) { nev = pvfsios_get_object(this->fsid, cpath, &ref, &creds, PVFS2_LOOKUP_LINK_FOLLOW); free(cpath); } else { nev = -ENOMEM; } if (nev < 0) { return errno_to_plfs_error(-nev); } /* root or exist check */ if (creds.uid == 0 || mode == F_OK) { return PLFS_SUCCESS; } pev = PVFS_sys_getattr(ref, PVFS_ATTR_SYS_ALL_NOHINT, &creds, &rep); if (pev < 0) { return errno_to_plfs_error(get_err(pev)); } #if 0 /* * XXXCDC: we'd really like to call PINT_check_mode, but libpvfs2 * includes don't provide a prototype for it even though it is * present in the lib. does that mean it is a private interface? */ pev = PINT_check_mode(&rep.attr, creds.uid, credis.gid, 0); PVFS_util_release_sys_attr(&rep.attr); /* frees memory chained off ats */ return(get_err(pev)); #endif /* * XXX: pvfs2fuse doesn't call PVFS_util_release_sys_attr on * repl.attr for access. this seems like a memory leak mistake to * me. */ auid = rep.attr.owner; agid = rep.attr.group; aperms = rep.attr.perms; PVFS_util_release_sys_attr(&rep.attr); /* frees memory chained off ats */ if (auid == creds.uid) { if ((mode & R_OK) && (aperms & PVFS_U_READ)) return PLFS_SUCCESS; if ((mode & W_OK) && (aperms & PVFS_U_WRITE)) return PLFS_SUCCESS; if ((mode & X_OK) && (aperms & PVFS_U_EXECUTE)) return PLFS_SUCCESS; } /* XXXCDC: doesn't check group list, e.g. getgroups() */ if (agid == creds.gid) { if ((mode & R_OK) && (aperms & PVFS_G_READ)) return PLFS_SUCCESS; if ((mode & W_OK) && (aperms & PVFS_G_WRITE)) return PLFS_SUCCESS; if ((mode & X_OK) && (aperms & PVFS_G_EXECUTE)) return PLFS_SUCCESS; } if ((mode & R_OK) && (aperms & PVFS_O_READ)) return PLFS_SUCCESS; if ((mode & W_OK) && (aperms & PVFS_O_WRITE)) return PLFS_SUCCESS; if ((mode & X_OK) && (aperms & PVFS_O_EXECUTE)) return PLFS_SUCCESS; return PLFS_EACCES; }