Coverage Report

Created: 2025-10-24 06:29

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/mosquitto/plugins/dynamic-security/config.c
Line
Count
Source
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
32
static int dynsec__general_config_load(struct dynsec__data *data, cJSON *tree)
33
5.58k
{
34
5.58k
  cJSON *j_default_access;
35
36
5.58k
  json_get_int64(tree, "changeIndex", &data->changeindex, true, 0);
37
38
5.58k
  j_default_access = cJSON_GetObjectItem(tree, "defaultACLAccess");
39
5.58k
  if(j_default_access && cJSON_IsObject(j_default_access)){
40
30
    json_get_bool(j_default_access, ACL_TYPE_PUB_C_SEND, &data->default_access.publish_c_send, true, false);
41
30
    json_get_bool(j_default_access, ACL_TYPE_PUB_C_RECV, &data->default_access.publish_c_recv, true, false);
42
30
    json_get_bool(j_default_access, ACL_TYPE_SUB_GENERIC, &data->default_access.subscribe, true, false);
43
30
    json_get_bool(j_default_access, ACL_TYPE_UNSUB_GENERIC, &data->default_access.unsubscribe, true, false);
44
30
  }
45
5.58k
  return MOSQ_ERR_SUCCESS;
46
5.58k
}
47
48
49
static int dynsec__general_config_save(struct dynsec__data *data, cJSON *tree)
50
0
{
51
0
  cJSON *j_default_access;
52
53
0
  cJSON_AddIntToObject(tree, "changeIndex", data->changeindex);
54
55
0
  j_default_access = cJSON_CreateObject();
56
0
  if(j_default_access == NULL){
57
0
    return 1;
58
0
  }
59
0
  cJSON_AddItemToObject(tree, "defaultACLAccess", j_default_access);
60
61
0
  if(cJSON_AddBoolToObject(j_default_access, ACL_TYPE_PUB_C_SEND, data->default_access.publish_c_send) == NULL
62
0
      || cJSON_AddBoolToObject(j_default_access, ACL_TYPE_PUB_C_RECV, data->default_access.publish_c_recv) == NULL
63
0
      || cJSON_AddBoolToObject(j_default_access, ACL_TYPE_SUB_GENERIC, data->default_access.subscribe) == NULL
64
0
      || cJSON_AddBoolToObject(j_default_access, ACL_TYPE_UNSUB_GENERIC, data->default_access.unsubscribe) == NULL
65
0
      ){
66
67
0
    return 1;
68
0
  }
69
70
0
  return MOSQ_ERR_SUCCESS;
71
0
}
72
73
74
int dynsec__config_from_json(struct dynsec__data *data, const char *json_str)
75
5.70k
{
76
5.70k
  cJSON *tree;
77
78
5.70k
  tree = cJSON_Parse(json_str);
79
5.70k
  if(tree == NULL){
80
125
    mosquitto_log_printf(MOSQ_LOG_ERR, "Error loading Dynamic security plugin config: File is not valid JSON.");
81
125
    return 1;
82
125
  }
83
84
5.58k
  if(dynsec__general_config_load(data, tree)
85
5.58k
      || dynsec_roles__config_load(data, tree)
86
5.57k
      || dynsec_clients__config_load(data, tree)
87
5.57k
      || dynsec_groups__config_load(data, tree)
88
5.58k
      ){
89
90
3
    cJSON_Delete(tree);
91
3
    return 1;
92
3
  }
93
94
5.57k
  cJSON_Delete(tree);
95
5.57k
  return 0;
96
5.58k
}
97
98
99
int dynsec__config_load(struct dynsec__data *data)
100
5.70k
{
101
5.70k
  FILE *fptr;
102
5.70k
  long flen_l;
103
5.70k
  size_t flen;
104
5.70k
  char *json_str;
105
5.70k
  int rc;
106
107
  /* Load from file */
108
5.70k
  fptr = fopen(data->config_file, "rb");
109
5.70k
  if(fptr == NULL){
110
    /* Attempt to initialise a new config file */
111
0
    if(dynsec__config_init(data) == MOSQ_ERR_SUCCESS){
112
      /* If it works, try to open the file again */
113
0
      fptr = fopen(data->config_file, "rb");
114
0
    }
115
116
0
    if(fptr == NULL){
117
0
      mosquitto_log_printf(MOSQ_LOG_ERR,
118
0
          "Error loading Dynamic security plugin config: File is not readable - check permissions.");
119
0
      return MOSQ_ERR_UNKNOWN;
120
0
    }
121
0
  }
122
123
5.70k
  fseek(fptr, 0, SEEK_END);
124
5.70k
  flen_l = ftell(fptr);
125
5.70k
  if(flen_l < 0){
126
0
    mosquitto_log_printf(MOSQ_LOG_ERR, "Error loading Dynamic security plugin config: %s", strerror(errno));
127
0
    fclose(fptr);
128
0
    return 1;
129
5.70k
  }else if(flen_l == 0){
130
0
    fclose(fptr);
131
0
    return 0;
132
0
  }
133
5.70k
  flen = (size_t)flen_l;
134
5.70k
  fseek(fptr, 0, SEEK_SET);
135
5.70k
  json_str = mosquitto_calloc(flen+1, sizeof(char));
136
5.70k
  if(json_str == NULL){
137
0
    mosquitto_log_printf(MOSQ_LOG_ERR, "Error: Out of memory.");
138
0
    fclose(fptr);
139
0
    return 1;
140
0
  }
141
5.70k
  if(fread(json_str, 1, flen, fptr) != flen){
142
0
    mosquitto_log_printf(MOSQ_LOG_WARNING, "Error loading Dynamic security plugin config: Unable to read file contents.\n");
143
0
    mosquitto_free(json_str);
144
0
    fclose(fptr);
145
0
    return 1;
146
0
  }
147
5.70k
  fclose(fptr);
148
149
5.70k
  rc = dynsec__config_from_json(data, json_str);
150
5.70k
  free(json_str);
151
5.70k
  return rc;
152
5.70k
}
153
154
155
char *dynsec__config_to_json(struct dynsec__data *data)
156
0
{
157
0
  cJSON *tree;
158
0
  char *json_str;
159
160
0
  tree = cJSON_CreateObject();
161
0
  if(tree == NULL){
162
0
    return NULL;
163
0
  }
164
165
0
  if(dynsec__general_config_save(data, tree)
166
0
      || dynsec_clients__config_save(data, tree)
167
0
      || dynsec_groups__config_save(data, tree)
168
0
      || dynsec_roles__config_save(data, tree)){
169
170
0
    cJSON_Delete(tree);
171
0
    return NULL;
172
0
  }
173
174
  /* Print json to string */
175
0
  json_str = cJSON_Print(tree);
176
0
  cJSON_Delete(tree);
177
0
  return json_str;
178
0
}
179
180
181
void dynsec__log_write_error(const char *msg)
182
0
{
183
0
  mosquitto_log_printf(MOSQ_LOG_ERR, "Error saving Dynamic security plugin config: %s", msg);
184
0
}
185
186
187
int dynsec__write_json_config(FILE *fptr, void *user_data)
188
0
{
189
0
  struct dynsec__data *data = (struct dynsec__data *)user_data;
190
0
  char *json_str;
191
0
  size_t json_str_len;
192
0
  int rc = MOSQ_ERR_SUCCESS;
193
194
0
  json_str = dynsec__config_to_json(data);
195
0
  if(json_str == NULL){
196
0
    mosquitto_log_printf(MOSQ_LOG_ERR, "Error saving Dynamic security plugin config: Out of memory.\n");
197
0
    return MOSQ_ERR_NOMEM;
198
0
  }
199
0
  json_str_len = strlen(json_str);
200
201
0
  if(fwrite(json_str, 1, json_str_len, fptr) != json_str_len){
202
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);
203
0
    rc = MOSQ_ERR_UNKNOWN;
204
0
  }
205
206
0
  mosquitto_free(json_str);
207
0
  return rc;
208
0
}
209
210
211
void dynsec__config_batch_save(struct dynsec__data *data)
212
0
{
213
0
  data->changeindex++;
214
0
  data->need_save = true;
215
0
}
216
217
218
void dynsec__config_save(struct dynsec__data *data)
219
0
{
220
0
  data->need_save = false;
221
  mosquitto_write_file(data->config_file, true, &dynsec__write_json_config, data, &dynsec__log_write_error);
222
0
}