Move config load/save to own file.

This commit is contained in:
Roger A. Light
2021-12-13 09:14:05 +00:00
parent ff41157c2c
commit 17fd1929eb
5 changed files with 239 additions and 199 deletions

View File

@@ -22,6 +22,7 @@ if(CJSON_FOUND AND WITH_TLS)
../../lib/base64_mosq.c ../../lib/base64_mosq.h
clients.c
clientlist.c
config.c
config_init.c
dynamic_security.h
groups.c

View File

@@ -14,6 +14,7 @@ OBJS= \
base64_mosq.o \
clients.o \
clientlist.o \
config.o \
config_init.o \
groups.o \
grouplist.o \
@@ -56,6 +57,9 @@ clients.o : clients.c dynamic_security.h
clientlist.o : clientlist.c dynamic_security.h
${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(PLUGIN_CPPFLAGS) $(PLUGIN_CFLAGS) -c $< -o $@
config.o : config.c dynamic_security.h
${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(PLUGIN_CPPFLAGS) $(PLUGIN_CFLAGS) -c $< -o $@
config_init.o : config_init.c dynamic_security.h
${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(PLUGIN_CPPFLAGS) $(PLUGIN_CFLAGS) -c $< -o $@

View File

@@ -0,0 +1,226 @@
/*
Copyright (c) 2020-2021 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 <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include "json_help.h"
#include "mosquitto.h"
#include "mosquitto_broker.h"
#include "mosquitto_plugin.h"
#include "mqtt_protocol.h"
#include "dynamic_security.h"
static int dynsec__general_config_load(cJSON *tree)
{
cJSON *j_default_access, *jtmp;
j_default_access = cJSON_GetObjectItem(tree, "defaultACLAccess");
if(j_default_access && cJSON_IsObject(j_default_access)){
jtmp = cJSON_GetObjectItem(j_default_access, ACL_TYPE_PUB_C_SEND);
if(jtmp && cJSON_IsBool(jtmp)){
default_access.publish_c_send = cJSON_IsTrue(jtmp);
}else{
default_access.publish_c_send = false;
}
jtmp = cJSON_GetObjectItem(j_default_access, ACL_TYPE_PUB_C_RECV);
if(jtmp && cJSON_IsBool(jtmp)){
default_access.publish_c_recv = cJSON_IsTrue(jtmp);
}else{
default_access.publish_c_recv = false;
}
jtmp = cJSON_GetObjectItem(j_default_access, ACL_TYPE_SUB_GENERIC);
if(jtmp && cJSON_IsBool(jtmp)){
default_access.subscribe = cJSON_IsTrue(jtmp);
}else{
default_access.subscribe = false;
}
jtmp = cJSON_GetObjectItem(j_default_access, ACL_TYPE_UNSUB_GENERIC);
if(jtmp && cJSON_IsBool(jtmp)){
default_access.unsubscribe = cJSON_IsTrue(jtmp);
}else{
default_access.unsubscribe = false;
}
}
return MOSQ_ERR_SUCCESS;
}
static int dynsec__general_config_save(cJSON *tree)
{
cJSON *j_default_access;
j_default_access = cJSON_CreateObject();
if(j_default_access == NULL){
return 1;
}
cJSON_AddItemToObject(tree, "defaultACLAccess", j_default_access);
if(cJSON_AddBoolToObject(j_default_access, ACL_TYPE_PUB_C_SEND, default_access.publish_c_send) == NULL
|| cJSON_AddBoolToObject(j_default_access, ACL_TYPE_PUB_C_RECV, default_access.publish_c_recv) == NULL
|| cJSON_AddBoolToObject(j_default_access, ACL_TYPE_SUB_GENERIC, default_access.subscribe) == NULL
|| cJSON_AddBoolToObject(j_default_access, ACL_TYPE_UNSUB_GENERIC, default_access.unsubscribe) == NULL
){
return 1;
}
return MOSQ_ERR_SUCCESS;
}
int dynsec__config_load(void)
{
FILE *fptr;
long flen_l;
size_t flen;
char *json_str;
cJSON *tree;
/* Load from file */
fptr = fopen(g_config_file, "rb");
if(fptr == NULL){
/* Attempt to initialise a new config file */
if(dynsec__config_init(g_config_file) == MOSQ_ERR_SUCCESS){
mosquitto_log_printf(MOSQ_LOG_INFO, "Dynamic security plugin config not found, generating a default config.");
mosquitto_log_printf(MOSQ_LOG_INFO, " Generated passwords are at %s.pw", g_config_file);
/* If it works, try to open the file again */
fptr = fopen(g_config_file, "rb");
}
if(fptr == NULL){
mosquitto_log_printf(MOSQ_LOG_ERR,
"Error loading Dynamic security plugin config: File is not readable - check permissions.");
return MOSQ_ERR_UNKNOWN;
}
}
fseek(fptr, 0, SEEK_END);
flen_l = ftell(fptr);
if(flen_l < 0){
mosquitto_log_printf(MOSQ_LOG_ERR, "Error loading Dynamic security plugin config: %s", strerror(errno));
fclose(fptr);
return 1;
}else if(flen_l == 0){
fclose(fptr);
return 0;
}
flen = (size_t)flen_l;
fseek(fptr, 0, SEEK_SET);
json_str = mosquitto_calloc(flen+1, sizeof(char));
if(json_str == NULL){
mosquitto_log_printf(MOSQ_LOG_ERR, "Error: Out of memory.");
fclose(fptr);
return 1;
}
if(fread(json_str, 1, flen, fptr) != flen){
mosquitto_log_printf(MOSQ_LOG_WARNING, "Error loading Dynamic security plugin config: Unable to read file contents.\n");
mosquitto_free(json_str);
fclose(fptr);
return 1;
}
fclose(fptr);
tree = cJSON_Parse(json_str);
mosquitto_free(json_str);
if(tree == NULL){
mosquitto_log_printf(MOSQ_LOG_ERR, "Error loading Dynamic security plugin config: File is not valid JSON.\n");
return 1;
}
if(dynsec__general_config_load(tree)
|| dynsec_roles__config_load(tree)
|| dynsec_clients__config_load(tree)
|| dynsec_groups__config_load(tree)
){
cJSON_Delete(tree);
return 1;
}
cJSON_Delete(tree);
return 0;
}
void dynsec__config_save(void)
{
cJSON *tree;
size_t file_path_len;
char *file_path;
FILE *fptr;
size_t json_str_len;
char *json_str;
tree = cJSON_CreateObject();
if(tree == NULL) return;
if(dynsec__general_config_save(tree)
|| dynsec_clients__config_save(tree)
|| dynsec_groups__config_save(tree)
|| dynsec_roles__config_save(tree)){
cJSON_Delete(tree);
return;
}
/* Print json to string */
json_str = cJSON_Print(tree);
if(json_str == NULL){
cJSON_Delete(tree);
mosquitto_log_printf(MOSQ_LOG_ERR, "Error saving Dynamic security plugin config: Out of memory.\n");
return;
}
cJSON_Delete(tree);
json_str_len = strlen(json_str);
/* Save to file */
file_path_len = strlen(g_config_file) + 1;
file_path = mosquitto_malloc(file_path_len);
if(file_path == NULL){
mosquitto_free(json_str);
mosquitto_log_printf(MOSQ_LOG_ERR, "Error saving Dynamic security plugin config: Out of memory.\n");
return;
}
snprintf(file_path, file_path_len, "%s.new", g_config_file);
fptr = fopen(file_path, "wt");
if(fptr == NULL){
mosquitto_free(json_str);
mosquitto_free(file_path);
mosquitto_log_printf(MOSQ_LOG_ERR, "Error saving Dynamic security plugin config: File is not writable - check permissions.\n");
return;
}
fwrite(json_str, 1, json_str_len, fptr);
mosquitto_free(json_str);
fclose(fptr);
/* Everything is ok, so move new file over proper file */
if(rename(file_path, g_config_file) < 0){
mosquitto_log_printf(MOSQ_LOG_ERR, "Error updating dynsec config file: %s", strerror(errno));
}
mosquitto_free(file_path);
}

View File

@@ -132,6 +132,7 @@ struct dynsec__acl_default_access{
extern struct dynsec__group *dynsec_anonymous_group;
extern struct dynsec__acl_default_access default_access;
extern char *g_config_file;
/* ################################################################
* #
@@ -141,6 +142,7 @@ extern struct dynsec__acl_default_access default_access;
int dynsec__config_init(const char *filename);
void dynsec__config_save(void);
int dynsec__config_load(void);
int dynsec__handle_control(cJSON *j_responses, 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);

View File

@@ -36,7 +36,7 @@ Contributors:
MOSQUITTO_PLUGIN_DECLARE_VERSION(5);
static mosquitto_plugin_id_t *plg_id = NULL;
static char *config_file = NULL;
char *g_config_file = NULL;
struct dynsec__acl_default_access default_access = {false, false, false, false};
void dynsec__command_reply(cJSON *j_responses, struct mosquitto *context, const char *command, const char *error, const char *correlation_data)
@@ -276,199 +276,6 @@ internal_error:
}
static int dynsec__general_config_load(cJSON *tree)
{
cJSON *j_default_access, *jtmp;
j_default_access = cJSON_GetObjectItem(tree, "defaultACLAccess");
if(j_default_access && cJSON_IsObject(j_default_access)){
jtmp = cJSON_GetObjectItem(j_default_access, ACL_TYPE_PUB_C_SEND);
if(jtmp && cJSON_IsBool(jtmp)){
default_access.publish_c_send = cJSON_IsTrue(jtmp);
}else{
default_access.publish_c_send = false;
}
jtmp = cJSON_GetObjectItem(j_default_access, ACL_TYPE_PUB_C_RECV);
if(jtmp && cJSON_IsBool(jtmp)){
default_access.publish_c_recv = cJSON_IsTrue(jtmp);
}else{
default_access.publish_c_recv = false;
}
jtmp = cJSON_GetObjectItem(j_default_access, ACL_TYPE_SUB_GENERIC);
if(jtmp && cJSON_IsBool(jtmp)){
default_access.subscribe = cJSON_IsTrue(jtmp);
}else{
default_access.subscribe = false;
}
jtmp = cJSON_GetObjectItem(j_default_access, ACL_TYPE_UNSUB_GENERIC);
if(jtmp && cJSON_IsBool(jtmp)){
default_access.unsubscribe = cJSON_IsTrue(jtmp);
}else{
default_access.unsubscribe = false;
}
}
return MOSQ_ERR_SUCCESS;
}
static int dynsec__general_config_save(cJSON *tree)
{
cJSON *j_default_access;
j_default_access = cJSON_CreateObject();
if(j_default_access == NULL){
return 1;
}
cJSON_AddItemToObject(tree, "defaultACLAccess", j_default_access);
if(cJSON_AddBoolToObject(j_default_access, ACL_TYPE_PUB_C_SEND, default_access.publish_c_send) == NULL
|| cJSON_AddBoolToObject(j_default_access, ACL_TYPE_PUB_C_RECV, default_access.publish_c_recv) == NULL
|| cJSON_AddBoolToObject(j_default_access, ACL_TYPE_SUB_GENERIC, default_access.subscribe) == NULL
|| cJSON_AddBoolToObject(j_default_access, ACL_TYPE_UNSUB_GENERIC, default_access.unsubscribe) == NULL
){
return 1;
}
return MOSQ_ERR_SUCCESS;
}
static int dynsec__config_load(void)
{
FILE *fptr;
long flen_l;
size_t flen;
char *json_str;
cJSON *tree;
/* Load from file */
fptr = fopen(config_file, "rb");
if(fptr == NULL){
/* Attempt to initialise a new config file */
if(dynsec__config_init(config_file) == MOSQ_ERR_SUCCESS){
mosquitto_log_printf(MOSQ_LOG_INFO, "Dynamic security plugin config not found, generating a default config.");
mosquitto_log_printf(MOSQ_LOG_INFO, " Generated passwords are at %s.pw", config_file);
/* If it works, try to open the file again */
fptr = fopen(config_file, "rb");
}
if(fptr == NULL){
mosquitto_log_printf(MOSQ_LOG_ERR,
"Error loading Dynamic security plugin config: File is not readable - check permissions.");
return MOSQ_ERR_UNKNOWN;
}
}
fseek(fptr, 0, SEEK_END);
flen_l = ftell(fptr);
if(flen_l < 0){
mosquitto_log_printf(MOSQ_LOG_ERR, "Error loading Dynamic security plugin config: %s", strerror(errno));
fclose(fptr);
return 1;
}else if(flen_l == 0){
fclose(fptr);
return 0;
}
flen = (size_t)flen_l;
fseek(fptr, 0, SEEK_SET);
json_str = mosquitto_calloc(flen+1, sizeof(char));
if(json_str == NULL){
mosquitto_log_printf(MOSQ_LOG_ERR, "Error: Out of memory.");
fclose(fptr);
return 1;
}
if(fread(json_str, 1, flen, fptr) != flen){
mosquitto_log_printf(MOSQ_LOG_WARNING, "Error loading Dynamic security plugin config: Unable to read file contents.\n");
mosquitto_free(json_str);
fclose(fptr);
return 1;
}
fclose(fptr);
tree = cJSON_Parse(json_str);
mosquitto_free(json_str);
if(tree == NULL){
mosquitto_log_printf(MOSQ_LOG_ERR, "Error loading Dynamic security plugin config: File is not valid JSON.\n");
return 1;
}
if(dynsec__general_config_load(tree)
|| dynsec_roles__config_load(tree)
|| dynsec_clients__config_load(tree)
|| dynsec_groups__config_load(tree)
){
cJSON_Delete(tree);
return 1;
}
cJSON_Delete(tree);
return 0;
}
void dynsec__config_save(void)
{
cJSON *tree;
size_t file_path_len;
char *file_path;
FILE *fptr;
size_t json_str_len;
char *json_str;
tree = cJSON_CreateObject();
if(tree == NULL) return;
if(dynsec__general_config_save(tree)
|| dynsec_clients__config_save(tree)
|| dynsec_groups__config_save(tree)
|| dynsec_roles__config_save(tree)){
cJSON_Delete(tree);
return;
}
/* Print json to string */
json_str = cJSON_Print(tree);
if(json_str == NULL){
cJSON_Delete(tree);
mosquitto_log_printf(MOSQ_LOG_ERR, "Error saving Dynamic security plugin config: Out of memory.\n");
return;
}
cJSON_Delete(tree);
json_str_len = strlen(json_str);
/* Save to file */
file_path_len = strlen(config_file) + 1;
file_path = mosquitto_malloc(file_path_len);
if(file_path == NULL){
mosquitto_free(json_str);
mosquitto_log_printf(MOSQ_LOG_ERR, "Error saving Dynamic security plugin config: Out of memory.\n");
return;
}
snprintf(file_path, file_path_len, "%s.new", config_file);
fptr = fopen(file_path, "wt");
if(fptr == NULL){
mosquitto_free(json_str);
mosquitto_free(file_path);
mosquitto_log_printf(MOSQ_LOG_ERR, "Error saving Dynamic security plugin config: File is not writable - check permissions.\n");
return;
}
fwrite(json_str, 1, json_str_len, fptr);
mosquitto_free(json_str);
fclose(fptr);
/* Everything is ok, so move new file over proper file */
if(rename(file_path, config_file) < 0){
mosquitto_log_printf(MOSQ_LOG_ERR, "Error updating dynsec config file: %s", strerror(errno));
}
mosquitto_free(file_path);
}
int mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **user_data, struct mosquitto_opt *options, int option_count)
{
int i;
@@ -477,14 +284,14 @@ int mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **user_data, s
for(i=0; i<option_count; i++){
if(!strcasecmp(options[i].key, "config_file")){
config_file = mosquitto_strdup(options[i].value);
if(config_file == NULL){
g_config_file = mosquitto_strdup(options[i].value);
if(g_config_file == NULL){
return MOSQ_ERR_NOMEM;
}
break;
}
}
if(config_file == NULL){
if(g_config_file == NULL){
mosquitto_log_printf(MOSQ_LOG_WARNING, "Warning: Dynamic security plugin has no plugin_opt_config_file defined. The plugin will not be activated.");
return MOSQ_ERR_SUCCESS;
}
@@ -510,8 +317,8 @@ int mosquitto_plugin_cleanup(void *user_data, struct mosquitto_opt *options, int
dynsec_clients__cleanup();
dynsec_roles__cleanup();
mosquitto_free(config_file);
config_file = NULL;
mosquitto_free(g_config_file);
g_config_file = NULL;
return MOSQ_ERR_SUCCESS;
}