From 3dfb23d036c068ec5166151ed63628738d68908a Mon Sep 17 00:00:00 2001 From: Jason Dillaman Date: Tue, 5 Jun 2018 13:24:48 -0400 Subject: [PATCH] mon/OSDMonitor: enforce caps when creating/deleting unmanaged snapshots The entity will require write access to the OSD service or permission for the synthetic "osd pool op unmanaged-snap" command. Signed-off-by: Jason Dillaman (cherry picked from commit 4972e054b32c8200600f27564d50d443e683153e) Conflicts: src/mon/OSDMonitor.cc: use 'bufferlist::begin' instead of 'bufferlist::cbegin' --- src/mon/OSDMonitor.cc | 131 +++++++++++++++++++++++++++++++++++++++++- src/mon/OSDMonitor.h | 1 + 2 files changed, 130 insertions(+), 2 deletions(-) diff --git a/src/mon/OSDMonitor.cc b/src/mon/OSDMonitor.cc index 244c8dbf1b21a..1229713e576ac 100644 --- a/src/mon/OSDMonitor.cc +++ b/src/mon/OSDMonitor.cc @@ -76,6 +76,9 @@ #include "include/str_map.h" #include "include/scope_guard.h" +#include "auth/cephx/CephxKeyServer.h" +#include "osd/OSDCap.h" + #include "json_spirit/json_spirit_reader.h" #include @@ -91,6 +94,87 @@ const uint32_t MAX_POOL_APPLICATIONS = 4; const uint32_t MAX_POOL_APPLICATION_KEYS = 64; const uint32_t MAX_POOL_APPLICATION_LENGTH = 128; +bool is_osd_writable(const OSDCapGrant& grant, const std::string* pool_name) { + // Note: this doesn't include support for the application tag match + if ((grant.spec.allow & OSD_CAP_W) != 0) { + auto& match = grant.match; + if (match.is_match_all()) { + return true; + } else if (pool_name != nullptr && match.auid < 0 && + !match.pool_namespace.pool_name.empty() && + match.pool_namespace.pool_name == *pool_name) { + return true; + } + } + return false; +} + +bool is_unmanaged_snap_op_permitted(CephContext* cct, + const KeyServer& key_server, + const EntityName& entity_name, + const MonCap& mon_caps, + const std::string* pool_name) +{ + typedef std::map CommandArgs; + + if (mon_caps.is_capable(cct, CEPH_ENTITY_TYPE_MON, + entity_name, "osd", + "osd pool op unmanaged-snap", + (pool_name == nullptr ? + CommandArgs{} /* pool DNE, require unrestricted cap */ : + CommandArgs{{"poolname", *pool_name}}), + false, true, false)) { + return true; + } + + AuthCapsInfo caps_info; + if (!key_server.get_service_caps(entity_name, CEPH_ENTITY_TYPE_OSD, + caps_info)) { + dout(10) << "unable to locate OSD cap data for " << entity_name + << " in auth db" << dendl; + return false; + } + + string caps_str; + if (caps_info.caps.length() > 0) { + auto p = caps_info.caps.begin(); + try { + decode(caps_str, p); + } catch (const buffer::error &err) { + derr << "corrupt OSD cap data for " << entity_name << " in auth db" + << dendl; + return false; + } + } + + OSDCap osd_cap; + if (!osd_cap.parse(caps_str, nullptr)) { + dout(10) << "unable to parse OSD cap data for " << entity_name + << " in auth db" << dendl; + return false; + } + + // if the entity has write permissions in one or all pools, permit + // usage of unmanaged-snapshots + if (osd_cap.allow_all()) { + return true; + } + + for (auto& grant : osd_cap.grants) { + if (grant.profile.is_valid()) { + for (auto& profile_grant : grant.profile_grants) { + if (is_osd_writable(profile_grant, pool_name)) { + return true; + } + } + } else if (is_osd_writable(grant, pool_name)) { + return true; + } + } + + return false; +} + } // anonymous namespace void LastEpochClean::Lec::report(ps_t ps, epoch_t last_epoch_clean) @@ -11698,11 +11782,54 @@ bool OSDMonitor::prepare_command_impl(MonOpRequestRef op, return true; } -bool OSDMonitor::preprocess_pool_op(MonOpRequestRef op) +bool OSDMonitor::enforce_pool_op_caps(MonOpRequestRef op) { op->mark_osdmon_event(__func__); + MPoolOp *m = static_cast(op->get_req()); - + MonSession *session = m->get_session(); + if (!session) { + _pool_op_reply(op, -EPERM, osdmap.get_epoch()); + return true; + } + + switch (m->op) { + case POOL_OP_CREATE_UNMANAGED_SNAP: + case POOL_OP_DELETE_UNMANAGED_SNAP: + { + const std::string* pool_name = nullptr; + const pg_pool_t *pg_pool = osdmap.get_pg_pool(m->pool); + if (pg_pool != nullptr) { + pool_name = &osdmap.get_pool_name(m->pool); + } + + if (!is_unmanaged_snap_op_permitted(cct, mon->key_server, + session->entity_name, session->caps, + pool_name)) { + dout(0) << "got unmanaged-snap pool op from entity with insufficient " + << "privileges. message: " << *m << std::endl + << "caps: " << session->caps << dendl; + _pool_op_reply(op, -EPERM, osdmap.get_epoch()); + return true; + } + } + break; + default: + break; + } + + return false; +} + +bool OSDMonitor::preprocess_pool_op(MonOpRequestRef op) +{ + op->mark_osdmon_event(__func__); + MPoolOp *m = static_cast(op->get_req()); + + if (enforce_pool_op_caps(op)) { + return true; + } + if (m->fsid != mon->monmap->fsid) { dout(0) << __func__ << " drop message on fsid " << m->fsid << " != " << mon->monmap->fsid << " for " << *m << dendl; diff --git a/src/mon/OSDMonitor.h b/src/mon/OSDMonitor.h index 92f83ea475abf..013bd6865044c 100644 --- a/src/mon/OSDMonitor.h +++ b/src/mon/OSDMonitor.h @@ -403,6 +403,7 @@ private: int _prepare_remove_pool(int64_t pool, ostream *ss, bool no_fake); int _prepare_rename_pool(int64_t pool, string newname); + bool enforce_pool_op_caps(MonOpRequestRef op); bool preprocess_pool_op (MonOpRequestRef op); bool preprocess_pool_op_create (MonOpRequestRef op); bool prepare_pool_op (MonOpRequestRef op); -- 2.39.5