Commit 1b9f09780e1ae659ef0e9b8086b173da2365a5a1
1 parent
4315fb5b
Start of auth plugin.
I kind of need a config file parser first, so I'm going to make that.
Showing
5 changed files
with
207 additions
and
1 deletions
CMakeLists.txt
| ... | ... | @@ -5,6 +5,7 @@ project(FlashMQ LANGUAGES CXX) |
| 5 | 5 | set(CMAKE_CXX_STANDARD 11) |
| 6 | 6 | set(CMAKE_CXX_STANDARD_REQUIRED ON) |
| 7 | 7 | |
| 8 | +SET(CMAKE_CXX_FLAGS "-rdynamic") | |
| 8 | 9 | |
| 9 | 10 | add_executable(FlashMQ |
| 10 | 11 | mainapp.cpp |
| ... | ... | @@ -21,6 +22,7 @@ add_executable(FlashMQ |
| 21 | 22 | retainedmessage.cpp |
| 22 | 23 | cirbuf.cpp |
| 23 | 24 | logger.cpp |
| 25 | + authplugin.cpp | |
| 24 | 26 | ) |
| 25 | 27 | |
| 26 | -target_link_libraries(FlashMQ pthread) | |
| 28 | +target_link_libraries(FlashMQ pthread dl) | ... | ... |
FlashMQTests/FlashMQTests.pro
| ... | ... | @@ -26,6 +26,7 @@ SOURCES += tst_maintests.cpp \ |
| 26 | 26 | ../types.cpp \ |
| 27 | 27 | ../utils.cpp \ |
| 28 | 28 | ../logger.cpp \ |
| 29 | + ../authplugin.cpp \ | |
| 29 | 30 | mainappthread.cpp \ |
| 30 | 31 | twoclienttestcontext.cpp |
| 31 | 32 | |
| ... | ... | @@ -44,5 +45,10 @@ HEADERS += \ |
| 44 | 45 | ../types.h \ |
| 45 | 46 | ../utils.h \ |
| 46 | 47 | ../logger.h \ |
| 48 | + ../authplugin.h \ | |
| 47 | 49 | mainappthread.h \ |
| 48 | 50 | twoclienttestcontext.h |
| 51 | + | |
| 52 | +LIBS += -ldl | |
| 53 | + | |
| 54 | +QMAKE_LFLAGS += -rdynamic | ... | ... |
authplugin.cpp
0 → 100644
| 1 | +#include "authplugin.h" | |
| 2 | + | |
| 3 | +#include <unistd.h> | |
| 4 | +#include <fcntl.h> | |
| 5 | +#include <sstream> | |
| 6 | +#include <dlfcn.h> | |
| 7 | + | |
| 8 | +#include "exceptions.h" | |
| 9 | + | |
| 10 | +// TODO: error handling on all the calls to the plugin. Exceptions? Passing to the caller? | |
| 11 | +// TODO: where to do the conditionals about whether the plugin is loaded, what to do on error, etc? | |
| 12 | +// -> Perhaps merely log the error (and return 'denied'?)? | |
| 13 | + | |
| 14 | +void mosquitto_log_printf(int level, const char *fmt, ...) | |
| 15 | +{ | |
| 16 | + Logger *logger = Logger::getInstance(); | |
| 17 | + va_list valist; | |
| 18 | + va_start(valist, fmt); | |
| 19 | + logger->logf(level, fmt); | |
| 20 | + va_end(valist); | |
| 21 | +} | |
| 22 | + | |
| 23 | + | |
| 24 | +AuthPlugin::AuthPlugin() // our configuration object as param | |
| 25 | +{ | |
| 26 | + logger = Logger::getInstance(); | |
| 27 | +} | |
| 28 | + | |
| 29 | +void *AuthPlugin::loadSymbol(void *handle, const char *symbol) | |
| 30 | +{ | |
| 31 | + void *r = dlsym(handle, symbol); | |
| 32 | + | |
| 33 | + if (r == NULL) | |
| 34 | + { | |
| 35 | + std::string errmsg(dlerror()); | |
| 36 | + throw FatalError(errmsg); | |
| 37 | + } | |
| 38 | + | |
| 39 | + return r; | |
| 40 | +} | |
| 41 | + | |
| 42 | +void AuthPlugin::loadPlugin(const std::string &pathToSoFile) | |
| 43 | +{ | |
| 44 | + logger->logf(LOG_INFO, "Loading auth plugin %s", pathToSoFile.c_str()); | |
| 45 | + | |
| 46 | + if (access(pathToSoFile.c_str(), R_OK) != 0) | |
| 47 | + { | |
| 48 | + std::ostringstream oss; | |
| 49 | + oss << "Error loading auth plugin: The file " << pathToSoFile << " is not there or not readable"; | |
| 50 | + throw FatalError(oss.str()); | |
| 51 | + } | |
| 52 | + | |
| 53 | + void *r = dlopen(pathToSoFile.c_str(), RTLD_NOW|RTLD_GLOBAL); | |
| 54 | + | |
| 55 | + if (r == NULL) | |
| 56 | + { | |
| 57 | + std::string errmsg(dlerror()); | |
| 58 | + throw FatalError(errmsg); | |
| 59 | + } | |
| 60 | + | |
| 61 | + version = (F_auth_plugin_version)loadSymbol(r, "mosquitto_auth_plugin_version"); | |
| 62 | + | |
| 63 | + if (version() != 2) | |
| 64 | + { | |
| 65 | + throw FatalError("Only Mosquitto plugin version 2 is supported at this time."); | |
| 66 | + } | |
| 67 | + | |
| 68 | + init_v2 = (F_auth_plugin_init_v2)loadSymbol(r, "mosquitto_auth_plugin_init"); | |
| 69 | + cleanup_v2 = (F_auth_plugin_cleanup_v2)loadSymbol(r, "mosquitto_auth_plugin_cleanup"); | |
| 70 | + security_init_v2 = (F_auth_plugin_security_init_v2)loadSymbol(r, "mosquitto_auth_security_init"); | |
| 71 | + security_cleanup_v2 = (F_auth_plugin_security_cleanup_v2)loadSymbol(r, "mosquitto_auth_security_cleanup"); | |
| 72 | + acl_check_v2 = (F_auth_plugin_acl_check_v2)loadSymbol(r, "mosquitto_auth_acl_check"); | |
| 73 | + unpwd_check_v2 = (F_auth_plugin_unpwd_check_v2)loadSymbol(r, "mosquitto_auth_unpwd_check"); | |
| 74 | + psk_key_get_v2 = (F_auth_plugin_psk_key_get_v2)loadSymbol(r, "mosquitto_auth_psk_key_get"); | |
| 75 | +} | |
| 76 | + | |
| 77 | +int AuthPlugin::init() | |
| 78 | +{ | |
| 79 | + struct mosquitto_auth_opt auth_opts[2]; // TODO: get auth opts from central config object | |
| 80 | + std::memset(&auth_opts, 0, sizeof(struct mosquitto_auth_opt) * 2); | |
| 81 | + int result = init_v2(&pluginData, auth_opts, 2); | |
| 82 | + return result; | |
| 83 | +} | |
| 84 | + | |
| 85 | +int AuthPlugin::cleanup() | |
| 86 | +{ | |
| 87 | + struct mosquitto_auth_opt auth_opts[2]; // TODO: get auth opts from central config object | |
| 88 | + std::memset(&auth_opts, 0, sizeof(struct mosquitto_auth_opt) * 2); | |
| 89 | + return cleanup_v2(pluginData, auth_opts, 2); | |
| 90 | +} | |
| 91 | + | |
| 92 | +int AuthPlugin::securityInit(bool reloading) | |
| 93 | +{ | |
| 94 | + struct mosquitto_auth_opt auth_opts[2]; // TODO: get auth opts from central config object | |
| 95 | + std::memset(&auth_opts, 0, sizeof(struct mosquitto_auth_opt) * 2); | |
| 96 | + return security_init_v2(pluginData, auth_opts, 2, reloading); | |
| 97 | +} | |
| 98 | + | |
| 99 | +int AuthPlugin::securityCleanup(bool reloading) | |
| 100 | +{ | |
| 101 | + struct mosquitto_auth_opt auth_opts[2]; // TODO: get auth opts from central config object | |
| 102 | + std::memset(&auth_opts, 0, sizeof(struct mosquitto_auth_opt) * 2); | |
| 103 | + return security_cleanup_v2(pluginData, auth_opts, 2, reloading); | |
| 104 | +} | |
| 105 | + | |
| 106 | +AuthResult AuthPlugin::aclCheck(const std::string &clientid, const std::string &username, const std::string &topic, AclAccess access) | |
| 107 | +{ | |
| 108 | + int result = acl_check_v2(pluginData, clientid.c_str(), username.c_str(), topic.c_str(), static_cast<int>(access)); | |
| 109 | + return static_cast<AuthResult>(result); | |
| 110 | +} | |
| 111 | + | |
| 112 | +AuthResult AuthPlugin::unPwdCheck(const std::string &username, const std::string &password) | |
| 113 | +{ | |
| 114 | + int result = unpwd_check_v2(pluginData, username.c_str(), password.c_str()); | |
| 115 | + return static_cast<AuthResult>(result); | |
| 116 | +} | |
| 117 | + | |
| 118 | + | ... | ... |
authplugin.h
0 → 100644
| 1 | +#ifndef AUTHPLUGIN_H | |
| 2 | +#define AUTHPLUGIN_H | |
| 3 | + | |
| 4 | +#include <string> | |
| 5 | +#include <cstring> | |
| 6 | + | |
| 7 | +#include "logger.h" | |
| 8 | + | |
| 9 | +// Compatible with Mosquitto | |
| 10 | +enum class AclAccess | |
| 11 | +{ | |
| 12 | + none = 0, | |
| 13 | + read = 1, | |
| 14 | + write = 2 | |
| 15 | +}; | |
| 16 | + | |
| 17 | +// Compatible with Mosquitto | |
| 18 | +enum class AuthResult | |
| 19 | +{ | |
| 20 | + success = 0, | |
| 21 | + acl_denied = 12, | |
| 22 | + login_denied = 11, | |
| 23 | + error = 13 | |
| 24 | +}; | |
| 25 | + | |
| 26 | +struct mosquitto_auth_opt { | |
| 27 | + char *key; | |
| 28 | + char *value; | |
| 29 | +}; | |
| 30 | + | |
| 31 | +typedef int (*F_auth_plugin_version)(void); | |
| 32 | + | |
| 33 | +typedef int (*F_auth_plugin_init_v2)(void **, struct mosquitto_auth_opt *, int); | |
| 34 | +typedef int (*F_auth_plugin_cleanup_v2)(void *, struct mosquitto_auth_opt *, int); | |
| 35 | +typedef int (*F_auth_plugin_security_init_v2)(void *, struct mosquitto_auth_opt *, int, bool); | |
| 36 | +typedef int (*F_auth_plugin_security_cleanup_v2)(void *, struct mosquitto_auth_opt *, int, bool); | |
| 37 | +typedef int (*F_auth_plugin_acl_check_v2)(void *, const char *, const char *, const char *, int); | |
| 38 | +typedef int (*F_auth_plugin_unpwd_check_v2)(void *, const char *, const char *); | |
| 39 | +typedef int (*F_auth_plugin_psk_key_get_v2)(void *, const char *, const char *, char *, int); | |
| 40 | + | |
| 41 | +extern "C" | |
| 42 | +{ | |
| 43 | + // Gets called by the plugin, so it needs to exist, globally | |
| 44 | + void mosquitto_log_printf(int level, const char *fmt, ...); | |
| 45 | +} | |
| 46 | + | |
| 47 | +class AuthPlugin | |
| 48 | +{ | |
| 49 | + F_auth_plugin_version version = nullptr; | |
| 50 | + F_auth_plugin_init_v2 init_v2 = nullptr; | |
| 51 | + F_auth_plugin_cleanup_v2 cleanup_v2 = nullptr; | |
| 52 | + F_auth_plugin_security_init_v2 security_init_v2 = nullptr; | |
| 53 | + F_auth_plugin_security_cleanup_v2 security_cleanup_v2 = nullptr; | |
| 54 | + F_auth_plugin_acl_check_v2 acl_check_v2 = nullptr; | |
| 55 | + F_auth_plugin_unpwd_check_v2 unpwd_check_v2 = nullptr; | |
| 56 | + F_auth_plugin_psk_key_get_v2 psk_key_get_v2 = nullptr; | |
| 57 | + | |
| 58 | + void *pluginData = nullptr; | |
| 59 | + Logger *logger = nullptr; | |
| 60 | + | |
| 61 | + void *loadSymbol(void *handle, const char *symbol); | |
| 62 | +public: | |
| 63 | + AuthPlugin(); | |
| 64 | + | |
| 65 | + void loadPlugin(const std::string &pathToSoFile); | |
| 66 | + int init(); | |
| 67 | + int cleanup(); | |
| 68 | + int securityInit(bool reloading); | |
| 69 | + int securityCleanup(bool reloading); | |
| 70 | + AuthResult aclCheck(const std::string &clientid, const std::string &username, const std::string &topic, AclAccess access); | |
| 71 | + AuthResult unPwdCheck(const std::string &username, const std::string &password); | |
| 72 | + | |
| 73 | +}; | |
| 74 | + | |
| 75 | +#endif // AUTHPLUGIN_H | ... | ... |
exceptions.h
| ... | ... | @@ -16,5 +16,10 @@ public: |
| 16 | 16 | NotImplementedException(const std::string &msg) : std::runtime_error(msg) {} |
| 17 | 17 | }; |
| 18 | 18 | |
| 19 | +class FatalError : public std::runtime_error | |
| 20 | +{ | |
| 21 | +public: | |
| 22 | + FatalError(const std::string &msg) : std::runtime_error(msg) {} | |
| 23 | +}; | |
| 19 | 24 | |
| 20 | 25 | #endif // EXCEPTIONS_H | ... | ... |