Coverage Report

Created: 2025-08-29 06:24

/src/mosquitto/plugins/dynamic-security/config.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
Copyright (c) 2020-2021 Roger Light <roger@atchoo.org>
3
4
All rights reserved. This program and the accompanying materials
5
are made available under the terms of the Eclipse Public License 2.0
6
and Eclipse Distribution License v1.0 which accompany this distribution.
7
8
The Eclipse Public License is available at
9
   https://www.eclipse.org/legal/epl-2.0/
10
and the Eclipse Distribution License is available at
11
  http://www.eclipse.org/org/documents/edl-v10.php.
12
13
SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
14
15
Contributors:
16
   Roger Light - initial implementation and documentation.
17
*/
18
19
#include "config.h"
20
21
#include <cjson/cJSON.h>
22
#include <errno.h>
23
#include <stdio.h>
24
#include <stdlib.h>
25
#include <string.h>
26
#include <sys/stat.h>
27
28
#include "dynamic_security.h"
29
#include "json_help.h"
30
31
static int dynsec__general_config_load(struct dynsec__data *data, cJSON *tree)
32
5.40k
{
33
5.40k
  cJSON *j_default_access;
34
35
5.40k
  json_get_int64(tree, "changeIndex", &data->changeindex, true, 0);
36
37
5.40k
  j_default_access = cJSON_GetObjectItem(tree, "defaultACLAccess");
38
5.40k
  if(j_default_access && cJSON_IsObject(j_default_access)){
39
32
    json_get_bool(j_default_access, ACL_TYPE_PUB_C_SEND, &data->default_access.publish_c_send, true, false);
40
32
    json_get_bool(j_default_access, ACL_TYPE_PUB_C_RECV, &data->default_access.publish_c_recv, true, false);
41
32
    json_get_bool(j_default_access, ACL_TYPE_SUB_GENERIC, &data->default_access.subscribe, true, false);
42
32
    json_get_bool(j_default_access, ACL_TYPE_UNSUB_GENERIC, &data->default_access.unsubscribe, true, false);
43
32
  }
44
5.40k
  return MOSQ_ERR_SUCCESS;
45
5.40k
}
46
47
static int dynsec__general_config_save(struct dynsec__data *data, cJSON *tree)
48
0
{
49
0
  cJSON *j_default_access;
50
51
0
  cJSON_AddIntToObject(tree, "changeIndex", data->changeindex);
52
53
0
  j_default_access = cJSON_CreateObject();
54
0
  if(j_default_access == NULL){
55
0
    return 1;
56
0
  }
57
0
  cJSON_AddItemToObject(tree, "defaultACLAccess", j_default_access);
58
59
0
  if(cJSON_AddBoolToObject(j_default_access, ACL_TYPE_PUB_C_SEND, data->default_access.publish_c_send) == NULL
60
0
      || cJSON_AddBoolToObject(j_default_access, ACL_TYPE_PUB_C_RECV, data->default_access.publish_c_recv) == NULL
61
0
      || cJSON_AddBoolToObject(j_default_access, ACL_TYPE_SUB_GENERIC, data->default_access.subscribe) == NULL
62
0
      || cJSON_AddBoolToObject(j_default_access, ACL_TYPE_UNSUB_GENERIC, data->default_access.unsubscribe) == NULL
63
0
      ){
64
65
0
    return 1;
66
0
  }
67
68
0
  return MOSQ_ERR_SUCCESS;
69
0
}
70
71
int dynsec__config_from_json(struct dynsec__data *data, const char *json_str)
72
5.54k
{
73
5.54k
  cJSON *tree;
74
75
5.54k
  tree = cJSON_Parse(json_str);
76
5.54k
  if(tree == NULL){
77
135
    mosquitto_log_printf(MOSQ_LOG_ERR, "Error loading Dynamic security plugin config: File is not valid JSON.");
78
135
    return 1;
79
135
  }
80
81
5.40k
  if(dynsec__general_config_load(data, tree)
82
5.40k
      || dynsec_roles__config_load(data, tree)
83
5.40k
      || dynsec_clients__config_load(data, tree)
84
5.40k
      || dynsec_groups__config_load(data, tree)
85
5.40k
      ){
86
87
3
    cJSON_Delete(tree);
88
3
    return 1;
89
3
  }
90
91
5.40k
  cJSON_Delete(tree);
92
5.40k
  return 0;
93
5.40k
}
94
95
int dynsec__config_load(struct dynsec__data *data)
96
5.54k
{
97
5.54k
  FILE *fptr;
98
5.54k
  long flen_l;
99
5.54k
  size_t flen;
100
5.54k
  char *json_str;
101
5.54k
  int rc;
102
103
  /* Load from file */
104
5.54k
  fptr = fopen(data->config_file, "rb");
105
5.54k
  if(fptr == NULL){
106
    /* Attempt to initialise a new config file */
107
0
    if(dynsec__config_init(data) == MOSQ_ERR_SUCCESS){
108
      /* If it works, try to open the file again */
109
0
      fptr = fopen(data->config_file, "rb");
110
0
    }
111
112
0
    if(fptr == NULL){
113
0
      mosquitto_log_printf(MOSQ_LOG_ERR,
114
0
          "Error loading Dynamic security plugin config: File is not readable - check permissions.");
115
0
      return MOSQ_ERR_UNKNOWN;
116
0
    }
117
0
  }
118
119
5.54k
  fseek(fptr, 0, SEEK_END);
120
5.54k
  flen_l = ftell(fptr);
121
5.54k
  if(flen_l < 0){
122
0
    mosquitto_log_printf(MOSQ_LOG_ERR, "Error loading Dynamic security plugin config: %s", strerror(errno));
123
0
    fclose(fptr);
124
0
    return 1;
125
5.54k
  }else if(flen_l == 0){
126
0
    fclose(fptr);
127
0
    return 0;
128
0
  }
129
5.54k
  flen = (size_t)flen_l;
130
5.54k
  fseek(fptr, 0, SEEK_SET);
131
5.54k
  json_str = mosquitto_calloc(flen+1, sizeof(char));
132
5.54k
  if(json_str == NULL){
133
0
    mosquitto_log_printf(MOSQ_LOG_ERR, "Error: Out of memory.");
134
0
    fclose(fptr);
135
0
    return 1;
136
0
  }
137
5.54k
  if(fread(json_str, 1, flen, fptr) != flen){
138
0
    mosquitto_log_printf(MOSQ_LOG_WARNING, "Error loading Dynamic security plugin config: Unable to read file contents.\n");
139
0
    mosquitto_free(json_str);
140
0
    fclose(fptr);
141
0
    return 1;
142
0
  }
143
5.54k
  fclose(fptr);
144
145
5.54k
  rc = dynsec__config_from_json(data, json_str);
146
5.54k
  free(json_str);
147
5.54k
  return rc;
148
5.54k
}
149
150
char *dynsec__config_to_json(struct dynsec__data *data)
151
0
{
152
0
  cJSON *tree;
153
0
  char *json_str;
154
155
0
  tree = cJSON_CreateObject();
156
0
  if(tree == NULL) return NULL;
157
158
0
  if(dynsec__general_config_save(data, tree)
159
0
      || dynsec_clients__config_save(data, tree)
160
0
      || dynsec_groups__config_save(data, tree)
161
0
      || dynsec_roles__config_save(data, tree)){
162
163
0
    cJSON_Delete(tree);
164
0
    return NULL;
165
0
  }
166
167
  /* Print json to string */
168
0
  json_str = cJSON_Print(tree);
169
0
  cJSON_Delete(tree);
170
0
  return json_str;
171
0
}
172
173
void dynsec__log_write_error(const char* msg)
174
0
{
175
0
  mosquitto_log_printf(MOSQ_LOG_ERR, "Error saving Dynamic security plugin config: %s", msg);
176
0
}
177
178
int dynsec__write_json_config(FILE* fptr, void* user_data)
179
0
{
180
0
  struct dynsec__data *data = (struct dynsec__data *)user_data;
181
0
  char *json_str;
182
0
  size_t json_str_len;
183
0
  int rc = MOSQ_ERR_SUCCESS;
184
185
0
  json_str = dynsec__config_to_json(data);
186
0
  if(json_str == NULL){
187
0
    mosquitto_log_printf(MOSQ_LOG_ERR, "Error saving Dynamic security plugin config: Out of memory.\n");
188
0
    return MOSQ_ERR_NOMEM;
189
0
  }
190
0
  json_str_len = strlen(json_str);
191
192
0
  if (fwrite(json_str, 1, json_str_len, fptr) != json_str_len){
193
0
    mosquitto_log_printf(MOSQ_LOG_ERR, "Error saving Dynamic security plugin config: Cannot write whole config (%ld) bytes to file %s", json_str_len, data->config_file);
194
0
    rc = MOSQ_ERR_UNKNOWN;
195
0
  }
196
197
0
  mosquitto_free(json_str);
198
0
  return rc;
199
0
}
200
201
void dynsec__config_batch_save(struct dynsec__data *data)
202
0
{
203
0
  data->changeindex++;
204
0
  data->need_save = true;
205
0
}
206
207
void dynsec__config_save(struct dynsec__data *data)
208
0
{
209
0
  data->need_save = false;
210
0
  mosquitto_write_file(data->config_file, true, &dynsec__write_json_config, data, &dynsec__log_write_error);
211
0
}