// Copyright 2021 James Pace // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include #include #include #include // Mosquitto Globals static mosquitto_plugin_id_t * plugin_id = nullptr; static std::unique_ptr authorizer = nullptr; int mosquitto_plugin_version(int supported_version_count, const int * supported_versions) { for (int index = 0; index < supported_version_count; index++) { if (supported_versions[index] == 5) { return 5; } } return -1; } int mosquitto_plugin_init( mosquitto_plugin_id_t * identifier, void ** userdata, struct mosquitto_opt * options, int option_count) { plugin_id = identifier; if (option_count != 2) { mosquitto_log_printf(MOSQ_LOG_ERR, "Missing an option. Found: %d", option_count); return MOSQ_ERR_INVAL; } std::string public_key; std::string issuer; for (int index = 0; index < option_count; index++) { const auto key = std::string(options[index].key); if (key == "public_key") { const auto key = Authorizer::read_key(std::string(options[index].value)); if (not key or key->empty()) { mosquitto_log_printf(MOSQ_LOG_ERR, "Could not read public key."); return MOSQ_ERR_INVAL; } public_key = *key; } else if (key == "issuer") { issuer = std::string(options[index].value); if (issuer.empty()) { mosquitto_log_printf(MOSQ_LOG_ERR, "issuer not set."); return MOSQ_ERR_INVAL; } } } authorizer = std::make_unique(public_key, issuer); // Register the callbacks. mosquitto_callback_register( plugin_id, MOSQ_EVT_BASIC_AUTH, j7s_auth_basic_auth_callback, NULL, NULL); mosquitto_callback_register(plugin_id, MOSQ_EVT_ACL_CHECK, j7s_acl_check_callback, NULL, NULL); mosquitto_callback_register( plugin_id, MOSQ_EVT_DISCONNECT, j7s_disconnect_callback, NULL, NULL); // May want MOSQ_EVT_RELOAD as well. return MOSQ_ERR_SUCCESS; } int mosquitto_plugin_cleanup(void * userdata, struct mosquitto_opt * options, int option_count) { if (plugin_id) { mosquitto_callback_unregister( plugin_id, MOSQ_EVT_BASIC_AUTH, j7s_auth_basic_auth_callback, NULL); mosquitto_callback_unregister(plugin_id, MOSQ_EVT_ACL_CHECK, j7s_acl_check_callback, NULL); mosquitto_callback_unregister( plugin_id, MOSQ_EVT_DISCONNECT, j7s_disconnect_callback, NULL); } return MOSQ_ERR_SUCCESS; } int j7s_auth_basic_auth_callback(int event, void * event_data, void * userdata) { if (not authorizer) { // Not sure this is possible. return MOSQ_ERR_AUTH; } struct mosquitto_evt_basic_auth * auth_data = static_cast(event_data); if (!auth_data->username) { // We need a username to do anything. return MOSQ_ERR_PLUGIN_DEFER; } if (!auth_data->password) { authorizer->add_unknown(std::string(auth_data->username)); return MOSQ_ERR_PLUGIN_DEFER; } bool is_authed = authorizer->add(std::string(auth_data->password), std::string(auth_data->username)); if (is_authed) { return MOSQ_ERR_SUCCESS; } else { return MOSQ_ERR_AUTH; } } int j7s_acl_check_callback(int event, void * event_data, void * userdata) { if (not authorizer) { return MOSQ_ERR_ACL_DENIED; } struct mosquitto_evt_acl_check * acl_data = static_cast(event_data); const std::string username = std::string(mosquitto_client_username(acl_data->client)); if (authorizer->is_unknown(username)) { return MOSQ_ERR_PLUGIN_DEFER; } switch (acl_data->access) { case MOSQ_ACL_SUBSCRIBE: return MOSQ_ERR_PLUGIN_DEFER; case MOSQ_ACL_UNSUBSCRIBE: return MOSQ_ERR_PLUGIN_DEFER; case MOSQ_ACL_WRITE: return (authorizer->can_write(username) ? MOSQ_ERR_SUCCESS : MOSQ_ERR_ACL_DENIED); case MOSQ_ACL_READ: return (authorizer->can_read(username) ? MOSQ_ERR_SUCCESS : MOSQ_ERR_ACL_DENIED); default: return MOSQ_ERR_ACL_DENIED; } } int j7s_disconnect_callback(int event, void * event_data, void * userdata) { struct mosquitto_evt_disconnect * disconnect_data = static_cast(event_data); const std::string username = std::string(mosquitto_client_username(disconnect_data->client)); authorizer->logout(username); return MOSQ_ERR_SUCCESS; }