// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- // vim: ts=8 sw=2 smarttab /* * Ceph - scalable distributed file system * * Copyright (C) 2014 Red Hat * * This is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License version 2.1, as published by the Free Software * Foundation. See file COPYING. * */ #ifndef MDS_AUTH_CAPS_H #define MDS_AUTH_CAPS_H #include #include #include #include #include "include/common_fwd.h" #include "include/types.h" #include "common/debug.h" #include "mdstypes.h" // unix-style capabilities enum { MAY_READ = (1 << 0), MAY_WRITE = (1 << 1), MAY_EXECUTE = (1 << 2), MAY_CHOWN = (1 << 4), MAY_CHGRP = (1 << 5), MAY_SET_VXATTR = (1 << 6), MAY_SNAPSHOT = (1 << 7), MAY_FULL = (1 << 8), }; // what we can do struct MDSCapSpec { static const unsigned ALL = (1 << 0); static const unsigned READ = (1 << 1); static const unsigned WRITE = (1 << 2); // if the capability permits setting vxattrs (layout, quota, etc) static const unsigned SET_VXATTR = (1 << 3); // if the capability permits mksnap/rmsnap static const unsigned SNAPSHOT = (1 << 4); // if the capability permits to bypass osd full check static const unsigned FULL = (1 << 5); static const unsigned RW = (READ|WRITE); static const unsigned RWF = (READ|WRITE|FULL); static const unsigned RWP = (READ|WRITE|SET_VXATTR); static const unsigned RWS = (READ|WRITE|SNAPSHOT); static const unsigned RWFP = (READ|WRITE|FULL|SET_VXATTR); static const unsigned RWFS = (READ|WRITE|FULL|SNAPSHOT); static const unsigned RWPS = (READ|WRITE|SET_VXATTR|SNAPSHOT); static const unsigned RWFPS = (READ|WRITE|FULL|SET_VXATTR|SNAPSHOT); MDSCapSpec() = default; MDSCapSpec(unsigned _caps) : caps(_caps) { if (caps & ALL) caps |= RWFPS; } bool allow_all() const { return (caps & ALL); } bool allow_read() const { return (caps & READ); } bool allow_write() const { return (caps & WRITE); } bool allows(bool r, bool w) const { if (allow_all()) return true; if (r && !allow_read()) return false; if (w && !allow_write()) return false; return true; } bool allow_snapshot() const { return (caps & SNAPSHOT); } bool allow_set_vxattr() const { return (caps & SET_VXATTR); } bool allow_full() const { return (caps & FULL); } private: unsigned caps = 0; }; // conditions before we are allowed to do it struct MDSCapMatch { static const int64_t MDS_AUTH_UID_ANY = -1; MDSCapMatch() : uid(MDS_AUTH_UID_ANY), fs_name(std::string()) {} MDSCapMatch(int64_t uid_, std::vector& gids_) : uid(uid_), gids(gids_), fs_name(std::string()) {} explicit MDSCapMatch(const std::string &path_) : uid(MDS_AUTH_UID_ANY), path(path_), fs_name(std::string()) { normalize_path(); } explicit MDSCapMatch(std::string path, std::string fs_name) : uid(MDS_AUTH_UID_ANY), path(std::move(path)), fs_name(std::move(fs_name)) { normalize_path(); } explicit MDSCapMatch(std::string path, std::string fs_name, bool root_squash_) : uid(MDS_AUTH_UID_ANY), path(std::move(path)), fs_name(std::move(fs_name)), root_squash(root_squash_) { normalize_path(); } MDSCapMatch(const std::string& path_, int64_t uid_, std::vector& gids_) : uid(uid_), gids(gids_), path(path_), fs_name(std::string()) { normalize_path(); } void normalize_path(); bool is_match_all() const { return uid == MDS_AUTH_UID_ANY && path == ""; } // check whether this grant matches against a given file and caller uid:gid bool match(std::string_view target_path, const int caller_uid, const int caller_gid, const std::vector *caller_gid_list) const; /** * Check whether this path *might* be accessible (actual permission * depends on the stronger check in match()). * * @param target_path filesystem path without leading '/' */ bool match_path(std::string_view target_path) const; int64_t uid; // Require UID to be equal to this, if !=MDS_AUTH_UID_ANY std::vector gids; // Use these GIDs std::string path; // Require path to be child of this (may be "" or "/" for any) std::string fs_name; bool root_squash=false; }; struct MDSCapGrant { MDSCapGrant(const MDSCapSpec &spec_, const MDSCapMatch &match_, boost::optional n) : spec(spec_), match(match_) { if (n) { network = *n; parse_network(); } } MDSCapGrant() {} void parse_network(); MDSCapSpec spec; MDSCapMatch match; std::string network; entity_addr_t network_parsed; unsigned network_prefix = 0; bool network_valid = true; }; class MDSAuthCaps { public: MDSAuthCaps() = default; explicit MDSAuthCaps(CephContext *cct_) : cct(cct_) {} // this ctor is used by spirit/phoenix; doesn't need cct. explicit MDSAuthCaps(const std::vector& grants_) : grants(grants_) {} void clear() { grants.clear(); } void set_allow_all(); bool parse(CephContext *cct, std::string_view str, std::ostream *err); bool allow_all() const; bool is_capable(std::string_view inode_path, uid_t inode_uid, gid_t inode_gid, unsigned inode_mode, uid_t uid, gid_t gid, const std::vector *caller_gid_list, unsigned mask, uid_t new_uid, gid_t new_gid, const entity_addr_t& addr) const; bool path_capable(std::string_view inode_path) const; bool fs_name_capable(std::string_view fs_name, unsigned mask) const { if (allow_all()) { return true; } for (const MDSCapGrant &g : grants) { if (g.match.fs_name == fs_name || g.match.fs_name.empty() || g.match.fs_name == "*") { if (mask & MAY_READ && g.spec.allow_read()) { return true; } if (mask & MAY_WRITE && g.spec.allow_write()) { return true; } } } return false; } friend std::ostream &operator<<(std::ostream &out, const MDSAuthCaps &cap); private: CephContext *cct = nullptr; std::vector grants; }; std::ostream &operator<<(std::ostream &out, const MDSCapMatch &match); std::ostream &operator<<(std::ostream &out, const MDSCapSpec &spec); std::ostream &operator<<(std::ostream &out, const MDSCapGrant &grant); std::ostream &operator<<(std::ostream &out, const MDSAuthCaps &cap); #endif // MDS_AUTH_CAPS_H