From 1fe5df101828cb79384be4aa37cd974ab4565b30 Mon Sep 17 00:00:00 2001 From: Maxim Mamontov Date: Wed, 9 Dec 2015 22:28:48 +0200 Subject: [PATCH] Optional authorization. --- .../stargazer/plugins/other/radius/config.cpp | 71 ++++++++++++++++--- .../stargazer/plugins/other/radius/config.h | 20 +++++- .../stargazer/plugins/other/radius/conn.cpp | 22 ++++-- .../stargazer/plugins/other/radius/conn.h | 3 +- .../stargazer/plugins/other/radius/radius.cpp | 31 +++++++- .../stargazer/plugins/other/radius/radius.h | 7 +- 6 files changed, 133 insertions(+), 21 deletions(-) diff --git a/projects/stargazer/plugins/other/radius/config.cpp b/projects/stargazer/plugins/other/radius/config.cpp index 68b8760f..79822852 100644 --- a/projects/stargazer/plugins/other/radius/config.cpp +++ b/projects/stargazer/plugins/other/radius/config.cpp @@ -20,6 +20,7 @@ #include "config.h" +#include "stg/user.h" #include "stg/common.h" #include @@ -34,6 +35,11 @@ namespace struct ParserError : public std::runtime_error { + ParserError(const std::string& message) + : runtime_error("Config is not valid. " + message), + position(0), + error(message) + {} ParserError(size_t pos, const std::string& message) : runtime_error("Parsing error at position " + x2str(pos) + ". " + message), position(pos), @@ -132,7 +138,7 @@ uid_t toUID(const std::vector& values) return -1; uid_t res = str2uid(values[0]); if (res == static_cast(-1)) - throw ParserError(0, "Invalid user name: '" + values[0] + "'"); + throw ParserError("Invalid user name: '" + values[0] + "'"); return res; } @@ -142,7 +148,7 @@ gid_t toGID(const std::vector& values) return -1; gid_t res = str2gid(values[0]); if (res == static_cast(-1)) - throw ParserError(0, "Invalid group name: '" + values[0] + "'"); + throw ParserError("Invalid group name: '" + values[0] + "'"); return res; } @@ -152,7 +158,7 @@ mode_t toMode(const std::vector& values) return -1; mode_t res = str2mode(values[0]); if (res == static_cast(-1)) - throw ParserError(0, "Invalid mode: '" + values[0] + "'"); + throw ParserError("Invalid mode: '" + values[0] + "'"); return res; } @@ -174,7 +180,7 @@ uint16_t toPort(const std::string& value) uint16_t res = 0; if (str2x(value, res) == 0) return res; - throw ParserError(0, "'" + value + "' is not a valid port number."); + throw ParserError("'" + value + "' is not a valid port number."); } typedef std::map Codes; @@ -215,6 +221,14 @@ Config::Pairs parseVector(const std::string& paramName, const std::vector& params) +{ + for (size_t i = 0; i < params.size(); ++i) + if (params[i].param == paramName) + return Config::Authorize(toPairs(params[i].value)); + return Config::Authorize(); +} + Config::ReturnCode parseReturnCode(const std::string& paramName, const std::vector& params) { for (size_t i = 0; i < params.size(); ++i) @@ -243,13 +257,13 @@ std::string parseAddress(Config::Type connectionType, const std::string& value) { size_t pos = value.find_first_of(':'); if (pos == std::string::npos) - throw ParserError(0, "Connection type is not specified. Should be either 'unix' or 'tcp'."); + throw ParserError("Connection type is not specified. Should be either 'unix' or 'tcp'."); if (connectionType == Config::UNIX) return value.substr(pos + 1); std::string address(value.substr(pos + 1)); pos = address.find_first_of(':', pos + 1); if (pos == std::string::npos) - throw ParserError(0, "Port is not specified."); + throw ParserError("Port is not specified."); return address.substr(0, pos - 1); } @@ -257,13 +271,13 @@ std::string parsePort(Config::Type connectionType, const std::string& value) { size_t pos = value.find_first_of(':'); if (pos == std::string::npos) - throw ParserError(0, "Connection type is not specified. Should be either 'unix' or 'tcp'."); + throw ParserError("Connection type is not specified. Should be either 'unix' or 'tcp'."); if (connectionType == Config::UNIX) return ""; std::string address(value.substr(pos + 1)); pos = address.find_first_of(':', pos + 1); if (pos == std::string::npos) - throw ParserError(0, "Port is not specified."); + throw ParserError("Port is not specified."); return address.substr(pos + 1); } @@ -271,13 +285,13 @@ Config::Type parseConnectionType(const std::string& address) { size_t pos = address.find_first_of(':'); if (pos == std::string::npos) - throw ParserError(0, "Connection type is not specified. Should be either 'unix' or 'tcp'."); + throw ParserError("Connection type is not specified. Should be either 'unix' or 'tcp'."); std::string type = ToLower(address.substr(0, pos)); if (type == "unix") return Config::UNIX; else if (type == "tcp") return Config::TCP; - throw ParserError(0, "Invalid connection type. Should be either 'unix' or 'tcp', got '" + type + "'"); + throw ParserError("Invalid connection type. Should be either 'unix' or 'tcp', got '" + type + "'"); } Config::Section parseSection(const std::string& paramName, const std::vector& params) @@ -287,7 +301,8 @@ Config::Section parseSection(const std::string& paramName, const std::vector& p } // namespace anonymous +bool Config::Authorize::check(const USER& user, const Config::Pairs& radiusData) const +{ + if (!m_auth) + return false; // No flag - no authorization. + + if (m_cond.empty()) + return true; // Empty parameter - always authorize. + + Config::Pairs::const_iterator it = m_cond.begin(); + for (; it != m_cond.end(); ++it) + { + const Config::Pairs::const_iterator pos = radiusData.find(it->first); + if (pos == radiusData.end()) + return false; // No required Radius parameter. + if (user.GetParamValue(it->second) != pos->second) + return false; // No match with the user. + } + + return true; +} + Config::Config(const MODULE_SETTINGS& settings) : autz(parseSection("autz", settings.moduleParams)), auth(parseSection("auth", settings.moduleParams)), @@ -334,4 +370,17 @@ Config::Config(const MODULE_SETTINGS& settings) sockGID(parseGID("sock_group", settings.moduleParams)), sockMode(parseMode("sock_mode", settings.moduleParams)) { + size_t count = 0; + if (autz.authorize.exists()) + ++count; + if (auth.authorize.exists()) + ++count; + if (postauth.authorize.exists()) + ++count; + if (preacct.authorize.exists()) + ++count; + if (acct.authorize.exists()) + ++count; + if (count > 0) + throw ParserError("Authorization flag is specified in more than one section."); } diff --git a/projects/stargazer/plugins/other/radius/config.h b/projects/stargazer/plugins/other/radius/config.h index 2d4f638f..44d5ed85 100644 --- a/projects/stargazer/plugins/other/radius/config.h +++ b/projects/stargazer/plugins/other/radius/config.h @@ -31,6 +31,8 @@ #include // uid_t, gid_t #include // mode_t +class USER; + namespace STG { @@ -52,15 +54,29 @@ struct Config UPDATED // Module sends some updates. }; + class Authorize + { + public: + Authorize() : m_auth(false) {} + Authorize(const Pairs& cond) : m_auth(true), m_cond(cond) {} + + bool check(const USER& user, const Pairs& radiusData) const; + bool exists() const { return m_auth; } + private: + bool m_auth; + Pairs m_cond; + }; + struct Section { Section() {} - Section(const Pairs& ma, const Pairs& mo, const Pairs& re, ReturnCode code) - : match(ma), modify(mo), reply(re), returnCode(code) {} + Section(const Pairs& ma, const Pairs& mo, const Pairs& re, ReturnCode code, const Authorize& auth) + : match(ma), modify(mo), reply(re), returnCode(code), authorize(auth) {} Pairs match; Pairs modify; Pairs reply; ReturnCode returnCode; + Authorize authorize; }; Config() {} diff --git a/projects/stargazer/plugins/other/radius/conn.cpp b/projects/stargazer/plugins/other/radius/conn.cpp index 8a416ae0..a209409b 100644 --- a/projects/stargazer/plugins/other/radius/conn.cpp +++ b/projects/stargazer/plugins/other/radius/conn.cpp @@ -20,6 +20,7 @@ #include "conn.h" +#include "radius.h" #include "config.h" #include "stg/json_parser.h" @@ -225,7 +226,7 @@ std::string toString(Config::ReturnCode code) class Conn::Impl { public: - Impl(USERS& users, PLUGIN_LOGGER& logger, const Config& config, int fd, const std::string& remote); + Impl(USERS& users, PLUGIN_LOGGER& logger, RADIUS& plugin, const Config& config, int fd, const std::string& remote); ~Impl(); int sock() const { return m_sock; } @@ -238,6 +239,7 @@ class Conn::Impl private: USERS& m_users; PLUGIN_LOGGER& m_logger; + RADIUS& m_plugin; const Config& m_config; int m_sock; std::string m_remote; @@ -245,6 +247,7 @@ class Conn::Impl time_t m_lastPing; time_t m_lastActivity; ProtoParser m_parser; + std::set m_authorized; template const T& stageMember(T Config::Section::* member) const @@ -264,6 +267,7 @@ class Conn::Impl const Config::Pairs& modify() const { return stageMember(&Config::Section::modify); } const Config::Pairs& reply() const { return stageMember(&Config::Section::reply); } Config::ReturnCode returnCode() const { return stageMember(&Config::Section::returnCode); } + const Config::Authorize& authorize() const { return stageMember(&Config::Section::authorize); } static void process(void* data); void processPing(); @@ -277,8 +281,8 @@ class Conn::Impl static bool write(void* data, const char* buf, size_t size); }; -Conn::Conn(USERS& users, PLUGIN_LOGGER& logger, const Config& config, int fd, const std::string& remote) - : m_impl(new Impl(users, logger, config, fd, remote)) +Conn::Conn(USERS& users, PLUGIN_LOGGER& logger, RADIUS& plugin, const Config& config, int fd, const std::string& remote) + : m_impl(new Impl(users, logger, plugin, config, fd, remote)) { } @@ -306,9 +310,10 @@ bool Conn::isOk() const return m_impl->isOk(); } -Conn::Impl::Impl(USERS& users, PLUGIN_LOGGER& logger, const Config& config, int fd, const std::string& remote) +Conn::Impl::Impl(USERS& users, PLUGIN_LOGGER& logger, RADIUS& plugin, const Config& config, int fd, const std::string& remote) : m_users(users), m_logger(logger), + m_plugin(plugin), m_config(config), m_sock(fd), m_remote(remote), @@ -322,6 +327,10 @@ Conn::Impl::Impl(USERS& users, PLUGIN_LOGGER& logger, const Config& config, int Conn::Impl::~Impl() { close(m_sock); + + std::set::const_iterator it = m_authorized.begin(); + for (; it != m_authorized.end(); ++it) + m_plugin.unauthorize(*it, "Lost connection to RADIUS server " + m_remote + "."); } bool Conn::Impl::read() @@ -433,6 +442,11 @@ void Conn::Impl::processData() if (!matched) continue; answer(*user); + if (authorize().check(*user, m_parser.data())) + { + m_plugin.authorize(*user); + m_authorized.insert(user->GetLogin()); + } break; } diff --git a/projects/stargazer/plugins/other/radius/conn.h b/projects/stargazer/plugins/other/radius/conn.h index 17ebc8cf..96e74300 100644 --- a/projects/stargazer/plugins/other/radius/conn.h +++ b/projects/stargazer/plugins/other/radius/conn.h @@ -28,6 +28,7 @@ class USER; class USERS; class PLUGIN_LOGGER; +class RADIUS; namespace STG { @@ -37,7 +38,7 @@ struct Config; class Conn { public: - Conn(USERS& users, PLUGIN_LOGGER& logger, const Config& config, int fd, const std::string& remote); + Conn(USERS& users, PLUGIN_LOGGER& logger, RADIUS& plugin, const Config& config, int fd, const std::string& remote); ~Conn(); int sock() const; diff --git a/projects/stargazer/plugins/other/radius/radius.cpp b/projects/stargazer/plugins/other/radius/radius.cpp index 376e4278..45a9d0e1 100644 --- a/projects/stargazer/plugins/other/radius/radius.cpp +++ b/projects/stargazer/plugins/other/radius/radius.cpp @@ -91,6 +91,11 @@ int RADIUS::Start() int RADIUS::Stop() { + std::set::const_iterator it = m_logins.begin(); + for (; it != m_logins.end(); ++it) + m_users->Unauthorize(*it, this, "Stopping RADIUS plugin."); + m_logins.clear(); + if (m_stopped) return 0; @@ -342,7 +347,7 @@ void RADIUS::acceptUNIX() return; } printfd(__FILE__, "New UNIX connection: '%s'\n", addr.sun_path); - m_conns.push_back(new Conn(*m_users, m_logger, m_config, res, addr.sun_path)); + m_conns.push_back(new Conn(*m_users, m_logger, *this, m_config, res, addr.sun_path)); } void RADIUS::acceptTCP() @@ -359,5 +364,27 @@ void RADIUS::acceptTCP() } std::string remote = inet_ntostring(addr.sin_addr.s_addr) + ":" + x2str(ntohs(addr.sin_port)); printfd(__FILE__, "New TCP connection: '%s'\n", remote.c_str()); - m_conns.push_back(new Conn(*m_users, m_logger, m_config, res, remote)); + m_conns.push_back(new Conn(*m_users, m_logger, *this, m_config, res, remote)); +} + +void RADIUS::authorize(const USER& user) +{ + uint32_t ip = 0; + const std::string& login(user.GetLogin()); + if (!m_users->Authorize(login, ip, 0xffFFffFF, this)) + { + m_error = "Unable to authorize user '" + login + "' with ip " + inet_ntostring(ip) + "."; + m_logger(m_error); + } + else + m_logins.insert(login); +} + +void RADIUS::unauthorize(const std::string& login, const std::string& reason) +{ + const std::set::const_iterator it = m_logins.find(login); + if (it == m_logins.end()) + return; + m_logins.erase(it); + m_users->Unauthorize(login, this, reason); } diff --git a/projects/stargazer/plugins/other/radius/radius.h b/projects/stargazer/plugins/other/radius/radius.h index 2573cf9e..742923f0 100644 --- a/projects/stargazer/plugins/other/radius/radius.h +++ b/projects/stargazer/plugins/other/radius/radius.h @@ -31,6 +31,7 @@ #include #include +#include #include #include @@ -59,12 +60,15 @@ public: bool IsRunning() { return m_running; } const std::string& GetStrError() const { return m_error; } - std::string GetVersion() const { return "RADIUS data access plugin v 1.0"; } + std::string GetVersion() const { return "RADIUS data access plugin v. 2.0"; } uint16_t GetStartPosition() const { return 30; } uint16_t GetStopPosition() const { return 30; } int SendMessage(const STG_MSG&, uint32_t) const { return 0; } + void authorize(const USER& user); + void unauthorize(const std::string& login, const std::string& reason); + private: RADIUS(const RADIUS & rvalue); RADIUS & operator=(const RADIUS & rvalue); @@ -96,6 +100,7 @@ private: int m_listenSocket; std::deque m_conns; + std::set m_logins; pthread_t m_thread; -- 2.43.2