diff --git a/projects/stargazer/plugins/CMakeLists.txt b/projects/stargazer/plugins/CMakeLists.txt index fc030b67..7d0e02e6 100644 --- a/projects/stargazer/plugins/CMakeLists.txt +++ b/projects/stargazer/plugins/CMakeLists.txt @@ -15,7 +15,8 @@ if ( BUILD_MOD_RADIUS ) find_package ( Boost REQUIRED ) add_library ( mod_radius MODULE other/radius/radius.cpp - other/radius/server.cpp) + other/radius/server.cpp + other/radius/config.cpp) target_link_libraries ( mod_radius PRIVATE scriptexecuter logger common ) set_target_properties ( mod_radius PROPERTIES PREFIX "" ) @@ -23,7 +24,7 @@ if ( BUILD_MOD_RADIUS ) ExternalProject_Add ( async-radius GIT_REPOSITORY https://github.com/madf/async-radius.git - GIT_TAG 1.1.3 + GIT_TAG 1.3.0 GIT_SHALLOW true INSTALL_COMMAND "" CMAKE_ARGS -DCMAKE_CXX_FLAGS=-fPIC ) diff --git a/projects/stargazer/plugins/other/radius/config.cpp b/projects/stargazer/plugins/other/radius/config.cpp new file mode 100644 index 00000000..c273761f --- /dev/null +++ b/projects/stargazer/plugins/other/radius/config.cpp @@ -0,0 +1,147 @@ +#include "config.h" +#include "radproto/error.h" +#include "stg/common.h" +#include + +#include +#include +#include +#include +#include + +using STG::Config; +using AttrValue = Config::AttrValue; +using ASection = Config::ASection; + +namespace +{ + std::string ShowRules(const std::vector>& attributes) + { + std::string result; + for (const auto& at : attributes) + { + if (!result.empty()) + result += ", "; + + if (at.second.type == AttrValue::Type::PARAM_NAME) + result.append(at.first + " = " + at.second.value); + else + result.append(at.first + " = '" + at.second.value + "'"); + } + return result; + } +} + +std::vector> Config::ParseRules(const std::string& value, const std::string& paramName) +{ + using tokenizer = boost::tokenizer>; + const boost::char_separator sep(","); + + const tokenizer tokens(value, sep); + + std::vector> res; + for (const auto& token : tokens) + { + const boost::char_separator sp(" ="); + const tokenizer tok(token, sp); + + std::vector keyValue; + for (const auto& t : tok) + keyValue.push_back(t); + + if (keyValue.size() != 2) + { + m_logger("The '%s' attribute specification has an incorrect format: '%s'.", paramName.c_str(), token.c_str()); + printfd(__FILE__, "The '%s' attribute specification has an incorrect format: '%s'.", paramName.c_str(), token.c_str()); + return {}; + } + + auto type = AttrValue::Type::PARAM_NAME; + std::string valueName = keyValue[1]; + if (valueName.front() == '\'' && valueName.back() == '\'') + { + type = AttrValue::Type::VALUE; + valueName.erase(0, 1); + valueName.erase(valueName.length() - 1, 1); + } + else if ((valueName.front() == '\'' && valueName.back() != '\'') || (valueName.front() != '\'' && valueName.back() == '\'')) + { + m_logger("Error ParseRules: '%s' attribute parameter value is invalid.\n", paramName.c_str()); + printfd(__FILE__, "Error ParseRules: '%s' attribute parameter value is invalid.\n", paramName.c_str()); + return {}; + } + res.emplace_back(keyValue[0], AttrValue{valueName, type}); + } + return res; +} + +ASection Config::parseASection(const std::vector& conf) +{ + ASection res; + const auto mit = std::find(conf.begin(), conf.end(), ParamValue("match", {})); + if (mit != conf.end()) + res.match = ParseRules(mit->value[0], mit->param); + + const auto sit = std::find(conf.begin(), conf.end(), ParamValue("send", {})); + if (sit != conf.end()) + res.send = ParseRules(sit->value[0], sit->param); + + return res; +} + +Config::Config() + : m_port(1812), + m_dictionaries("/usr/share/freeradius/dictionary"), + m_logger(PluginLogger::get("radius")) +{} + +int Config::ParseSettings(const ModuleSettings & s) +{ + ParamValue pv; + int p; + + pv.param = "Port"; + auto pvi = std::find(s.moduleParams.begin(), s.moduleParams.end(), pv); + if (pvi != s.moduleParams.end() && !pvi->value.empty()) + { + if (ParseIntInRange(pvi->value[0], 2, 65535, &p) != 0) + { + m_errorStr = "Cannot parse parameter \'Port\': " + m_errorStr; + printfd(__FILE__, "Cannot parse parameter 'Port'\n"); + return -1; + } + m_port = static_cast(p); + } + + pv.param = "Secret"; + pvi = std::find(s.moduleParams.begin(), s.moduleParams.end(), pv); + if (pvi == s.moduleParams.end() || pvi->value.empty()) + { + m_errorStr = "Parameter \'Secret\' not found."; + printfd(__FILE__, "Parameter 'Secret' not found\n"); + m_secret = ""; + } + else + m_secret = pvi->value[0]; + + pv.param = "Dictionaries"; + pvi = std::find(s.moduleParams.begin(), s.moduleParams.end(), pv); + if (pvi != s.moduleParams.end() && !pvi->value.empty()) + m_dictionaries = pvi->value[0]; + + const auto authIt = std::find(s.moduleParams.begin(), s.moduleParams.end(), ParamValue("auth", {})); + if (authIt != s.moduleParams.end()) + m_auth = parseASection(authIt->sections); + + const auto autzIt = std::find(s.moduleParams.begin(), s.moduleParams.end(), ParamValue("autz", {})); + if (autzIt != s.moduleParams.end()) + m_autz = parseASection(autzIt->sections); + + printfd(__FILE__, " auth.match = \"%s\"\n", ShowRules(m_auth.match).c_str()); + printfd(__FILE__, " auth.send = \"%s\"\n", ShowRules(m_auth.send).c_str()); + printfd(__FILE__, " autz.match = \"%s\"\n", ShowRules(m_autz.match).c_str()); + printfd(__FILE__, " autz.send = \"%s\"\n", ShowRules(m_autz.send).c_str()); + + return 0; +} + diff --git a/projects/stargazer/plugins/other/radius/config.h b/projects/stargazer/plugins/other/radius/config.h new file mode 100644 index 00000000..74e483a5 --- /dev/null +++ b/projects/stargazer/plugins/other/radius/config.h @@ -0,0 +1,60 @@ +#pragma once + +#include "stg/module_settings.h" +#include "stg/subscriptions.h" +#include "stg/logger.h" + +#include +#include //uint8_t, uint32_t + +namespace STG +{ + struct Settings; + + class Config + { + public: + Config(); + + struct AttrValue + { + enum class Type + { + PARAM_NAME, + VALUE + }; + std::string value; + Type type; + }; + + struct ASection + { + using Pairs = std::vector>; + Pairs match; + Pairs send; + }; + + const std::string& GetStrError() const { return m_errorStr; } + int ParseSettings(const ModuleSettings& s); + + uint16_t GetPort() const { return m_port; } + const std::string& GetDictionaries() const { return m_dictionaries; } + const std::string& GetSecret() const { return m_secret; } + const ASection& getAuth() const { return m_auth; } + const ASection& getAutz() const { return m_autz; } + + private: + std::vector> ParseRules(const std::string& value, const std::string& paramName); + ASection parseASection(const std::vector& conf); + + std::string m_errorStr; + uint16_t m_port; + std::string m_dictionaries; + std::string m_secret; + + ASection m_auth; + ASection m_autz; + + PluginLogger m_logger; + }; +} diff --git a/projects/stargazer/plugins/other/radius/radius.cpp b/projects/stargazer/plugins/other/radius/radius.cpp index 68291dcf..e3c8ad4e 100644 --- a/projects/stargazer/plugins/other/radius/radius.cpp +++ b/projects/stargazer/plugins/other/radius/radius.cpp @@ -3,16 +3,9 @@ #include "stg/common.h" #include -#include #include -#include -#include -#include using STG::RADIUS; -using STG::RAD_SETTINGS; -using AttrValue = RAD_SETTINGS::AttrValue; -using ASection = RAD_SETTINGS::ASection; extern "C" STG::Plugin* GetPlugin() { @@ -20,138 +13,6 @@ extern "C" STG::Plugin* GetPlugin() return &plugin; } -namespace -{ - std::string ShowRules(const std::vector>& attributes) - { - std::string result; - for (const auto& at : attributes) - { - if (!result.empty()) - result += ", "; - - if (at.second.type == AttrValue::Type::PARAM_NAME) - result.append(at.first + " = " + at.second.value); - else - result.append(at.first + " = '" + at.second.value + "'"); - } - return result; - } -} - -std::vector> RAD_SETTINGS::ParseRules(const std::string& value, const std::string& paramName) -{ - using tokenizer = boost::tokenizer>; - const boost::char_separator sep(","); - - const tokenizer tokens(value, sep); - - std::vector> res; - for (const auto& token : tokens) - { - const boost::char_separator sp(" ="); - const tokenizer tok(token, sp); - - std::vector keyValue; - for (const auto& t : tok) - keyValue.push_back(t); - - if (keyValue.size() != 2) - { - m_logger("The '%s' attribute specification has an incorrect format: '%s'.", paramName.c_str(), token.c_str()); - printfd(__FILE__, "The '%s' attribute specification has an incorrect format: '%s'.", paramName.c_str(), token.c_str()); - return {}; - } - - auto type = AttrValue::Type::PARAM_NAME; - std::string valueName = keyValue[1]; - if (valueName.front() == '\'' && valueName.back() == '\'') - { - type = AttrValue::Type::VALUE; - valueName.erase(0, 1); - valueName.erase(valueName.length() - 1, 1); - } - else if ((valueName.front() == '\'' && valueName.back() != '\'') || (valueName.front() != '\'' && valueName.back() == '\'')) - { - m_logger("Error ParseRules: '%s' attribute parameter value is invalid.\n", paramName.c_str()); - printfd(__FILE__, "Error ParseRules: '%s' attribute parameter value is invalid.\n", paramName.c_str()); - return {}; - } - res.emplace_back(keyValue[0], AttrValue{valueName, type}); - } - return res; -} - -ASection RAD_SETTINGS::parseASection(const std::vector& conf) -{ - ASection res; - const auto mit = std::find(conf.begin(), conf.end(), ParamValue("match", {})); - if (mit != conf.end()) - res.match = ParseRules(mit->value[0], mit->param); - - const auto sit = std::find(conf.begin(), conf.end(), ParamValue("send", {})); - if (sit != conf.end()) - res.send = ParseRules(sit->value[0], sit->param); - - return res; -} - -RAD_SETTINGS::RAD_SETTINGS() - : m_port(1812), - m_dictionaries("/usr/share/freeradius/dictionary"), - m_logger(PluginLogger::get("radius")) -{} - -int RAD_SETTINGS::ParseSettings(const ModuleSettings & s) -{ - ParamValue pv; - int p; - - pv.param = "Port"; - auto pvi = std::find(s.moduleParams.begin(), s.moduleParams.end(), pv); - if (pvi != s.moduleParams.end() && !pvi->value.empty()) - { - if (ParseIntInRange(pvi->value[0], 2, 65535, &p) != 0) - { - m_errorStr = "Cannot parse parameter \'Port\': " + m_errorStr; - printfd(__FILE__, "Cannot parse parameter 'Port'\n"); - return -1; - } - m_port = static_cast(p); - } - - pv.param = "Secret"; - pvi = std::find(s.moduleParams.begin(), s.moduleParams.end(), pv); - if (pvi == s.moduleParams.end() || pvi->value.empty()) - { - m_errorStr = "Parameter \'Secret\' not found."; - printfd(__FILE__, "Parameter 'Secret' not found\n"); - m_secret = ""; - } - else - m_secret = pvi->value[0]; - - pv.param = "Dictionaries"; - pvi = std::find(s.moduleParams.begin(), s.moduleParams.end(), pv); - if (pvi != s.moduleParams.end() && !pvi->value.empty()) - m_dictionaries = pvi->value[0]; - - const auto authIt = std::find(s.moduleParams.begin(), s.moduleParams.end(), ParamValue("auth", {})); - if (authIt != s.moduleParams.end()) - m_auth = parseASection(authIt->sections); - - const auto autzIt = std::find(s.moduleParams.begin(), s.moduleParams.end(), ParamValue("autz", {})); - if (autzIt != s.moduleParams.end()) - m_autz = parseASection(autzIt->sections); - - printfd(__FILE__, " auth.match = \"%s\"\n", ShowRules(m_auth.match).c_str()); - printfd(__FILE__, " auth.send = \"%s\"\n", ShowRules(m_auth.send).c_str()); - printfd(__FILE__, " autz.match = \"%s\"\n", ShowRules(m_autz.match).c_str()); - printfd(__FILE__, " autz.send = \"%s\"\n", ShowRules(m_autz.send).c_str()); - - return 0; -} - RADIUS::RADIUS() : m_running(false), m_users(NULL), @@ -161,9 +22,9 @@ RADIUS::RADIUS() int RADIUS::ParseSettings() { - auto ret = m_radSettings.ParseSettings(m_settings); + auto ret = m_config.ParseSettings(m_settings); if (ret != 0) - m_errorStr = m_radSettings.GetStrError(); + m_errorStr = m_config.GetStrError(); return ret; } @@ -212,7 +73,7 @@ int RADIUS::Run(std::stop_token token) try { if (!m_server) - m_server = std::make_unique(m_ioContext, m_radSettings.GetSecret(), m_radSettings.GetPort(), m_radSettings.GetDictionaries(), std::move(token), m_logger, m_users); + m_server = std::make_unique(m_ioContext, m_config.GetSecret(), m_config.GetPort(), m_config.GetDictionaries(), std::move(token), m_logger, m_users, m_config); m_ioContext.run(); } catch (const std::exception& e) diff --git a/projects/stargazer/plugins/other/radius/radius.h b/projects/stargazer/plugins/other/radius/radius.h index 80e032a8..87136d83 100644 --- a/projects/stargazer/plugins/other/radius/radius.h +++ b/projects/stargazer/plugins/other/radius/radius.h @@ -2,6 +2,7 @@ #include "stg/auth.h" #include "stg/plugin.h" +#include "config.h" #include "stg/module_settings.h" #include "stg/subscriptions.h" #include "stg/logger.h" @@ -16,58 +17,8 @@ namespace STG { - struct Settings; - class Users; - class RAD_SETTINGS - { - public: - RAD_SETTINGS(); - virtual ~RAD_SETTINGS() {} - - struct AttrValue - { - enum class Type - { - PARAM_NAME, - VALUE - }; - std::string value; - Type type; - }; - - struct ASection - { - using Pairs = std::vector>; - Pairs match; - Pairs send; - }; - - const std::string& GetStrError() const { return m_errorStr; } - int ParseSettings(const ModuleSettings& s); - - uint16_t GetPort() const { return m_port; } - const std::string& GetDictionaries() const { return m_dictionaries; } - const std::string& GetSecret() const { return m_secret; } - const ASection& getAuth() const { return m_auth; } - const ASection& getAutz() const { return m_autz; } - - private: - std::vector> ParseRules(const std::string& value, const std::string& paramName); - ASection parseASection(const std::vector& conf); - - std::string m_errorStr; - uint16_t m_port; - std::string m_dictionaries; - std::string m_secret; - - ASection m_auth; - ASection m_autz; - - PluginLogger m_logger; - }; - class RADIUS : public Auth { public: @@ -100,7 +51,7 @@ namespace STG int Run(std::stop_token token); mutable std::string m_errorStr; - RAD_SETTINGS m_radSettings; + Config m_config; ModuleSettings m_settings; bool m_running; diff --git a/projects/stargazer/plugins/other/radius/server.cpp b/projects/stargazer/plugins/other/radius/server.cpp index 9e6766d5..7df44a8a 100644 --- a/projects/stargazer/plugins/other/radius/server.cpp +++ b/projects/stargazer/plugins/other/radius/server.cpp @@ -1,20 +1,27 @@ #include "server.h" +#include "radproto/attribute.h" #include "radproto/packet_codes.h" -#include "radproto/attribute_types.h" +#include "radproto/attribute_codes.h" #include "stg/user.h" #include "stg/users.h" #include "stg/common.h" +#include +#include +#include +#include // для std::replace #include #include #include //uint8_t, uint32_t using STG::Server; +using STG::User; using boost::system::error_code; -Server::Server(boost::asio::io_context& io_context, const std::string& secret, uint16_t port, const std::string& filePath, std::stop_token token, PluginLogger& logger, Users* users) +Server::Server(boost::asio::io_context& io_context, const std::string& secret, uint16_t port, const std::string& filePath, std::stop_token token, PluginLogger& logger, Users* users, const Config& config) : m_radius(io_context, secret, port), m_dictionaries(filePath), m_users(users), + m_config(config), m_token(std::move(token)), m_logger(logger) { @@ -37,27 +44,44 @@ void Server::startReceive() m_radius.asyncReceive([this](const auto& error, const auto& packet, const boost::asio::ip::udp::endpoint& source){ handleReceive(error, packet, source); }); } -RadProto::Packet Server::makeResponse(const RadProto::Packet& request) +std::vector Server::makeAttributes(const User* user) { std::vector attributes; - attributes.push_back(new RadProto::String(m_dictionaries.attributeCode("User-Name"), "test")); - attributes.push_back(new RadProto::Integer(m_dictionaries.attributeCode("NAS-Port"), 20)); - std::array address {127, 104, 22, 17}; - attributes.push_back(new RadProto::IpAddress(m_dictionaries.attributeCode("NAS-IP-Address"), address)); - std::vector bytes {'1', '2', '3', 'a', 'b', 'c'}; - attributes.push_back(new RadProto::Bytes(m_dictionaries.attributeCode("Callback-Number"), bytes)); - std::vector chapPassword {'1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g' }; - attributes.push_back(new RadProto::ChapPassword(m_dictionaries.attributeCode("CHAP-Password"), 1, chapPassword)); - - std::vector vendorSpecific; - std::vector vendorValue {0, 0, 0, 3}; - vendorSpecific.push_back(RadProto::VendorSpecific(m_dictionaries.vendorCode("Dlink"), m_dictionaries.vendorAttributeCode("Dlink", "Dlink-User-Level"), vendorValue)); - - if (request.type() != RadProto::ACCESS_REQUEST) + std::string attrName; + std::string attrValue; + std::string attrType; + uint8_t attrCode; + + for (const auto& at : m_config.getAuth().send) + { + if (at.second.type == Config::AttrValue::Type::PARAM_NAME) + attrValue = user->GetParamValue(at.second.value); + else + attrValue = at.second.value; + + attrName = at.first; + attrCode = m_dictionaries.attributeCode(attrName); + attrType = m_dictionaries.attributeType(attrCode); + + if ((attrType == "integer") && (m_dictionaries.attributeValueFindByName(attrName, attrValue))) + attributes.push_back(RadProto::Attribute::make(attrCode, attrType, std::to_string(m_dictionaries.attributeValueCode(attrName, attrValue)))); + else + attributes.push_back(RadProto::Attribute::make(attrCode, attrType, attrValue)); + } + return attributes; +} + +RadProto::Packet Server::makeResponse(const RadProto::Packet& request) +{ + const User* user; + + user = findUser(request); + + if (request.code() != RadProto::ACCESS_REQUEST) return RadProto::Packet(RadProto::ACCESS_REJECT, request.id(), request.auth(), {}, {}); - if (findUser(request)) - return RadProto::Packet(RadProto::ACCESS_ACCEPT, request.id(), request.auth(), attributes, vendorSpecific); + if (user != nullptr) + return RadProto::Packet(RadProto::ACCESS_ACCEPT, request.id(), request.auth(), makeAttributes(user), {}); printfd(__FILE__, "Error findUser\n"); return RadProto::Packet(RadProto::ACCESS_REJECT, request.id(), request.auth(), {}, {}); @@ -97,16 +121,16 @@ void Server::handleReceive(const error_code& error, const std::optionaltype() == RadProto::USER_NAME) + if (attribute->code() == RadProto::USER_NAME) login = attribute->toString(); - if (attribute->type() == RadProto::USER_PASSWORD) + if (attribute->code() == RadProto::USER_PASSWORD) password = attribute->toString(); } @@ -115,7 +139,7 @@ bool Server::findUser(const RadProto::Packet& packet) { m_logger("User '%s' not found.", login.c_str()); printfd(__FILE__, "User '%s' NOT found!\n", login.c_str()); - return false; + return nullptr; } printfd(__FILE__, "User '%s' FOUND!\n", user->GetLogin().c_str()); @@ -124,7 +148,7 @@ bool Server::findUser(const RadProto::Packet& packet) { m_logger("User's password is incorrect. %s", password.c_str()); printfd(__FILE__, "User's password is incorrect.\n", password.c_str()); - return false; + return nullptr; } - return true; + return user; } diff --git a/projects/stargazer/plugins/other/radius/server.h b/projects/stargazer/plugins/other/radius/server.h index d4efef98..1523179e 100644 --- a/projects/stargazer/plugins/other/radius/server.h +++ b/projects/stargazer/plugins/other/radius/server.h @@ -2,6 +2,7 @@ #include "radproto/socket.h" #include "radproto/packet.h" +#include "config.h" #include "radproto/dictionaries.h" #include "stg/logger.h" #include @@ -12,15 +13,17 @@ namespace STG { class Users; + class User; class Server { public: - Server(boost::asio::io_context& io_context, const std::string& secret, uint16_t port, const std::string& filePath, std::stop_token token, PluginLogger& logger, Users* users); + Server(boost::asio::io_context& io_context, const std::string& secret, uint16_t port, const std::string& filePath, std::stop_token token, PluginLogger& logger, Users* users, const Config& config); void stop(); private: + std::vector makeAttributes(const User* user); RadProto::Packet makeResponse(const RadProto::Packet& request); - bool findUser(const RadProto::Packet& packet); + const User* findUser(const RadProto::Packet& packet); void handleReceive(const boost::system::error_code& error, const std::optional& packet, const boost::asio::ip::udp::endpoint& source); void handleSend(const boost::system::error_code& ec); void start(); @@ -29,6 +32,7 @@ namespace STG RadProto::Socket m_radius; RadProto::Dictionaries m_dictionaries; Users* m_users; + const Config& m_config; std::stop_token m_token; PluginLogger& m_logger;