From: Maksym Mamontov Date: Fri, 27 Mar 2026 20:31:34 +0000 (+0200) Subject: Merge remote-tracking branch 'github/master' X-Git-Url: https://git.stg.codes/stg.git/commitdiff_plain/c57f51df2406db09088196b6adf78e373c97591f?hp=0843b701fa2c856cfe6ab08aecff3e3c05c48d17 Merge remote-tracking branch 'github/master' --- diff --git a/projects/stargazer/inst/linux/etc/stargazer/conf-available.d/mod_radius.conf b/projects/stargazer/inst/linux/etc/stargazer/conf-available.d/mod_radius.conf index d4cf6c4c..7373af21 100644 --- a/projects/stargazer/inst/linux/etc/stargazer/conf-available.d/mod_radius.conf +++ b/projects/stargazer/inst/linux/etc/stargazer/conf-available.d/mod_radius.conf @@ -17,4 +17,13 @@ # Default: 1812 # Port = 1812 + + send = "Framed-IP-Address = currip, Service-Type = \'Framed-User\'" + match = "Calling-Station-Id = userdata0" + + + + send = "Framed-IP-Address = currip, Service-Type = \'Login-User\'" + match = "User-Name = login, User-Password = password" + diff --git a/projects/stargazer/plugins/CMakeLists.txt b/projects/stargazer/plugins/CMakeLists.txt index c8dd8f50..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.2 + 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..108b61e6 --- /dev/null +++ b/projects/stargazer/plugins/other/radius/config.cpp @@ -0,0 +1,146 @@ +#include "config.h" +#include "radproto/error.h" +#include "stg/common.h" +#include +#include + +#include +#include +#include +#include +#include + +using STG::Config; +using AttrValue = Config::AttrValue; +using ASection = Config::ASection; + +namespace +{ + std::string ShowRules(const std::vector>& rules) + { + std::string result; + for (const auto& at : rules) + { + 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) + { + std::vector keyValue; + + split(keyValue, boost::algorithm::trim_copy_if(token, boost::is_any_of(" \t")), boost::is_any_of(" ="), boost::token_compress_on); + + 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"); + return -1; + } + 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 8e5b37a8..11fbcf7b 100644 --- a/projects/stargazer/plugins/other/radius/radius.cpp +++ b/projects/stargazer/plugins/other/radius/radius.cpp @@ -1,12 +1,11 @@ #include "radius.h" #include "radproto/error.h" #include "stg/common.h" +#include -#include -#include +#include using STG::RADIUS; -using STG::RAD_SETTINGS; extern "C" STG::Plugin* GetPlugin() { @@ -14,60 +13,18 @@ extern "C" STG::Plugin* GetPlugin() return &plugin; } -RAD_SETTINGS::RAD_SETTINGS() - : m_port(1812), - m_dictionaries("/usr/share/freeradius/dictionary") -{} - -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]; - return 0; -} - RADIUS::RADIUS() : m_running(false), + m_users(nullptr), m_logger(PluginLogger::get("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; } @@ -116,8 +73,8 @@ int RADIUS::Run(std::stop_token token) try { if (!m_server) - m_server = std::make_unique(m_ioService, m_radSettings.GetSecret(), m_radSettings.GetPort(), m_radSettings.GetDictionaries(), std::move(token), m_logger); - m_ioService.run(); + 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 6b742de5..59cbf0d5 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,26 +17,7 @@ namespace STG { - struct Settings; - - class RAD_SETTINGS - { - public: - RAD_SETTINGS(); - virtual ~RAD_SETTINGS() {} - 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; } - - private: - std::string m_errorStr; - uint16_t m_port; - std::string m_dictionaries; - std::string m_secret; - }; + class Users; class RADIUS : public Auth { @@ -44,37 +26,38 @@ namespace STG RADIUS(const RADIUS&) = delete; RADIUS& operator=(const RADIUS&) = delete; - void SetSettings(const ModuleSettings & s) override { m_settings = s; } + void SetUsers(Users* u) override { m_users = u; } + void SetSettings(const ModuleSettings& s) override { m_settings = s; } int ParseSettings() override; int Start() override; int Stop() override; - int Reload(const ModuleSettings & /*ms*/) override { return 0; } + int Reload(const ModuleSettings& /*ms*/) override { return 0; } bool IsRunning() override; - void SetRunning(bool val); - const std::string & GetStrError() const override { return m_errorStr; } + const std::string& GetStrError() const override { return m_errorStr; } std::string GetVersion() const override; uint16_t GetStartPosition() const override { return 0; } uint16_t GetStopPosition() const override { return 0; } - int SendMessage(const Message & msg, uint32_t ip) const override { return 0; } + int SendMessage(const Message& /*msg*/, uint32_t /*ip*/) const override { return 0; } private: std::mutex m_mutex; - boost::asio::io_service m_ioService; + boost::asio::io_context m_ioContext; + void SetRunning(bool val); int Run(std::stop_token token); mutable std::string m_errorStr; - RAD_SETTINGS m_radSettings; + Config m_config; ModuleSettings m_settings; bool m_running; std::jthread m_thread; - + Users* m_users; PluginLogger m_logger; std::unique_ptr m_server; diff --git a/projects/stargazer/plugins/other/radius/server.cpp b/projects/stargazer/plugins/other/radius/server.cpp index 850847fa..11aa9a79 100644 --- a/projects/stargazer/plugins/other/radius/server.cpp +++ b/projects/stargazer/plugins/other/radius/server.cpp @@ -1,15 +1,26 @@ #include "server.h" +#include "radproto/attribute.h" #include "radproto/packet_codes.h" +#include "radproto/attribute_codes.h" +#include "stg/user.h" +#include "stg/users.h" #include "stg/common.h" +#include +#include +#include #include #include +#include //uint8_t, uint32_t using STG::Server; +using STG::User; using boost::system::error_code; -Server::Server(boost::asio::io_service& io_service, const std::string& secret, uint16_t port, const std::string& filePath, std::stop_token token, PluginLogger& logger) - : m_radius(io_service, secret, port), +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) { @@ -32,26 +43,45 @@ 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) - return RadProto::Packet(RadProto::ACCESS_ACCEPT, request.id(), request.auth(), attributes, vendorSpecific); - - return RadProto::Packet(RadProto::ACCESS_REJECT, request.id(), request.auth(), attributes, vendorSpecific); + + for (const auto& at : m_config.getAuth().send) + { + std::string attrValue; + + if (at.second.type == Config::AttrValue::Type::PARAM_NAME) + attrValue = user->GetParamValue(at.second.value); + else + attrValue = at.second.value; + + const auto attrName = at.first; + const auto attrCode = m_dictionaries.attributeCode(attrName); + const auto 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) +{ + if (request.code() != RadProto::ACCESS_REQUEST) + return RadProto::Packet(RadProto::ACCESS_REJECT, request.id(), request.auth(), {}, {}); + + const User* user; + + user = findUser(request); + + 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(), {}, {}); } void Server::handleSend(const error_code& ec) @@ -76,6 +106,7 @@ void Server::handleReceive(const error_code& error, const std::optionalcode() == RadProto::USER_NAME) + login = attribute->toString(); + + if (attribute->code() == RadProto::USER_PASSWORD) + password = attribute->toString(); + } + + User* user = nullptr; + if (m_users->FindByName(login, &user)) + { + m_logger("User '%s' not found.", login.c_str()); + printfd(__FILE__, "User '%s' NOT found!\n", login.c_str()); + return nullptr; + } + + printfd(__FILE__, "User '%s' FOUND!\n", user->GetLogin().c_str()); + + if (password != user->GetProperties().password.Get()) + { + m_logger("User's password is incorrect."); + printfd(__FILE__, "User's password is incorrect.\n"); + return nullptr; + } + return user; +} diff --git a/projects/stargazer/plugins/other/radius/server.h b/projects/stargazer/plugins/other/radius/server.h index e33a23b3..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 @@ -11,13 +12,18 @@ namespace STG { + class Users; + class User; + class Server { public: - Server(boost::asio::io_service& io_service, const std::string& secret, uint16_t port, const std::string& filePath, std::stop_token token, PluginLogger& logger); + 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); + 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(); @@ -25,6 +31,8 @@ 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;