X-Git-Url: https://git.stg.codes/stg.git/blobdiff_plain/1538d6276533140505fddb71c99a0bafe6ca9182..21ba4dfad49d2d489a9399d36d078eab8c44e0d6:/projects/stargazer/plugins/other/radius/radius.cpp diff --git a/projects/stargazer/plugins/other/radius/radius.cpp b/projects/stargazer/plugins/other/radius/radius.cpp index fe989b28..68291dcf 100644 --- a/projects/stargazer/plugins/other/radius/radius.cpp +++ b/projects/stargazer/plugins/other/radius/radius.cpp @@ -1,186 +1,227 @@ -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -/* - * Author : Maxim Mamontov - */ - #include "radius.h" - -#include "stg/store.h" -#include "stg/users.h" -#include "stg/plugin_creator.h" - -#include -#include -#include +#include "radproto/error.h" +#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() +{ + static RADIUS plugin; + return &plugin; +} namespace { + std::string ShowRules(const std::vector>& attributes) + { + std::string result; + for (const auto& at : attributes) + { + if (!result.empty()) + result += ", "; -PLUGIN_CREATOR creator; - + 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; + } } -extern "C" PLUGIN * GetPlugin() +std::vector> RAD_SETTINGS::ParseRules(const std::string& value, const std::string& paramName) { - return creator.GetPlugin(); -} + using tokenizer = boost::tokenizer>; + const boost::char_separator sep(","); -RADIUS::RADIUS() - : m_running(false), - m_stopped(true), - m_users(NULL), - m_store(NULL), - m_logger(GetPluginLogger(GetStgLogger(), "radius")) -{ -} + const tokenizer tokens(value, sep); -int RADIUS::ParseSettings() -{ - try { - m_config = STG::Config(m_settings); - return 0; - } catch (const std::runtime_error& ex) { - m_logger("Failed to parse settings. %s", ex.what()); - return -1; + 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; } -int RADIUS::Start() +ASection RAD_SETTINGS::parseASection(const std::vector& conf) { - if (m_running) - return 0; + 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); - int res = pthread_create(&m_thread, NULL, run, this); - if (res == 0) - return 0; + const auto sit = std::find(conf.begin(), conf.end(), ParamValue("send", {})); + if (sit != conf.end()) + res.send = ParseRules(sit->value[0], sit->param); - m_error = strerror(res); - m_logger("Failed to create thread: '" + m_error + "'."); - return -1; + return res; } -int RADIUS::Stop() -{ - if (m_stopped) - return 0; +RAD_SETTINGS::RAD_SETTINGS() + : m_port(1812), + m_dictionaries("/usr/share/freeradius/dictionary"), + m_logger(PluginLogger::get("radius")) +{} - m_running = false; +int RAD_SETTINGS::ParseSettings(const ModuleSettings & s) +{ + ParamValue pv; + int p; - for (size_t i = 0; i < 25 && !m_stopped; i++) { - struct timespec ts = {0, 200000000}; - nanosleep(&ts, NULL); + 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); } - if (m_stopped) { - pthread_join(m_thread, NULL); - return 0; + 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]; - m_error = "Failed to stop thread."; - m_logger(m_error); - return -1; -} -//----------------------------------------------------------------------------- -void* RADIUS::run(void* d) -{ - sigset_t signalSet; - sigfillset(&signalSet); - pthread_sigmask(SIG_BLOCK, &signalSet, NULL); + 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); - static_cast(d)->runImpl(); + const auto autzIt = std::find(s.moduleParams.begin(), s.moduleParams.end(), ParamValue("autz", {})); + if (autzIt != s.moduleParams.end()) + m_autz = parseASection(autzIt->sections); - return NULL; + 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; } -void RADIUS::runImpl() +RADIUS::RADIUS() + : m_running(false), + m_users(NULL), + m_logger(PluginLogger::get("radius")) { - m_running = true; +} - while (m_running) { - fd_set fds; +int RADIUS::ParseSettings() +{ + auto ret = m_radSettings.ParseSettings(m_settings); + if (ret != 0) + m_errorStr = m_radSettings.GetStrError(); - buildFDSet(fds); + return ret; +} - struct timeval tv; - tv.tv_sec = 0; - tv.tv_usec = 500000; +std::string RADIUS::GetVersion() const +{ + return "Radius v.1.0"; +} - int res = select(maxFD() + 1, &fds, NULL, NULL, &tv); - if (res < 0) - { - m_error = std::string("'select' is failed: '") + strerror(errno) + "'."; - m_logger(m_error); - break; - } +int RADIUS::Start() +{ + m_thread = std::jthread([this](auto token){ Run(std::move(token)); }); + return 0; +} - if (!m_running) - break; +int RADIUS::Stop() +{ + if (!m_thread.joinable()) + return 0; - if (res > 0) - handleEvents(fds); + m_thread.request_stop(); - cleanupConns(); - } + if (m_server) + m_server->stop(); - m_stopped = true; + m_thread.join(); + return 0; } -int RADIUS::maxFD() const +bool RADIUS::IsRunning() { - int maxFD = m_listenSocket; - std::deque::const_iterator it; - for (it = m_conns.begin(); it != m_conns.end(); ++it) - if (maxFD < (*it)->sock()) - maxFD = (*it)->sock(); - return maxFD; + const std::lock_guard lock(m_mutex); + return m_running; } -void RADIUS::buildFDSet(fd_set & fds) const +void RADIUS::SetRunning(bool val) { - FD_ZERO(&fds); - FD_SET(m_listenSocket, &fds); - std::deque::const_iterator it; - for (it = m_conns.begin(); it != m_conns.end(); ++it) - FD_SET((*it)->sock(), &fds); + const std::lock_guard lock(m_mutex); + m_running = val; } -void RADIUS::cleanupConns() +int RADIUS::Run(std::stop_token token) { - std::deque::iterator pos; - for (pos = m_conns.begin(); pos != m_conns.end(); ++pos) - if (((*pos)->isDone() && !(*pos)->isKeepAlive()) || !(*pos)->isOk()) { - delete *pos; - *pos = NULL; - } + SetRunning(true); - pos = std::remove(m_conns.begin(), m_conns.end(), static_cast(NULL)); - m_conns.erase(pos, m_conns.end()); -} - -void RADIUS::handleEvents(const fd_set & fds) -{ - if (FD_ISSET(m_listenSocket, &fds)) - acceptConnection(); - else + 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_ioContext.run(); + } + catch (const std::exception& e) { - std::deque::iterator it; - for (it = m_conns.begin(); it != m_conns.end(); ++it) - if (FD_ISSET((*it)->sock(), &fds)) - (*it)->read(); + m_errorStr = "Exception in RADIUS::Run(): " + std::string(e.what()); + m_logger("Exception in RADIUS:: Run(): %s", e.what()); + printfd(__FILE__, "Exception in RADIUS:: Run(). Message: '%s'\n", e.what()); } + + SetRunning(false); + return 0; }