123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089 |
- /*
- Copyright (c) 2020 Roger Light <roger@atchoo.org>
- All rights reserved. This program and the accompanying materials
- are made available under the terms of the Eclipse Public License 2.0
- and Eclipse Distribution License v1.0 which accompany this distribution.
- The Eclipse Public License is available at
- https://www.eclipse.org/legal/epl-2.0/
- and the Eclipse Distribution License is available at
- http://www.eclipse.org/org/documents/edl-v10.php.
- SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
- Contributors:
- Roger Light - initial implementation and documentation.
- */
- #include "config.h"
- #include <cjson/cJSON.h>
- #include <stdio.h>
- #include <uthash.h>
- #include "mosquitto.h"
- #include "mosquitto_broker.h"
- #include "json_help.h"
- #include "dynamic_security.h"
- /* ################################################################
- * #
- * # Plugin global variables
- * #
- * ################################################################ */
- struct dynsec__group *dynsec_anonymous_group = NULL;
- /* ################################################################
- * #
- * # Function declarations
- * #
- * ################################################################ */
- static int dynsec__remove_all_clients_from_group(struct dynsec__group *group);
- static int dynsec__remove_all_roles_from_group(struct dynsec__group *group);
- static cJSON *add_group_to_json(struct dynsec__group *group);
- /* ################################################################
- * #
- * # Local variables
- * #
- * ################################################################ */
- static struct dynsec__group *local_groups = NULL;
- /* ################################################################
- * #
- * # Utility functions
- * #
- * ################################################################ */
- static void group__kick_all(struct dynsec__group *group)
- {
- if(group == dynsec_anonymous_group){
- mosquitto_kick_client_by_username(NULL, false);
- }
- dynsec_clientlist__kick_all(group->clientlist);
- }
- static int group_cmp(void *a, void *b)
- {
- struct dynsec__group *group_a = a;
- struct dynsec__group *group_b = b;
- return strcmp(group_a->groupname, group_b->groupname);
- }
- struct dynsec__group *dynsec_groups__find(const char *groupname)
- {
- struct dynsec__group *group = NULL;
- if(groupname){
- HASH_FIND(hh, local_groups, groupname, strlen(groupname), group);
- }
- return group;
- }
- static void group__free_item(struct dynsec__group *group)
- {
- struct dynsec__group *found_group = NULL;
- if(group == NULL) return;
- found_group = dynsec_groups__find(group->groupname);
- if(found_group){
- HASH_DEL(local_groups, found_group);
- }
- dynsec__remove_all_clients_from_group(group);
- mosquitto_free(group->text_name);
- mosquitto_free(group->text_description);
- mosquitto_free(group->groupname);
- dynsec_rolelist__cleanup(&group->rolelist);
- mosquitto_free(group);
- }
- int dynsec_groups__process_add_role(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data)
- {
- char *groupname, *rolename;
- struct dynsec__group *group;
- struct dynsec__role *role;
- int priority;
- const char *admin_clientid, *admin_username;
- int rc;
- if(json_get_string(command, "groupname", &groupname, false) != MOSQ_ERR_SUCCESS){
- dynsec__command_reply(j_responses, context, "addGroupRole", "Invalid/missing groupname", correlation_data);
- return MOSQ_ERR_INVAL;
- }
- if(mosquitto_validate_utf8(groupname, (int)strlen(groupname)) != MOSQ_ERR_SUCCESS){
- dynsec__command_reply(j_responses, context, "addGroupRole", "Group name not valid UTF-8", correlation_data);
- return MOSQ_ERR_INVAL;
- }
- if(json_get_string(command, "rolename", &rolename, false) != MOSQ_ERR_SUCCESS){
- dynsec__command_reply(j_responses, context, "addGroupRole", "Invalid/missing rolename", correlation_data);
- return MOSQ_ERR_INVAL;
- }
- if(mosquitto_validate_utf8(rolename, (int)strlen(rolename)) != MOSQ_ERR_SUCCESS){
- dynsec__command_reply(j_responses, context, "addGroupRole", "Role name not valid UTF-8", correlation_data);
- return MOSQ_ERR_INVAL;
- }
- json_get_int(command, "priority", &priority, true, -1);
- group = dynsec_groups__find(groupname);
- if(group == NULL){
- dynsec__command_reply(j_responses, context, "addGroupRole", "Group not found", correlation_data);
- return MOSQ_ERR_SUCCESS;
- }
- role = dynsec_roles__find(rolename);
- if(role == NULL){
- dynsec__command_reply(j_responses, context, "addGroupRole", "Role not found", correlation_data);
- return MOSQ_ERR_SUCCESS;
- }
- admin_clientid = mosquitto_client_id(context);
- admin_username = mosquitto_client_username(context);
- rc = dynsec_rolelist__group_add(group, role, priority);
- if(rc == MOSQ_ERR_SUCCESS){
- /* Continue */
- }else if(rc == MOSQ_ERR_ALREADY_EXISTS){
- dynsec__command_reply(j_responses, context, "addGroupRole", "Group is already in this role", correlation_data);
- return MOSQ_ERR_ALREADY_EXISTS;
- }else{
- dynsec__command_reply(j_responses, context, "addGroupRole", "Internal error", correlation_data);
- return MOSQ_ERR_UNKNOWN;
- }
- mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | addGroupRole | groupname=%s | rolename=%s | priority=%d",
- admin_clientid, admin_username, groupname, rolename, priority);
- dynsec__config_save();
- dynsec__command_reply(j_responses, context, "addGroupRole", NULL, correlation_data);
- /* Enforce any changes */
- group__kick_all(group);
- return MOSQ_ERR_SUCCESS;
- }
- void dynsec_groups__cleanup(void)
- {
- struct dynsec__group *group, *group_tmp = NULL;
- HASH_ITER(hh, local_groups, group, group_tmp){
- group__free_item(group);
- }
- }
- /* ################################################################
- * #
- * # Config file load
- * #
- * ################################################################ */
- int dynsec_groups__config_load(cJSON *tree)
- {
- cJSON *j_groups, *j_group;
- cJSON *j_clientlist, *j_client, *j_username;
- cJSON *j_roles, *j_role, *j_rolename;
- struct dynsec__group *group;
- struct dynsec__role *role;
- char *str;
- int priority;
- j_groups = cJSON_GetObjectItem(tree, "groups");
- if(j_groups == NULL){
- return 0;
- }
- if(cJSON_IsArray(j_groups) == false){
- return 1;
- }
- cJSON_ArrayForEach(j_group, j_groups){
- if(cJSON_IsObject(j_group) == true){
- group = mosquitto_calloc(1, sizeof(struct dynsec__group));
- if(group == NULL){
- return MOSQ_ERR_NOMEM;
- }
- /* Group name */
- if(json_get_string(j_group, "groupname", &str, false) != MOSQ_ERR_SUCCESS){
- mosquitto_free(group);
- continue;
- }
- group->groupname = strdup(str);
- if(group->groupname == NULL){
- mosquitto_free(group);
- continue;
- }
- /* Text name */
- if(json_get_string(j_group, "textname", &str, false) == MOSQ_ERR_SUCCESS){
- if(str){
- group->text_name = strdup(str);
- if(group->text_name == NULL){
- mosquitto_free(group->groupname);
- mosquitto_free(group);
- continue;
- }
- }
- }
- /* Text description */
- if(json_get_string(j_group, "textdescription", &str, false) == MOSQ_ERR_SUCCESS){
- if(str){
- group->text_description = strdup(str);
- if(group->text_description == NULL){
- mosquitto_free(group->text_name);
- mosquitto_free(group->groupname);
- mosquitto_free(group);
- continue;
- }
- }
- }
- /* Roles */
- j_roles = cJSON_GetObjectItem(j_group, "roles");
- if(j_roles && cJSON_IsArray(j_roles)){
- cJSON_ArrayForEach(j_role, j_roles){
- if(cJSON_IsObject(j_role)){
- j_rolename = cJSON_GetObjectItem(j_role, "rolename");
- if(j_rolename && cJSON_IsString(j_rolename)){
- json_get_int(j_role, "priority", &priority, true, -1);
- role = dynsec_roles__find(j_rolename->valuestring);
- dynsec_rolelist__group_add(group, role, priority);
- }
- }
- }
- }
- /* This must go before clients are loaded, otherwise the group won't be found */
- HASH_ADD_KEYPTR(hh, local_groups, group->groupname, strlen(group->groupname), group);
- /* Clients */
- j_clientlist = cJSON_GetObjectItem(j_group, "clients");
- if(j_clientlist && cJSON_IsArray(j_clientlist)){
- cJSON_ArrayForEach(j_client, j_clientlist){
- if(cJSON_IsObject(j_client)){
- j_username = cJSON_GetObjectItem(j_client, "username");
- if(j_username && cJSON_IsString(j_username)){
- json_get_int(j_client, "priority", &priority, true, -1);
- dynsec_groups__add_client(j_username->valuestring, group->groupname, priority, false);
- }
- }
- }
- }
- }
- }
- HASH_SORT(local_groups, group_cmp);
- j_group = cJSON_GetObjectItem(tree, "anonymousGroup");
- if(j_group && cJSON_IsString(j_group)){
- dynsec_anonymous_group = dynsec_groups__find(j_group->valuestring);
- }
- return 0;
- }
- /* ################################################################
- * #
- * # Config load and save
- * #
- * ################################################################ */
- static int dynsec__config_add_groups(cJSON *j_groups)
- {
- struct dynsec__group *group, *group_tmp = NULL;
- cJSON *j_group, *j_clients, *j_roles;
- HASH_ITER(hh, local_groups, group, group_tmp){
- j_group = cJSON_CreateObject();
- if(j_group == NULL) return 1;
- cJSON_AddItemToArray(j_groups, j_group);
- if(cJSON_AddStringToObject(j_group, "groupname", group->groupname) == NULL
- || (group->text_name && cJSON_AddStringToObject(j_group, "textname", group->text_name) == NULL)
- || (group->text_description && cJSON_AddStringToObject(j_group, "textdescription", group->text_description) == NULL)
- ){
- return 1;
- }
- j_roles = dynsec_rolelist__all_to_json(group->rolelist);
- if(j_roles == NULL){
- return 1;
- }
- cJSON_AddItemToObject(j_group, "roles", j_roles);
- j_clients = dynsec_clientlist__all_to_json(group->clientlist);
- if(j_clients == NULL){
- return 1;
- }
- cJSON_AddItemToObject(j_group, "clients", j_clients);
- }
- return 0;
- }
- int dynsec_groups__config_save(cJSON *tree)
- {
- cJSON *j_groups;
- j_groups = cJSON_CreateArray();
- if(j_groups == NULL){
- return 1;
- }
- cJSON_AddItemToObject(tree, "groups", j_groups);
- if(dynsec__config_add_groups(j_groups)){
- return 1;
- }
- if(dynsec_anonymous_group
- && cJSON_AddStringToObject(tree, "anonymousGroup", dynsec_anonymous_group->groupname) == NULL){
- return 1;
- }
- return 0;
- }
- int dynsec_groups__process_create(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data)
- {
- char *groupname, *text_name, *text_description;
- struct dynsec__group *group = NULL;
- int rc = MOSQ_ERR_SUCCESS;
- const char *admin_clientid, *admin_username;
- if(json_get_string(command, "groupname", &groupname, false) != MOSQ_ERR_SUCCESS){
- dynsec__command_reply(j_responses, context, "createGroup", "Invalid/missing groupname", correlation_data);
- return MOSQ_ERR_INVAL;
- }
- if(mosquitto_validate_utf8(groupname, (int)strlen(groupname)) != MOSQ_ERR_SUCCESS){
- dynsec__command_reply(j_responses, context, "createGroup", "Group name not valid UTF-8", correlation_data);
- return MOSQ_ERR_INVAL;
- }
- if(json_get_string(command, "textname", &text_name, true) != MOSQ_ERR_SUCCESS){
- dynsec__command_reply(j_responses, context, "createGroup", "Invalid/missing textname", correlation_data);
- return MOSQ_ERR_INVAL;
- }
- if(json_get_string(command, "textdescription", &text_description, true) != MOSQ_ERR_SUCCESS){
- dynsec__command_reply(j_responses, context, "createGroup", "Invalid/missing textdescription", correlation_data);
- return MOSQ_ERR_INVAL;
- }
- group = dynsec_groups__find(groupname);
- if(group){
- dynsec__command_reply(j_responses, context, "createGroup", "Group already exists", correlation_data);
- return MOSQ_ERR_SUCCESS;
- }
- group = mosquitto_calloc(1, sizeof(struct dynsec__group));
- if(group == NULL){
- dynsec__command_reply(j_responses, context, "createGroup", "Internal error", correlation_data);
- return MOSQ_ERR_NOMEM;
- }
- group->groupname = strdup(groupname);
- if(group->groupname == NULL){
- dynsec__command_reply(j_responses, context, "createGroup", "Internal error", correlation_data);
- group__free_item(group);
- return MOSQ_ERR_NOMEM;
- }
- if(text_name){
- group->text_name = strdup(text_name);
- if(group->text_name == NULL){
- dynsec__command_reply(j_responses, context, "createGroup", "Internal error", correlation_data);
- group__free_item(group);
- return MOSQ_ERR_NOMEM;
- }
- }
- if(text_description){
- group->text_description = strdup(text_description);
- if(group->text_description == NULL){
- dynsec__command_reply(j_responses, context, "createGroup", "Internal error", correlation_data);
- group__free_item(group);
- return MOSQ_ERR_NOMEM;
- }
- }
- rc = dynsec_rolelist__load_from_json(command, &group->rolelist);
- if(rc == MOSQ_ERR_SUCCESS || rc == ERR_LIST_NOT_FOUND){
- }else if(rc == MOSQ_ERR_NOT_FOUND){
- dynsec__command_reply(j_responses, context, "createGroup", "Role not found", correlation_data);
- group__free_item(group);
- return MOSQ_ERR_INVAL;
- }else{
- dynsec__command_reply(j_responses, context, "createGroup", "Internal error", correlation_data);
- group__free_item(group);
- return MOSQ_ERR_INVAL;
- }
- HASH_ADD_KEYPTR_INORDER(hh, local_groups, group->groupname, strlen(group->groupname), group, group_cmp);
- admin_clientid = mosquitto_client_id(context);
- admin_username = mosquitto_client_username(context);
- mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | createGroup | groupname=%s",
- admin_clientid, admin_username, groupname);
- dynsec__config_save();
- dynsec__command_reply(j_responses, context, "createGroup", NULL, correlation_data);
- return MOSQ_ERR_SUCCESS;
- }
- int dynsec_groups__process_delete(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data)
- {
- char *groupname;
- struct dynsec__group *group;
- const char *admin_clientid, *admin_username;
- if(json_get_string(command, "groupname", &groupname, false) != MOSQ_ERR_SUCCESS){
- dynsec__command_reply(j_responses, context, "deleteGroup", "Invalid/missing groupname", correlation_data);
- return MOSQ_ERR_INVAL;
- }
- if(mosquitto_validate_utf8(groupname, (int)strlen(groupname)) != MOSQ_ERR_SUCCESS){
- dynsec__command_reply(j_responses, context, "deleteGroup", "Group name not valid UTF-8", correlation_data);
- return MOSQ_ERR_INVAL;
- }
- group = dynsec_groups__find(groupname);
- if(group){
- /* Enforce any changes */
- group__kick_all(group);
- dynsec__remove_all_roles_from_group(group);
- group__free_item(group);
- dynsec__config_save();
- dynsec__command_reply(j_responses, context, "deleteGroup", NULL, correlation_data);
- admin_clientid = mosquitto_client_id(context);
- admin_username = mosquitto_client_username(context);
- mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | deleteGroup | groupname=%s",
- admin_clientid, admin_username, groupname);
- return MOSQ_ERR_SUCCESS;
- }else{
- dynsec__command_reply(j_responses, context, "deleteGroup", "Group not found", correlation_data);
- return MOSQ_ERR_SUCCESS;
- }
- }
- int dynsec_groups__add_client(const char *username, const char *groupname, int priority, bool update_config)
- {
- struct dynsec__client *client;
- struct dynsec__clientlist *clientlist;
- struct dynsec__group *group;
- int rc;
- client = dynsec_clients__find(username);
- if(client == NULL){
- return ERR_USER_NOT_FOUND;
- }
- group = dynsec_groups__find(groupname);
- if(group == NULL){
- return ERR_GROUP_NOT_FOUND;
- }
- HASH_FIND(hh, group->clientlist, username, strlen(username), clientlist);
- if(clientlist != NULL){
- /* Client is already in the group */
- return MOSQ_ERR_ALREADY_EXISTS;
- }
- rc = dynsec_clientlist__add(&group->clientlist, client, priority);
- if(rc){
- return rc;
- }
- rc = dynsec_grouplist__add(&client->grouplist, group, priority);
- if(rc){
- dynsec_clientlist__remove(&group->clientlist, client);
- return rc;
- }
- if(update_config){
- dynsec__config_save();
- }
- return MOSQ_ERR_SUCCESS;
- }
- int dynsec_groups__process_add_client(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data)
- {
- char *username, *groupname;
- int rc;
- int priority;
- const char *admin_clientid, *admin_username;
- if(json_get_string(command, "username", &username, false) != MOSQ_ERR_SUCCESS){
- dynsec__command_reply(j_responses, context, "addGroupClient", "Invalid/missing username", correlation_data);
- return MOSQ_ERR_INVAL;
- }
- if(mosquitto_validate_utf8(username, (int)strlen(username)) != MOSQ_ERR_SUCCESS){
- dynsec__command_reply(j_responses, context, "addGroupClient", "Username not valid UTF-8", correlation_data);
- return MOSQ_ERR_INVAL;
- }
- if(json_get_string(command, "groupname", &groupname, false) != MOSQ_ERR_SUCCESS){
- dynsec__command_reply(j_responses, context, "addGroupClient", "Invalid/missing groupname", correlation_data);
- return MOSQ_ERR_INVAL;
- }
- if(mosquitto_validate_utf8(groupname, (int)strlen(groupname)) != MOSQ_ERR_SUCCESS){
- dynsec__command_reply(j_responses, context, "addGroupClient", "Group name not valid UTF-8", correlation_data);
- return MOSQ_ERR_INVAL;
- }
- json_get_int(command, "priority", &priority, true, -1);
- rc = dynsec_groups__add_client(username, groupname, priority, true);
- if(rc == MOSQ_ERR_SUCCESS){
- admin_clientid = mosquitto_client_id(context);
- admin_username = mosquitto_client_username(context);
- mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | addGroupClient | groupname=%s | username=%s | priority=%d",
- admin_clientid, admin_username, groupname, username, priority);
- dynsec__command_reply(j_responses, context, "addGroupClient", NULL, correlation_data);
- }else if(rc == ERR_USER_NOT_FOUND){
- dynsec__command_reply(j_responses, context, "addGroupClient", "Client not found", correlation_data);
- }else if(rc == ERR_GROUP_NOT_FOUND){
- dynsec__command_reply(j_responses, context, "addGroupClient", "Group not found", correlation_data);
- }else if(rc == MOSQ_ERR_ALREADY_EXISTS){
- dynsec__command_reply(j_responses, context, "addGroupClient", "Client is already in this group", correlation_data);
- }else{
- dynsec__command_reply(j_responses, context, "addGroupClient", "Internal error", correlation_data);
- }
- /* Enforce any changes */
- mosquitto_kick_client_by_username(username, false);
- return rc;
- }
- static int dynsec__remove_all_clients_from_group(struct dynsec__group *group)
- {
- struct dynsec__clientlist *clientlist, *clientlist_tmp = NULL;
- HASH_ITER(hh, group->clientlist, clientlist, clientlist_tmp){
- /* Remove client stored group reference */
- dynsec_grouplist__remove(&clientlist->client->grouplist, group);
- HASH_DELETE(hh, group->clientlist, clientlist);
- mosquitto_free(clientlist);
- }
- return MOSQ_ERR_SUCCESS;
- }
- static int dynsec__remove_all_roles_from_group(struct dynsec__group *group)
- {
- struct dynsec__rolelist *rolelist, *rolelist_tmp = NULL;
- HASH_ITER(hh, group->rolelist, rolelist, rolelist_tmp){
- dynsec_rolelist__group_remove(group, rolelist->role);
- }
- return MOSQ_ERR_SUCCESS;
- }
- int dynsec_groups__remove_client(const char *username, const char *groupname, bool update_config)
- {
- struct dynsec__client *client;
- struct dynsec__group *group;
- client = dynsec_clients__find(username);
- if(client == NULL){
- return ERR_USER_NOT_FOUND;
- }
- group = dynsec_groups__find(groupname);
- if(group == NULL){
- return ERR_GROUP_NOT_FOUND;
- }
- dynsec_clientlist__remove(&group->clientlist, client);
- dynsec_grouplist__remove(&client->grouplist, group);
- if(update_config){
- dynsec__config_save();
- }
- return MOSQ_ERR_SUCCESS;
- }
- int dynsec_groups__process_remove_client(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data)
- {
- char *username, *groupname;
- int rc;
- const char *admin_clientid, *admin_username;
- if(json_get_string(command, "username", &username, false) != MOSQ_ERR_SUCCESS){
- dynsec__command_reply(j_responses, context, "removeGroupClient", "Invalid/missing username", correlation_data);
- return MOSQ_ERR_INVAL;
- }
- if(mosquitto_validate_utf8(username, (int)strlen(username)) != MOSQ_ERR_SUCCESS){
- dynsec__command_reply(j_responses, context, "removeGroupClient", "Username not valid UTF-8", correlation_data);
- return MOSQ_ERR_INVAL;
- }
- if(json_get_string(command, "groupname", &groupname, false) != MOSQ_ERR_SUCCESS){
- dynsec__command_reply(j_responses, context, "removeGroupClient", "Invalid/missing groupname", correlation_data);
- return MOSQ_ERR_INVAL;
- }
- if(mosquitto_validate_utf8(groupname, (int)strlen(groupname)) != MOSQ_ERR_SUCCESS){
- dynsec__command_reply(j_responses, context, "removeGroupClient", "Group name not valid UTF-8", correlation_data);
- return MOSQ_ERR_INVAL;
- }
- rc = dynsec_groups__remove_client(username, groupname, true);
- if(rc == MOSQ_ERR_SUCCESS){
- admin_clientid = mosquitto_client_id(context);
- admin_username = mosquitto_client_username(context);
- mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | removeGroupClient | groupname=%s | username=%s",
- admin_clientid, admin_username, groupname, username);
- dynsec__command_reply(j_responses, context, "removeGroupClient", NULL, correlation_data);
- }else if(rc == ERR_USER_NOT_FOUND){
- dynsec__command_reply(j_responses, context, "removeGroupClient", "Client not found", correlation_data);
- }else if(rc == ERR_GROUP_NOT_FOUND){
- dynsec__command_reply(j_responses, context, "removeGroupClient", "Group not found", correlation_data);
- }else{
- dynsec__command_reply(j_responses, context, "removeGroupClient", "Internal error", correlation_data);
- }
- /* Enforce any changes */
- mosquitto_kick_client_by_username(username, false);
- return rc;
- }
- static cJSON *add_group_to_json(struct dynsec__group *group)
- {
- cJSON *j_group, *jtmp, *j_clientlist, *j_client, *j_rolelist;
- struct dynsec__clientlist *clientlist, *clientlist_tmp = NULL;
- j_group = cJSON_CreateObject();
- if(j_group == NULL){
- return NULL;
- }
- if(cJSON_AddStringToObject(j_group, "groupname", group->groupname) == NULL
- || (group->text_name && cJSON_AddStringToObject(j_group, "textname", group->text_name) == NULL)
- || (group->text_description && cJSON_AddStringToObject(j_group, "textdescription", group->text_description) == NULL)
- || (j_clientlist = cJSON_AddArrayToObject(j_group, "clients")) == NULL
- ){
- cJSON_Delete(j_group);
- return NULL;
- }
- HASH_ITER(hh, group->clientlist, clientlist, clientlist_tmp){
- j_client = cJSON_CreateObject();
- if(j_client == NULL){
- cJSON_Delete(j_group);
- return NULL;
- }
- cJSON_AddItemToArray(j_clientlist, j_client);
- jtmp = cJSON_CreateStringReference(clientlist->client->username);
- if(jtmp == NULL){
- cJSON_Delete(j_group);
- return NULL;
- }
- cJSON_AddItemToObject(j_client, "username", jtmp);
- }
- j_rolelist = dynsec_rolelist__all_to_json(group->rolelist);
- if(j_rolelist == NULL){
- cJSON_Delete(j_group);
- return NULL;
- }
- cJSON_AddItemToObject(j_group, "roles", j_rolelist);
- return j_group;
- }
- int dynsec_groups__process_list(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data)
- {
- bool verbose;
- cJSON *tree, *j_groups, *j_group, *j_data;
- struct dynsec__group *group, *group_tmp = NULL;
- int i, count, offset;
- const char *admin_clientid, *admin_username;
- json_get_bool(command, "verbose", &verbose, true, false);
- json_get_int(command, "count", &count, true, -1);
- json_get_int(command, "offset", &offset, true, 0);
- tree = cJSON_CreateObject();
- if(tree == NULL){
- dynsec__command_reply(j_responses, context, "listGroups", "Internal error", correlation_data);
- return MOSQ_ERR_NOMEM;
- }
- if(cJSON_AddStringToObject(tree, "command", "listGroups") == NULL
- || (j_data = cJSON_AddObjectToObject(tree, "data")) == NULL
- || cJSON_AddIntToObject(j_data, "totalCount", (int)HASH_CNT(hh, local_groups)) == NULL
- || (j_groups = cJSON_AddArrayToObject(j_data, "groups")) == NULL
- || (correlation_data && cJSON_AddStringToObject(tree, "correlationData", correlation_data) == NULL)
- ){
- cJSON_Delete(tree);
- dynsec__command_reply(j_responses, context, "listGroups", "Internal error", correlation_data);
- return MOSQ_ERR_NOMEM;
- }
- i = 0;
- HASH_ITER(hh, local_groups, group, group_tmp){
- if(i>=offset){
- if(verbose){
- j_group = add_group_to_json(group);
- if(j_group == NULL){
- cJSON_Delete(tree);
- dynsec__command_reply(j_responses, context, "listGroups", "Internal error", correlation_data);
- return MOSQ_ERR_NOMEM;
- }
- cJSON_AddItemToArray(j_groups, j_group);
- }else{
- j_group = cJSON_CreateString(group->groupname);
- if(j_group){
- cJSON_AddItemToArray(j_groups, j_group);
- }else{
- cJSON_Delete(tree);
- dynsec__command_reply(j_responses, context, "listGroups", "Internal error", correlation_data);
- return MOSQ_ERR_NOMEM;
- }
- }
- if(count >= 0){
- count--;
- if(count <= 0){
- break;
- }
- }
- }
- i++;
- }
- cJSON_AddItemToArray(j_responses, tree);
- admin_clientid = mosquitto_client_id(context);
- admin_username = mosquitto_client_username(context);
- mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | listGroups | verbose=%s | count=%d | offset=%d",
- admin_clientid, admin_username, verbose?"true":"false", count, offset);
- return MOSQ_ERR_SUCCESS;
- }
- int dynsec_groups__process_get(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data)
- {
- char *groupname;
- cJSON *tree, *j_group, *j_data;
- struct dynsec__group *group;
- const char *admin_clientid, *admin_username;
- if(json_get_string(command, "groupname", &groupname, false) != MOSQ_ERR_SUCCESS){
- dynsec__command_reply(j_responses, context, "getGroup", "Invalid/missing groupname", correlation_data);
- return MOSQ_ERR_INVAL;
- }
- if(mosquitto_validate_utf8(groupname, (int)strlen(groupname)) != MOSQ_ERR_SUCCESS){
- dynsec__command_reply(j_responses, context, "getGroup", "Group name not valid UTF-8", correlation_data);
- return MOSQ_ERR_INVAL;
- }
- tree = cJSON_CreateObject();
- if(tree == NULL){
- dynsec__command_reply(j_responses, context, "getGroup", "Internal error", correlation_data);
- return MOSQ_ERR_NOMEM;
- }
- if(cJSON_AddStringToObject(tree, "command", "getGroup") == NULL
- || (j_data = cJSON_AddObjectToObject(tree, "data")) == NULL
- || (correlation_data && cJSON_AddStringToObject(tree, "correlationData", correlation_data) == NULL)
- ){
- cJSON_Delete(tree);
- dynsec__command_reply(j_responses, context, "getGroup", "Internal error", correlation_data);
- return MOSQ_ERR_NOMEM;
- }
- group = dynsec_groups__find(groupname);
- if(group){
- j_group = add_group_to_json(group);
- if(j_group == NULL){
- cJSON_Delete(tree);
- dynsec__command_reply(j_responses, context, "getGroup", "Internal error", correlation_data);
- return MOSQ_ERR_NOMEM;
- }
- cJSON_AddItemToObject(j_data, "group", j_group);
- }else{
- cJSON_Delete(tree);
- dynsec__command_reply(j_responses, context, "getGroup", "Group not found", correlation_data);
- return MOSQ_ERR_NOMEM;
- }
- cJSON_AddItemToArray(j_responses, tree);
- admin_clientid = mosquitto_client_id(context);
- admin_username = mosquitto_client_username(context);
- mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | getGroup | groupname=%s",
- admin_clientid, admin_username, groupname);
- return MOSQ_ERR_SUCCESS;
- }
- int dynsec_groups__process_remove_role(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data)
- {
- char *groupname, *rolename;
- struct dynsec__group *group;
- struct dynsec__role *role;
- const char *admin_clientid, *admin_username;
- if(json_get_string(command, "groupname", &groupname, false) != MOSQ_ERR_SUCCESS){
- dynsec__command_reply(j_responses, context, "removeGroupRole", "Invalid/missing groupname", correlation_data);
- return MOSQ_ERR_INVAL;
- }
- if(mosquitto_validate_utf8(groupname, (int)strlen(groupname)) != MOSQ_ERR_SUCCESS){
- dynsec__command_reply(j_responses, context, "removeGroupRole", "Group name not valid UTF-8", correlation_data);
- return MOSQ_ERR_INVAL;
- }
- if(json_get_string(command, "rolename", &rolename, false) != MOSQ_ERR_SUCCESS){
- dynsec__command_reply(j_responses, context, "removeGroupRole", "Invalid/missing rolename", correlation_data);
- return MOSQ_ERR_INVAL;
- }
- if(mosquitto_validate_utf8(rolename, (int)strlen(rolename)) != MOSQ_ERR_SUCCESS){
- dynsec__command_reply(j_responses, context, "removeGroupRole", "Role name not valid UTF-8", correlation_data);
- return MOSQ_ERR_INVAL;
- }
- group = dynsec_groups__find(groupname);
- if(group == NULL){
- dynsec__command_reply(j_responses, context, "removeGroupRole", "Group not found", correlation_data);
- return MOSQ_ERR_SUCCESS;
- }
- role = dynsec_roles__find(rolename);
- if(role == NULL){
- dynsec__command_reply(j_responses, context, "removeGroupRole", "Role not found", correlation_data);
- return MOSQ_ERR_SUCCESS;
- }
- dynsec_rolelist__group_remove(group, role);
- dynsec__config_save();
- dynsec__command_reply(j_responses, context, "removeGroupRole", NULL, correlation_data);
- /* Enforce any changes */
- group__kick_all(group);
- admin_clientid = mosquitto_client_id(context);
- admin_username = mosquitto_client_username(context);
- mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | removeGroupRole | groupname=%s | rolename=%s",
- admin_clientid, admin_username, groupname, rolename);
- return MOSQ_ERR_SUCCESS;
- }
- int dynsec_groups__process_modify(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data)
- {
- char *groupname;
- char *text_name, *text_description;
- struct dynsec__group *group;
- struct dynsec__rolelist *rolelist = NULL;
- char *str;
- int rc;
- int priority;
- cJSON *j_client, *j_clients, *jtmp;
- const char *admin_clientid, *admin_username;
- if(json_get_string(command, "groupname", &groupname, false) != MOSQ_ERR_SUCCESS){
- dynsec__command_reply(j_responses, context, "modifyGroup", "Invalid/missing groupname", correlation_data);
- return MOSQ_ERR_INVAL;
- }
- if(mosquitto_validate_utf8(groupname, (int)strlen(groupname)) != MOSQ_ERR_SUCCESS){
- dynsec__command_reply(j_responses, context, "modifyGroup", "Group name not valid UTF-8", correlation_data);
- return MOSQ_ERR_INVAL;
- }
- group = dynsec_groups__find(groupname);
- if(group == NULL){
- dynsec__command_reply(j_responses, context, "modifyGroup", "Group not found", correlation_data);
- return MOSQ_ERR_INVAL;
- }
- if(json_get_string(command, "textname", &text_name, false) == MOSQ_ERR_SUCCESS){
- str = mosquitto_strdup(text_name);
- if(str == NULL){
- dynsec__command_reply(j_responses, context, "modifyGroup", "Internal error", correlation_data);
- return MOSQ_ERR_NOMEM;
- }
- mosquitto_free(group->text_name);
- group->text_name = str;
- }
- if(json_get_string(command, "textdescription", &text_description, false) == MOSQ_ERR_SUCCESS){
- str = mosquitto_strdup(text_description);
- if(str == NULL){
- dynsec__command_reply(j_responses, context, "modifyGroup", "Internal error", correlation_data);
- return MOSQ_ERR_NOMEM;
- }
- mosquitto_free(group->text_description);
- group->text_description = str;
- }
- rc = dynsec_rolelist__load_from_json(command, &rolelist);
- if(rc == MOSQ_ERR_SUCCESS){
- dynsec_rolelist__cleanup(&group->rolelist);
- group->rolelist = rolelist;
- }else if(rc == ERR_LIST_NOT_FOUND){
- /* There was no list in the JSON, so no modification */
- }else if(rc == MOSQ_ERR_NOT_FOUND){
- dynsec__command_reply(j_responses, context, "modifyGroup", "Role not found", correlation_data);
- dynsec_rolelist__cleanup(&rolelist);
- group__kick_all(group);
- return MOSQ_ERR_INVAL;
- }else{
- if(rc == MOSQ_ERR_INVAL){
- dynsec__command_reply(j_responses, context, "modifyGroup", "'roles' not an array or missing/invalid rolename", correlation_data);
- }else{
- dynsec__command_reply(j_responses, context, "modifyGroup", "Internal error", correlation_data);
- }
- dynsec_rolelist__cleanup(&rolelist);
- group__kick_all(group);
- return MOSQ_ERR_INVAL;
- }
- j_clients = cJSON_GetObjectItem(command, "clients");
- if(j_clients && cJSON_IsArray(j_clients)){
- dynsec__remove_all_clients_from_group(group);
- cJSON_ArrayForEach(j_client, j_clients){
- if(cJSON_IsObject(j_client)){
- jtmp = cJSON_GetObjectItem(j_client, "username");
- if(jtmp && cJSON_IsString(jtmp)){
- json_get_int(j_client, "priority", &priority, true, -1);
- dynsec_groups__add_client(jtmp->valuestring, groupname, priority, false);
- }
- }
- }
- }
- dynsec__config_save();
- dynsec__command_reply(j_responses, context, "modifyGroup", NULL, correlation_data);
- /* Enforce any changes */
- group__kick_all(group);
- admin_clientid = mosquitto_client_id(context);
- admin_username = mosquitto_client_username(context);
- mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | modifyGroup | groupname=%s",
- admin_clientid, admin_username, groupname);
- return MOSQ_ERR_SUCCESS;
- }
- int dynsec_groups__process_set_anonymous_group(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data)
- {
- char *groupname;
- struct dynsec__group *group = NULL;
- const char *admin_clientid, *admin_username;
- if(json_get_string(command, "groupname", &groupname, false) != MOSQ_ERR_SUCCESS){
- dynsec__command_reply(j_responses, context, "setAnonymousGroup", "Invalid/missing groupname", correlation_data);
- return MOSQ_ERR_INVAL;
- }
- if(mosquitto_validate_utf8(groupname, (int)strlen(groupname)) != MOSQ_ERR_SUCCESS){
- dynsec__command_reply(j_responses, context, "setAnonymousGroup", "Group name not valid UTF-8", correlation_data);
- return MOSQ_ERR_INVAL;
- }
- group = dynsec_groups__find(groupname);
- if(group == NULL){
- dynsec__command_reply(j_responses, context, "setAnonymousGroup", "Group not found", correlation_data);
- return MOSQ_ERR_SUCCESS;
- }
- dynsec_anonymous_group = group;
- dynsec__config_save();
- dynsec__command_reply(j_responses, context, "setAnonymousGroup", NULL, correlation_data);
- /* Enforce any changes */
- mosquitto_kick_client_by_username(NULL, false);
- admin_clientid = mosquitto_client_id(context);
- admin_username = mosquitto_client_username(context);
- mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | setAnonymousGroup | groupname=%s",
- admin_clientid, admin_username, groupname);
- return MOSQ_ERR_SUCCESS;
- }
- int dynsec_groups__process_get_anonymous_group(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data)
- {
- cJSON *tree, *j_data, *j_group;
- const char *groupname;
- const char *admin_clientid, *admin_username;
- UNUSED(command);
- tree = cJSON_CreateObject();
- if(tree == NULL){
- dynsec__command_reply(j_responses, context, "getAnonymousGroup", "Internal error", correlation_data);
- return MOSQ_ERR_NOMEM;
- }
- if(dynsec_anonymous_group){
- groupname = dynsec_anonymous_group->groupname;
- }else{
- groupname = "";
- }
- if(cJSON_AddStringToObject(tree, "command", "getAnonymousGroup") == NULL
- || (j_data = cJSON_AddObjectToObject(tree, "data")) == NULL
- || (j_group = cJSON_AddObjectToObject(j_data, "group")) == NULL
- || cJSON_AddStringToObject(j_group, "groupname", groupname) == NULL
- || (correlation_data && cJSON_AddStringToObject(tree, "correlationData", correlation_data) == NULL)
- ){
- cJSON_Delete(tree);
- dynsec__command_reply(j_responses, context, "getAnonymousGroup", "Internal error", correlation_data);
- return MOSQ_ERR_NOMEM;
- }
- cJSON_AddItemToArray(j_responses, tree);
- admin_clientid = mosquitto_client_id(context);
- admin_username = mosquitto_client_username(context);
- mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | getAnonymousGroup",
- admin_clientid, admin_username);
- return MOSQ_ERR_SUCCESS;
- }
|