diff --git a/CMakeLists.txt b/CMakeLists.txt index 9cbd01e..60123d0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,6 +5,7 @@ project(FlashMQ LANGUAGES CXX) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) +SET(CMAKE_CXX_FLAGS "-rdynamic") add_executable(FlashMQ mainapp.cpp @@ -21,6 +22,7 @@ add_executable(FlashMQ retainedmessage.cpp cirbuf.cpp logger.cpp + authplugin.cpp ) -target_link_libraries(FlashMQ pthread) +target_link_libraries(FlashMQ pthread dl) diff --git a/FlashMQTests/FlashMQTests.pro b/FlashMQTests/FlashMQTests.pro index 281e8fc..0a4e429 100644 --- a/FlashMQTests/FlashMQTests.pro +++ b/FlashMQTests/FlashMQTests.pro @@ -26,6 +26,7 @@ SOURCES += tst_maintests.cpp \ ../types.cpp \ ../utils.cpp \ ../logger.cpp \ + ../authplugin.cpp \ mainappthread.cpp \ twoclienttestcontext.cpp @@ -44,5 +45,10 @@ HEADERS += \ ../types.h \ ../utils.h \ ../logger.h \ + ../authplugin.h \ mainappthread.h \ twoclienttestcontext.h + +LIBS += -ldl + +QMAKE_LFLAGS += -rdynamic diff --git a/authplugin.cpp b/authplugin.cpp new file mode 100644 index 0000000..41b40d4 --- /dev/null +++ b/authplugin.cpp @@ -0,0 +1,118 @@ +#include "authplugin.h" + +#include +#include +#include +#include + +#include "exceptions.h" + +// TODO: error handling on all the calls to the plugin. Exceptions? Passing to the caller? +// TODO: where to do the conditionals about whether the plugin is loaded, what to do on error, etc? +// -> Perhaps merely log the error (and return 'denied'?)? + +void mosquitto_log_printf(int level, const char *fmt, ...) +{ + Logger *logger = Logger::getInstance(); + va_list valist; + va_start(valist, fmt); + logger->logf(level, fmt); + va_end(valist); +} + + +AuthPlugin::AuthPlugin() // our configuration object as param +{ + logger = Logger::getInstance(); +} + +void *AuthPlugin::loadSymbol(void *handle, const char *symbol) +{ + void *r = dlsym(handle, symbol); + + if (r == NULL) + { + std::string errmsg(dlerror()); + throw FatalError(errmsg); + } + + return r; +} + +void AuthPlugin::loadPlugin(const std::string &pathToSoFile) +{ + logger->logf(LOG_INFO, "Loading auth plugin %s", pathToSoFile.c_str()); + + 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"); +} + +int AuthPlugin::init() +{ + struct mosquitto_auth_opt auth_opts[2]; // TODO: get auth opts from central config object + std::memset(&auth_opts, 0, sizeof(struct mosquitto_auth_opt) * 2); + int result = init_v2(&pluginData, auth_opts, 2); + return result; +} + +int AuthPlugin::cleanup() +{ + struct mosquitto_auth_opt auth_opts[2]; // TODO: get auth opts from central config object + std::memset(&auth_opts, 0, sizeof(struct mosquitto_auth_opt) * 2); + return cleanup_v2(pluginData, auth_opts, 2); +} + +int AuthPlugin::securityInit(bool reloading) +{ + struct mosquitto_auth_opt auth_opts[2]; // TODO: get auth opts from central config object + std::memset(&auth_opts, 0, sizeof(struct mosquitto_auth_opt) * 2); + return security_init_v2(pluginData, auth_opts, 2, reloading); +} + +int AuthPlugin::securityCleanup(bool reloading) +{ + struct mosquitto_auth_opt auth_opts[2]; // TODO: get auth opts from central config object + std::memset(&auth_opts, 0, sizeof(struct mosquitto_auth_opt) * 2); + return security_cleanup_v2(pluginData, auth_opts, 2, reloading); +} + +AuthResult AuthPlugin::aclCheck(const std::string &clientid, const std::string &username, const std::string &topic, AclAccess access) +{ + int result = acl_check_v2(pluginData, clientid.c_str(), username.c_str(), topic.c_str(), static_cast(access)); + return static_cast(result); +} + +AuthResult AuthPlugin::unPwdCheck(const std::string &username, const std::string &password) +{ + int result = unpwd_check_v2(pluginData, username.c_str(), password.c_str()); + return static_cast(result); +} + + diff --git a/authplugin.h b/authplugin.h new file mode 100644 index 0000000..ca3d548 --- /dev/null +++ b/authplugin.h @@ -0,0 +1,75 @@ +#ifndef AUTHPLUGIN_H +#define AUTHPLUGIN_H + +#include +#include + +#include "logger.h" + +// Compatible with Mosquitto +enum class AclAccess +{ + none = 0, + read = 1, + write = 2 +}; + +// Compatible with Mosquitto +enum class AuthResult +{ + success = 0, + acl_denied = 12, + login_denied = 11, + error = 13 +}; + +struct mosquitto_auth_opt { + char *key; + char *value; +}; + +typedef int (*F_auth_plugin_version)(void); + +typedef int (*F_auth_plugin_init_v2)(void **, struct mosquitto_auth_opt *, int); +typedef int (*F_auth_plugin_cleanup_v2)(void *, struct mosquitto_auth_opt *, int); +typedef int (*F_auth_plugin_security_init_v2)(void *, struct mosquitto_auth_opt *, int, bool); +typedef int (*F_auth_plugin_security_cleanup_v2)(void *, struct mosquitto_auth_opt *, int, bool); +typedef int (*F_auth_plugin_acl_check_v2)(void *, const char *, const char *, const char *, int); +typedef int (*F_auth_plugin_unpwd_check_v2)(void *, const char *, const char *); +typedef int (*F_auth_plugin_psk_key_get_v2)(void *, const char *, const char *, char *, int); + +extern "C" +{ + // Gets called by the plugin, so it needs to exist, globally + void mosquitto_log_printf(int level, const char *fmt, ...); +} + +class AuthPlugin +{ + F_auth_plugin_version version = nullptr; + F_auth_plugin_init_v2 init_v2 = nullptr; + F_auth_plugin_cleanup_v2 cleanup_v2 = nullptr; + F_auth_plugin_security_init_v2 security_init_v2 = nullptr; + F_auth_plugin_security_cleanup_v2 security_cleanup_v2 = nullptr; + F_auth_plugin_acl_check_v2 acl_check_v2 = nullptr; + F_auth_plugin_unpwd_check_v2 unpwd_check_v2 = nullptr; + F_auth_plugin_psk_key_get_v2 psk_key_get_v2 = nullptr; + + void *pluginData = nullptr; + Logger *logger = nullptr; + + void *loadSymbol(void *handle, const char *symbol); +public: + AuthPlugin(); + + void loadPlugin(const std::string &pathToSoFile); + int init(); + int cleanup(); + int securityInit(bool reloading); + int securityCleanup(bool reloading); + AuthResult aclCheck(const std::string &clientid, const std::string &username, const std::string &topic, AclAccess access); + AuthResult unPwdCheck(const std::string &username, const std::string &password); + +}; + +#endif // AUTHPLUGIN_H diff --git a/exceptions.h b/exceptions.h index 99f7e21..55cac3b 100644 --- a/exceptions.h +++ b/exceptions.h @@ -16,5 +16,10 @@ public: NotImplementedException(const std::string &msg) : std::runtime_error(msg) {} }; +class FatalError : public std::runtime_error +{ +public: + FatalError(const std::string &msg) : std::runtime_error(msg) {} +}; #endif // EXCEPTIONS_H