/*
This file is part of FlashMQ (https://www.flashmq.org)
Copyright (C) 2021 Wiebe Cazemier
FlashMQ is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, version 3.
FlashMQ 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public
License along with FlashMQ. If not, see .
*/
#include "authplugin.h"
#include
#include
#include
#include
#include "exceptions.h"
#include "unscopedlock.h"
std::mutex AuthPlugin::initMutex;
std::mutex AuthPlugin::authChecksMutex;
void mosquitto_log_printf(int level, const char *fmt, ...)
{
Logger *logger = Logger::getInstance();
va_list valist;
va_start(valist, fmt);
logger->logf(level, fmt, valist);
va_end(valist);
}
AuthPlugin::AuthPlugin(Settings &settings) :
settings(settings)
{
logger = Logger::getInstance();
}
AuthPlugin::~AuthPlugin()
{
cleanup();
}
void *AuthPlugin::loadSymbol(void *handle, const char *symbol) const
{
void *r = dlsym(handle, symbol);
if (r == NULL)
{
std::string errmsg(dlerror());
throw FatalError(errmsg);
}
return r;
}
void AuthPlugin::loadPlugin(const std::string &pathToSoFile)
{
if (pathToSoFile.empty())
return;
logger->logf(LOG_NOTICE, "Loading auth plugin %s", pathToSoFile.c_str());
initialized = false;
wanted = true;
if (access(pathToSoFile.c_str(), R_OK) != 0)
{
std::ostringstream oss;
oss << "Error loading auth plugin: The file " << pathToSoFile << " is not there or not readable";
throw FatalError(oss.str());
}
void *r = dlopen(pathToSoFile.c_str(), RTLD_NOW|RTLD_GLOBAL);
if (r == NULL)
{
std::string errmsg(dlerror());
throw FatalError(errmsg);
}
version = (F_auth_plugin_version)loadSymbol(r, "mosquitto_auth_plugin_version");
if (version() != 2)
{
throw FatalError("Only Mosquitto plugin version 2 is supported at this time.");
}
init_v2 = (F_auth_plugin_init_v2)loadSymbol(r, "mosquitto_auth_plugin_init");
cleanup_v2 = (F_auth_plugin_cleanup_v2)loadSymbol(r, "mosquitto_auth_plugin_cleanup");
security_init_v2 = (F_auth_plugin_security_init_v2)loadSymbol(r, "mosquitto_auth_security_init");
security_cleanup_v2 = (F_auth_plugin_security_cleanup_v2)loadSymbol(r, "mosquitto_auth_security_cleanup");
acl_check_v2 = (F_auth_plugin_acl_check_v2)loadSymbol(r, "mosquitto_auth_acl_check");
unpwd_check_v2 = (F_auth_plugin_unpwd_check_v2)loadSymbol(r, "mosquitto_auth_unpwd_check");
psk_key_get_v2 = (F_auth_plugin_psk_key_get_v2)loadSymbol(r, "mosquitto_auth_psk_key_get");
initialized = true;
}
void AuthPlugin::init()
{
if (!wanted)
return;
UnscopedLock lock(initMutex);
if (settings.authPluginSerializeInit)
lock.lock();
if (quitting)
return;
AuthOptCompatWrap &authOpts = settings.getAuthOptsCompat();
int result = init_v2(&pluginData, authOpts.head(), authOpts.size());
if (result != 0)
throw FatalError("Error initialising auth plugin.");
}
void AuthPlugin::cleanup()
{
if (!cleanup_v2)
return;
securityCleanup(false);
AuthOptCompatWrap &authOpts = settings.getAuthOptsCompat();
int result = cleanup_v2(pluginData, authOpts.head(), authOpts.size());
if (result != 0)
logger->logf(LOG_ERR, "Error cleaning up auth plugin"); // Not doing exception, because we're shutting down anyway.
}
void AuthPlugin::securityInit(bool reloading)
{
if (!wanted)
return;
UnscopedLock lock(initMutex);
if (settings.authPluginSerializeInit)
lock.lock();
if (quitting)
return;
AuthOptCompatWrap &authOpts = settings.getAuthOptsCompat();
int result = security_init_v2(pluginData, authOpts.head(), authOpts.size(), reloading);
if (result != 0)
{
throw AuthPluginException("Plugin function mosquitto_auth_security_init returned an error. If it didn't log anything, we don't know what it was.");
}
initialized = true;
}
void AuthPlugin::securityCleanup(bool reloading)
{
if (!wanted)
return;
initialized = false;
AuthOptCompatWrap &authOpts = settings.getAuthOptsCompat();
int result = security_cleanup_v2(pluginData, authOpts.head(), authOpts.size(), reloading);
if (result != 0)
{
throw AuthPluginException("Plugin function mosquitto_auth_security_cleanup returned an error. If it didn't log anything, we don't know what it was.");
}
}
AuthResult AuthPlugin::aclCheck(const std::string &clientid, const std::string &username, const std::string &topic, AclAccess access)
{
if (!wanted)
return AuthResult::success;
if (!initialized)
{
logger->logf(LOG_ERR, "ACL check wanted, but initialization failed. Can't perform check.");
return AuthResult::error;
}
UnscopedLock lock(authChecksMutex);
if (settings.authPluginSerializeAuthChecks)
lock.lock();
int result = acl_check_v2(pluginData, clientid.c_str(), username.c_str(), topic.c_str(), static_cast(access));
AuthResult result_ = static_cast(result);
if (result_ == AuthResult::error)
{
logger->logf(LOG_ERR, "ACL check by plugin returned error for topic '%s'. If it didn't log anything, we don't know what it was.", topic.c_str());
}
return result_;
}
AuthResult AuthPlugin::unPwdCheck(const std::string &username, const std::string &password)
{
if (!wanted)
return AuthResult::success;
if (!initialized)
{
logger->logf(LOG_ERR, "Username+password check wanted, but initialization failed. Can't perform check.");
return AuthResult::error;
}
UnscopedLock lock(authChecksMutex);
if (settings.authPluginSerializeAuthChecks)
lock.lock();
int result = unpwd_check_v2(pluginData, username.c_str(), password.c_str());
AuthResult r = static_cast(result);
if (r == AuthResult::error)
{
logger->logf(LOG_ERR, "Username+password check by plugin returned error for user '%s'. If it didn't log anything, we don't know what it was.", username.c_str());
}
return r;
}
void AuthPlugin::setQuitting()
{
this->quitting = true;
}
std::string AuthResultToString(AuthResult r)
{
{
if (r == AuthResult::success)
return "success";
if (r == AuthResult::acl_denied)
return "ACL denied";
if (r == AuthResult::login_denied)
return "login Denied";
if (r == AuthResult::error)
return "error in check";
}
return "";
}