From: Sage Weil Date: Tue, 7 Mar 2017 20:36:36 +0000 (-0500) Subject: mgr/DaemonServer: enforce caps X-Git-Tag: v12.0.2~252^2~41 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=23575077153ffe35787b5c8bd3619e0cd125e4f9;p=ceph.git mgr/DaemonServer: enforce caps Signed-off-by: Sage Weil --- diff --git a/src/mgr/DaemonServer.cc b/src/mgr/DaemonServer.cc index 72fb03967884..b154b8eee2bf 100644 --- a/src/mgr/DaemonServer.cc +++ b/src/mgr/DaemonServer.cc @@ -128,6 +128,7 @@ bool DaemonServer::ms_verify_authorizer(Connection *con, } MgrSessionRef s(new MgrSession); + s->addr = con->get_peer_addr(); AuthCapsInfo caps_info; is_valid = handler->verify_authorizer( @@ -280,6 +281,11 @@ struct MgrCommand { string module; string perm; string availability; + + bool requires_perm(char p) const { + return (perm.find(p) != string::npos); + } + } mgr_commands[] = { #define COMMAND(parsesig, helptext, module, perm, availability) \ @@ -288,6 +294,71 @@ struct MgrCommand { #undef COMMAND }; +void DaemonServer::_generate_command_map( + map& cmdmap, + map ¶m_str_map) +{ + for (map::const_iterator p = cmdmap.begin(); + p != cmdmap.end(); ++p) { + if (p->first == "prefix") + continue; + if (p->first == "caps") { + vector cv; + if (cmd_getval(g_ceph_context, cmdmap, "caps", cv) && + cv.size() % 2 == 0) { + for (unsigned i = 0; i < cv.size(); i += 2) { + string k = string("caps_") + cv[i]; + param_str_map[k] = cv[i + 1]; + } + continue; + } + } + param_str_map[p->first] = cmd_vartype_stringify(p->second); + } +} + +const MgrCommand *DaemonServer::_get_mgrcommand( + const string &cmd_prefix, + MgrCommand *cmds, + int cmds_size) +{ + MgrCommand *this_cmd = NULL; + for (MgrCommand *cp = cmds; + cp < &cmds[cmds_size]; cp++) { + if (cp->cmdstring.compare(0, cmd_prefix.size(), cmd_prefix) == 0) { + this_cmd = cp; + break; + } + } + return this_cmd; +} + +bool DaemonServer::_allowed_command( + MgrSession *s, + const string &module, + const string &prefix, + const map& cmdmap, + const map& param_str_map, + const MgrCommand *this_cmd) { + + if (s->entity_name.is_mon()) { + // mon is all-powerful. even when it is forwarding commands on behalf of + // old clients; we expect the mon is validating commands before proxying! + return true; + } + + bool cmd_r = this_cmd->requires_perm('r'); + bool cmd_w = this_cmd->requires_perm('w'); + bool cmd_x = this_cmd->requires_perm('x'); + + bool capable = s->caps.is_capable(g_ceph_context, s->entity_name, + module, prefix, param_str_map, + cmd_r, cmd_w, cmd_x); + + dout(10) << " " << (capable ? "" : "not ") << "capable" << dendl; + return capable; +} + bool DaemonServer::handle_command(MCommand *m) { int r = 0; @@ -299,14 +370,20 @@ bool DaemonServer::handle_command(MCommand *m) cmdmap_t cmdmap; - // TODO enforce some caps - // TODO background the call into python land so that we don't // block a messenger thread on python code. ConnectionRef con = m->get_connection(); + MgrSessionRef session(static_cast(con->get_priv())); + if (!session) { + return true; + } + session->put(); // SessionRef takes a ref + string format; boost::scoped_ptr f; + const MgrCommand *mgr_cmd; + map param_str_map; if (!cmdmap_from_json(m->cmd, &cmdmap, ss)) { r = -EINVAL; @@ -355,12 +432,37 @@ bool DaemonServer::handle_command(MCommand *m) goto out; } + // lookup command + mgr_cmd = _get_mgrcommand(prefix, mgr_commands, + ARRAY_SIZE(mgr_commands)); + _generate_command_map(cmdmap, param_str_map); + if (!mgr_cmd) { + ss << "command not supported"; + r = -EINVAL; + goto out; + } + + // validate user's permissions for requested command + if (!_allowed_command(session.get(), mgr_cmd->module, prefix, cmdmap, + param_str_map, mgr_cmd)) { + dout(1) << __func__ << " access denied" << dendl; +#if 0 + // FIXME: audit_clog? + clog->info() << "from='" << session->addr << "' " + << "entity='" << session->entity_name << "' " + << "cmd=" << m->cmd << ": access denied"; +#endif + ss << "access denied"; + r = -EACCES; + goto out; + } + // ----------- // PG commands - else if (prefix == "pg scrub" || - prefix == "pg repair" || - prefix == "pg deep-scrub") { + if (prefix == "pg scrub" || + prefix == "pg repair" || + prefix == "pg deep-scrub") { string scrubop = prefix.substr(3, string::npos); pg_t pgid; string pgidstr; diff --git a/src/mgr/DaemonServer.h b/src/mgr/DaemonServer.h index ee805919d1bb..5f020ea7a514 100644 --- a/src/mgr/DaemonServer.h +++ b/src/mgr/DaemonServer.h @@ -33,6 +33,7 @@ class MMgrReport; class MMgrOpen; class MCommand; +struct MgrCommand; /** * Session state associated with the Connection. @@ -40,6 +41,7 @@ class MCommand; struct MgrSession : public RefCountedObject { uint64_t global_id = 0; EntityName entity_name; + entity_addr_t addr; // mon caps are suitably generic for mgr MonCap caps; @@ -76,6 +78,15 @@ protected: Mutex lock; + static void _generate_command_map(map& cmdmap, + map ¶m_str_map); + static const MgrCommand *_get_mgrcommand(const string &cmd_prefix, + MgrCommand *cmds, int cmds_size); + bool _allowed_command( + MgrSession *s, const string &module, const string &prefix, + const map& cmdmap, + const map& param_str_map, + const MgrCommand *this_cmd); public: int init(uint64_t gid, entity_addr_t client_addr);