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 "" )
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 )
--- /dev/null
+#include "config.h"
+#include "radproto/error.h"
+#include "stg/common.h"
+#include <boost/tokenizer.hpp>
+#include <boost/algorithm/string.hpp>
+
+#include <vector>
+#include <utility>
+#include <iterator>
+#include <algorithm>
+#include <iostream>
+
+using STG::Config;
+using AttrValue = Config::AttrValue;
+using ASection = Config::ASection;
+
+namespace
+{
+ std::string ShowRules(const std::vector<std::pair<std::string, AttrValue>>& 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<std::pair<std::string, AttrValue>> Config::ParseRules(const std::string& value, const std::string& paramName)
+{
+ using tokenizer = boost::tokenizer<boost::char_separator<char>>;
+ const boost::char_separator<char> sep(",");
+
+ const tokenizer tokens(value, sep);
+
+ std::vector<std::pair<std::string, AttrValue>> res;
+
+ for (const auto& token : tokens)
+ {
+ std::vector<std::string> 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<ParamValue>& 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<uint16_t>(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;
+}
+
--- /dev/null
+#pragma once
+
+#include "stg/module_settings.h"
+#include "stg/subscriptions.h"
+#include "stg/logger.h"
+
+#include <string>
+#include <cstdint> //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<std::pair<std::string, AttrValue>>;
+ 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<std::pair<std::string, AttrValue>> ParseRules(const std::string& value, const std::string& paramName);
+ ASection parseASection(const std::vector<ParamValue>& 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;
+ };
+}
#include "stg/common.h"
#include <boost/tokenizer.hpp>
-#include <vector>
#include <utility>
-#include <iterator>
-#include <algorithm>
-#include <iostream>
using STG::RADIUS;
-using STG::RAD_SETTINGS;
-using AttrValue = RAD_SETTINGS::AttrValue;
-using ASection = RAD_SETTINGS::ASection;
extern "C" STG::Plugin* GetPlugin()
{
return &plugin;
}
-namespace
-{
- std::string ShowRules(const std::vector<std::pair<std::string, AttrValue>>& 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<std::pair<std::string, AttrValue>> RAD_SETTINGS::ParseRules(const std::string& value, const std::string& paramName)
-{
- using tokenizer = boost::tokenizer<boost::char_separator<char>>;
- const boost::char_separator<char> sep(",");
-
- const tokenizer tokens(value, sep);
-
- std::vector<std::pair<std::string, AttrValue>> res;
- for (const auto& token : tokens)
- {
- const boost::char_separator<char> sp(" =");
- const tokenizer tok(token, sp);
-
- std::vector<std::string> 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<ParamValue>& 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<uint16_t>(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),
+ 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;
}
try
{
if (!m_server)
- m_server = std::make_unique<Server>(m_ioContext, m_radSettings.GetSecret(), m_radSettings.GetPort(), m_radSettings.GetDictionaries(), std::move(token), m_logger, m_users);
+ m_server = std::make_unique<Server>(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)
#include "stg/auth.h"
#include "stg/plugin.h"
+#include "config.h"
#include "stg/module_settings.h"
#include "stg/subscriptions.h"
#include "stg/logger.h"
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<std::pair<std::string, AttrValue>>;
- 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<std::pair<std::string, AttrValue>> ParseRules(const std::string& value, const std::string& paramName);
- ASection parseASection(const std::vector<ParamValue>& 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:
int Stop() override;
int Reload(const ModuleSettings& /*ms*/) override { return 0; }
bool IsRunning() override;
- void SetRunning(bool val);
const std::string& GetStrError() const override { return m_errorStr; }
std::string GetVersion() const override;
std::mutex m_mutex;
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;
#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 <vector>
+#include <string>
+#include <sstream>
#include <cstring>
#include <functional>
#include <cstdint> //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)
{
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<RadProto::Attribute*> Server::makeAttributes(const User* user)
{
std::vector<RadProto::Attribute*> 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<uint8_t, 4> address {127, 104, 22, 17};
- attributes.push_back(new RadProto::IpAddress(m_dictionaries.attributeCode("NAS-IP-Address"), address));
- std::vector<uint8_t> bytes {'1', '2', '3', 'a', 'b', 'c'};
- attributes.push_back(new RadProto::Bytes(m_dictionaries.attributeCode("Callback-Number"), bytes));
- std::vector<uint8_t> 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<RadProto::VendorSpecific> vendorSpecific;
- std::vector<uint8_t> 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)
+
+ 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(), {}, {});
- if (findUser(request))
- return RadProto::Packet(RadProto::ACCESS_ACCEPT, request.id(), request.auth(), attributes, vendorSpecific);
+ 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(), {}, {});
{
m_logger("Error asyncReceive: %s", error.message().c_str());
printfd(__FILE__, "Error asyncReceive: '%s'\n", error.message().c_str());
+ return;
}
if (packet == std::nullopt)
m_radius.asyncSend(makeResponse(*packet), source, [this](const auto& ec){ handleSend(ec); });
}
-bool Server::findUser(const RadProto::Packet& packet)
+const User* Server::findUser(const RadProto::Packet& packet)
{
std::string login;
std::string password;
for (const auto& attribute : packet.attributes())
{
- if (attribute->type() == 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();
}
{
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());
if (password != user->GetProperties().password.Get())
{
- m_logger("User's password is incorrect. %s", password.c_str());
- printfd(__FILE__, "User's password is incorrect.\n", password.c_str());
- return false;
+ m_logger("User's password is incorrect.");
+ printfd(__FILE__, "User's password is incorrect.\n");
+ return nullptr;
}
- return true;
+ return user;
}
#include "radproto/socket.h"
#include "radproto/packet.h"
+#include "config.h"
#include "radproto/dictionaries.h"
#include "stg/logger.h"
#include <boost/asio.hpp>
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<RadProto::Attribute*> 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<RadProto::Packet>& packet, const boost::asio::ip::udp::endpoint& source);
void handleSend(const boost::system::error_code& ec);
void start();
RadProto::Socket m_radius;
RadProto::Dictionaries m_dictionaries;
Users* m_users;
+ const Config& m_config;
std::stop_token m_token;
PluginLogger& m_logger;