From 051fbadb9953b13e5609856528e86709cf7ca1f9 Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Tue, 8 Mar 2022 21:28:22 +0000 Subject: [PATCH] Move some CONTROL code to plugin common. --- plugins/common/plugin_common.c | 93 +++++++- plugins/common/plugin_common.h | 6 +- plugins/dynamic-security/control.c | 248 +++++++------------- plugins/dynamic-security/dynamic_security.h | 1 - 4 files changed, 180 insertions(+), 168 deletions(-) diff --git a/plugins/common/plugin_common.c b/plugins/common/plugin_common.c index ed71a6cf..198edc43 100644 --- a/plugins/common/plugin_common.c +++ b/plugins/common/plugin_common.c @@ -1,4 +1,5 @@ #include "plugin_common.h" +#include "json_help.h" #include #include @@ -24,7 +25,7 @@ void plugin__command_reply(struct plugin_cmd *cmd, const char *error) cJSON_AddItemToArray(cmd->j_responses, j_response); } -void plugin_send_response(cJSON *tree, const char *topic) +void plugin__send_response(cJSON *tree, const char *topic) { char *payload; size_t payload_len; @@ -40,3 +41,93 @@ void plugin_send_response(cJSON *tree, const char *topic) } mosquitto_broker_publish(NULL, topic, (int)payload_len, payload, 0, 0, NULL); } + + +static int plugin__generic_handle_commands(struct plugin_cmd *cmd, struct mosquitto *context, cJSON *commands, void *userdata, int (*cmd_cb)(struct plugin_cmd *cmd, struct mosquitto *context, const char *command, void *userdata)) +{ + cJSON *aiter; + char *command; + + cJSON_ArrayForEach(aiter, commands){ + cmd->command_name = "Unknown command"; + if(cJSON_IsObject(aiter)){ + if(json_get_string(aiter, "command", &command, false) == MOSQ_ERR_SUCCESS){ + cmd->j_command = aiter; + cmd->correlation_data = NULL; + cmd->command_name = command; + + if(json_get_string(aiter, "correlationData", &cmd->correlation_data, true) != MOSQ_ERR_SUCCESS){ + plugin__command_reply(cmd, "Invalid correlationData data type."); + return MOSQ_ERR_INVAL; + } + + cmd_cb(cmd, context, command, userdata); + }else{ + plugin__command_reply(cmd, "Missing command"); + return MOSQ_ERR_INVAL; + } + }else{ + plugin__command_reply(cmd, "Command not an object"); + return MOSQ_ERR_INVAL; + } + } + return MOSQ_ERR_SUCCESS; +} + +int plugin__generic_control_callback(struct mosquitto_evt_control *event_data, const char *response_topic, void *userdata, + int (*cmd_cb)(struct plugin_cmd *cmd, struct mosquitto *context, const char *command, void *userdata)) + +{ + struct mosquitto_evt_control *ed = event_data; + struct plugin_cmd cmd; + cJSON *tree, *commands; + cJSON *j_response_tree; + + if(!event_data || !cmd_cb){ + return MOSQ_ERR_INVAL; + } + + memset(&cmd, 0, sizeof(cmd)); + cmd.command_name = "Unknown command"; + + /* Create object for responses */ + j_response_tree = cJSON_CreateObject(); + if(j_response_tree == NULL){ + return MOSQ_ERR_NOMEM; + } + cmd.j_responses = cJSON_AddArrayToObject(j_response_tree, "responses"); + if(cmd.j_responses == NULL){ + cJSON_Delete(j_response_tree); + return MOSQ_ERR_NOMEM; + } + + /* Parse cJSON tree. + * Using cJSON_ParseWithLength() is the best choice here, but Mosquitto + * always adds an extra 0 to the end of the payload memory, so using + * cJSON_Parse() on its own will still not overrun. */ +#if CJSON_VERSION_FULL < 1007013 + tree = cJSON_Parse(ed->payload); +#else + tree = cJSON_ParseWithLength(ed->payload, ed->payloadlen); +#endif + if(tree == NULL){ + plugin__command_reply(&cmd, "Payload not valid JSON"); + plugin__send_response(j_response_tree, response_topic); + return MOSQ_ERR_SUCCESS; + } + commands = cJSON_GetObjectItem(tree, "commands"); + if(commands == NULL || !cJSON_IsArray(commands)){ + cJSON_Delete(tree); + plugin__command_reply(&cmd, "Invalid/missing commands"); + plugin__send_response(j_response_tree, response_topic); + return MOSQ_ERR_SUCCESS; + } + + /* Handle commands */ + plugin__generic_handle_commands(&cmd, ed->client, commands, userdata, cmd_cb); + cJSON_Delete(tree); + + plugin__send_response(j_response_tree, response_topic); + + return MOSQ_ERR_SUCCESS; +} diff --git a/plugins/common/plugin_common.h b/plugins/common/plugin_common.h index ce5be931..895bf4c9 100644 --- a/plugins/common/plugin_common.h +++ b/plugins/common/plugin_common.h @@ -2,6 +2,7 @@ #define PLUGIN_SHARED_H #include +#include "mosquitto_broker.h" struct plugin_cmd{ cJSON *j_responses; @@ -11,7 +12,8 @@ struct plugin_cmd{ }; void plugin__command_reply(struct plugin_cmd *cmd, const char *error); - -void plugin_send_response(cJSON *tree, const char* topic); +void plugin__send_response(cJSON *tree, const char* topic); +int plugin__generic_control_callback(struct mosquitto_evt_control *event_data, const char *response_topic, void *userdata, + int (*cmd_cb)(struct plugin_cmd *cmd, struct mosquitto *context, const char *command, void *userdata)); #endif diff --git a/plugins/dynamic-security/control.c b/plugins/dynamic-security/control.c index f5b8633d..e0c68d3c 100644 --- a/plugins/dynamic-security/control.c +++ b/plugins/dynamic-security/control.c @@ -33,178 +33,98 @@ Contributors: #include "dynamic_security.h" -static void send_response(cJSON *tree) +#define RESPONSE_TOPIC "$CONTROL/dynamic-security/v1/response" + +static int dynsec__handle_command(struct plugin_cmd *cmd, struct mosquitto *context, const char *command, void *userdata) { - plugin_send_response(tree, "$CONTROL/dynamic-security/v1/response"); + struct dynsec__data *data = userdata; + int rc = MOSQ_ERR_SUCCESS; + + /* Plugin */ + if(!strcasecmp(command, "setDefaultACLAccess")){ + rc = dynsec__process_set_default_acl_access(data, cmd, context); + }else if(!strcasecmp(command, "getDefaultACLAccess")){ + rc = dynsec__process_get_default_acl_access(data, cmd, context); + + /* Clients */ + }else if(!strcasecmp(command, "createClient")){ + rc = dynsec_clients__process_create(data, cmd, context); + }else if(!strcasecmp(command, "deleteClient")){ + rc = dynsec_clients__process_delete(data, cmd, context); + }else if(!strcasecmp(command, "getClient")){ + rc = dynsec_clients__process_get(data, cmd, context); + }else if(!strcasecmp(command, "listClients")){ + rc = dynsec_clients__process_list(data, cmd, context); + }else if(!strcasecmp(command, "modifyClient")){ + rc = dynsec_clients__process_modify(data, cmd, context); + }else if(!strcasecmp(command, "setClientPassword")){ + rc = dynsec_clients__process_set_password(data, cmd, context); + }else if(!strcasecmp(command, "setClientId")){ + rc = dynsec_clients__process_set_id(data, cmd, context); + }else if(!strcasecmp(command, "addClientRole")){ + rc = dynsec_clients__process_add_role(data, cmd, context); + }else if(!strcasecmp(command, "removeClientRole")){ + rc = dynsec_clients__process_remove_role(data, cmd, context); + }else if(!strcasecmp(command, "enableClient")){ + rc = dynsec_clients__process_enable(data, cmd, context); + }else if(!strcasecmp(command, "disableClient")){ + rc = dynsec_clients__process_disable(data, cmd, context); + + /* Groups */ + }else if(!strcasecmp(command, "addGroupClient")){ + rc = dynsec_groups__process_add_client(data, cmd, context); + }else if(!strcasecmp(command, "createGroup")){ + rc = dynsec_groups__process_create(data, cmd, context); + }else if(!strcasecmp(command, "deleteGroup")){ + rc = dynsec_groups__process_delete(data, cmd, context); + }else if(!strcasecmp(command, "getGroup")){ + rc = dynsec_groups__process_get(data, cmd, context); + }else if(!strcasecmp(command, "listGroups")){ + rc = dynsec_groups__process_list(data, cmd, context); + }else if(!strcasecmp(command, "modifyGroup")){ + rc = dynsec_groups__process_modify(data, cmd, context); + }else if(!strcasecmp(command, "removeGroupClient")){ + rc = dynsec_groups__process_remove_client(data, cmd, context); + }else if(!strcasecmp(command, "addGroupRole")){ + rc = dynsec_groups__process_add_role(data, cmd, context); + }else if(!strcasecmp(command, "removeGroupRole")){ + rc = dynsec_groups__process_remove_role(data, cmd, context); + }else if(!strcasecmp(command, "setAnonymousGroup")){ + rc = dynsec_groups__process_set_anonymous_group(data, cmd, context); + }else if(!strcasecmp(command, "getAnonymousGroup")){ + rc = dynsec_groups__process_get_anonymous_group(data, cmd, context); + + /* Roles */ + }else if(!strcasecmp(command, "createRole")){ + rc = dynsec_roles__process_create(data, cmd, context); + }else if(!strcasecmp(command, "getRole")){ + rc = dynsec_roles__process_get(data, cmd, context); + }else if(!strcasecmp(command, "listRoles")){ + rc = dynsec_roles__process_list(data, cmd, context); + }else if(!strcasecmp(command, "modifyRole")){ + rc = dynsec_roles__process_modify(data, cmd, context); + }else if(!strcasecmp(command, "deleteRole")){ + rc = dynsec_roles__process_delete(data, cmd, context); + }else if(!strcasecmp(command, "addRoleACL")){ + rc = dynsec_roles__process_add_acl(data, cmd, context); + }else if(!strcasecmp(command, "removeRoleACL")){ + rc = dynsec_roles__process_remove_acl(data, cmd, context); + + /* Unknown */ + }else{ + plugin__command_reply(cmd, "Unknown command"); + rc = MOSQ_ERR_INVAL; + } + + return rc; } int dynsec_control_callback(int event, void *event_data, void *userdata) { struct mosquitto_evt_control *ed = event_data; - struct dynsec__data *data = userdata; - struct plugin_cmd cmd; - cJSON *tree, *commands; - cJSON *j_response_tree; UNUSED(event); - UNUSED(userdata); - memset(&cmd, 0, sizeof(cmd)); - cmd.command_name = "Unknown command"; - - /* Create object for responses */ - j_response_tree = cJSON_CreateObject(); - if(j_response_tree == NULL){ - return MOSQ_ERR_NOMEM; - } - cmd.j_responses = cJSON_AddArrayToObject(j_response_tree, "responses"); - if(cmd.j_responses == NULL){ - cJSON_Delete(j_response_tree); - return MOSQ_ERR_NOMEM; - } - - /* Parse cJSON tree. - * Using cJSON_ParseWithLength() is the best choice here, but Mosquitto - * always adds an extra 0 to the end of the payload memory, so using - * cJSON_Parse() on its own will still not overrun. */ -#if CJSON_VERSION_FULL < 1007013 - tree = cJSON_Parse(ed->payload); -#else - tree = cJSON_ParseWithLength(ed->payload, ed->payloadlen); -#endif - if(tree == NULL){ - plugin__command_reply(&cmd, "Payload not valid JSON"); - send_response(j_response_tree); - return MOSQ_ERR_SUCCESS; - } - commands = cJSON_GetObjectItem(tree, "commands"); - if(commands == NULL || !cJSON_IsArray(commands)){ - cJSON_Delete(tree); - plugin__command_reply(&cmd, "Invalid/missing commands"); - send_response(j_response_tree); - return MOSQ_ERR_SUCCESS; - } - - /* Handle commands */ - dynsec__handle_control(data, &cmd, ed->client, commands); - cJSON_Delete(tree); - - send_response(j_response_tree); - - return MOSQ_ERR_SUCCESS; -} - - -/* ################################################################ - * # - * # $CONTROL/dynamic-security/v1 handler - * # - * ################################################################ */ - -int dynsec__handle_control(struct dynsec__data *data, struct plugin_cmd *cmd, struct mosquitto *context, cJSON *commands) -{ - int rc = MOSQ_ERR_SUCCESS; - cJSON *aiter; - char *command; - - cJSON_ArrayForEach(aiter, commands){ - cmd->command_name = "Unknown command"; - if(cJSON_IsObject(aiter)){ - if(json_get_string(aiter, "command", &command, false) == MOSQ_ERR_SUCCESS){ - cmd->j_command = aiter; - cmd->correlation_data = NULL; - cmd->command_name = command; - - if(json_get_string(aiter, "correlationData", &cmd->correlation_data, true) != MOSQ_ERR_SUCCESS){ - plugin__command_reply(cmd, "Invalid correlationData data type."); - return MOSQ_ERR_INVAL; - } - - /* Plugin */ - if(!strcasecmp(command, "setDefaultACLAccess")){ - rc = dynsec__process_set_default_acl_access(data, cmd, context); - }else if(!strcasecmp(command, "getDefaultACLAccess")){ - rc = dynsec__process_get_default_acl_access(data, cmd, context); - - /* Clients */ - }else if(!strcasecmp(command, "createClient")){ - rc = dynsec_clients__process_create(data, cmd, context); - }else if(!strcasecmp(command, "deleteClient")){ - rc = dynsec_clients__process_delete(data, cmd, context); - }else if(!strcasecmp(command, "getClient")){ - rc = dynsec_clients__process_get(data, cmd, context); - }else if(!strcasecmp(command, "listClients")){ - rc = dynsec_clients__process_list(data, cmd, context); - }else if(!strcasecmp(command, "modifyClient")){ - rc = dynsec_clients__process_modify(data, cmd, context); - }else if(!strcasecmp(command, "setClientPassword")){ - rc = dynsec_clients__process_set_password(data, cmd, context); - }else if(!strcasecmp(command, "setClientId")){ - rc = dynsec_clients__process_set_id(data, cmd, context); - }else if(!strcasecmp(command, "addClientRole")){ - rc = dynsec_clients__process_add_role(data, cmd, context); - }else if(!strcasecmp(command, "removeClientRole")){ - rc = dynsec_clients__process_remove_role(data, cmd, context); - }else if(!strcasecmp(command, "enableClient")){ - rc = dynsec_clients__process_enable(data, cmd, context); - }else if(!strcasecmp(command, "disableClient")){ - rc = dynsec_clients__process_disable(data, cmd, context); - - /* Groups */ - }else if(!strcasecmp(command, "addGroupClient")){ - rc = dynsec_groups__process_add_client(data, cmd, context); - }else if(!strcasecmp(command, "createGroup")){ - rc = dynsec_groups__process_create(data, cmd, context); - }else if(!strcasecmp(command, "deleteGroup")){ - rc = dynsec_groups__process_delete(data, cmd, context); - }else if(!strcasecmp(command, "getGroup")){ - rc = dynsec_groups__process_get(data, cmd, context); - }else if(!strcasecmp(command, "listGroups")){ - rc = dynsec_groups__process_list(data, cmd, context); - }else if(!strcasecmp(command, "modifyGroup")){ - rc = dynsec_groups__process_modify(data, cmd, context); - }else if(!strcasecmp(command, "removeGroupClient")){ - rc = dynsec_groups__process_remove_client(data, cmd, context); - }else if(!strcasecmp(command, "addGroupRole")){ - rc = dynsec_groups__process_add_role(data, cmd, context); - }else if(!strcasecmp(command, "removeGroupRole")){ - rc = dynsec_groups__process_remove_role(data, cmd, context); - }else if(!strcasecmp(command, "setAnonymousGroup")){ - rc = dynsec_groups__process_set_anonymous_group(data, cmd, context); - }else if(!strcasecmp(command, "getAnonymousGroup")){ - rc = dynsec_groups__process_get_anonymous_group(data, cmd, context); - - /* Roles */ - }else if(!strcasecmp(command, "createRole")){ - rc = dynsec_roles__process_create(data, cmd, context); - }else if(!strcasecmp(command, "getRole")){ - rc = dynsec_roles__process_get(data, cmd, context); - }else if(!strcasecmp(command, "listRoles")){ - rc = dynsec_roles__process_list(data, cmd, context); - }else if(!strcasecmp(command, "modifyRole")){ - rc = dynsec_roles__process_modify(data, cmd, context); - }else if(!strcasecmp(command, "deleteRole")){ - rc = dynsec_roles__process_delete(data, cmd, context); - }else if(!strcasecmp(command, "addRoleACL")){ - rc = dynsec_roles__process_add_acl(data, cmd, context); - }else if(!strcasecmp(command, "removeRoleACL")){ - rc = dynsec_roles__process_remove_acl(data, cmd, context); - - /* Unknown */ - }else{ - plugin__command_reply(cmd, "Unknown command"); - rc = MOSQ_ERR_INVAL; - } - }else{ - plugin__command_reply(cmd, "Missing command"); - rc = MOSQ_ERR_INVAL; - } - }else{ - plugin__command_reply(cmd, "Command not an object"); - rc = MOSQ_ERR_INVAL; - } - } - - return rc; + return plugin__generic_control_callback(ed, RESPONSE_TOPIC, userdata, dynsec__handle_command); } diff --git a/plugins/dynamic-security/dynamic_security.h b/plugins/dynamic-security/dynamic_security.h index 16508cd9..87e5f4f4 100644 --- a/plugins/dynamic-security/dynamic_security.h +++ b/plugins/dynamic-security/dynamic_security.h @@ -149,7 +149,6 @@ struct dynsec__data{ int dynsec__config_init(const char *filename); void dynsec__config_save(struct dynsec__data *data); int dynsec__config_load(struct dynsec__data *data); -int dynsec__handle_control(struct dynsec__data *data, struct plugin_cmd *cmd, struct mosquitto *context, cJSON *commands); void dynsec__command_reply(cJSON *j_responses, struct mosquitto *context, const char *command, const char *error, const char *correlation_data); int dynsec_control_callback(int event, void *event_data, void *userdata);