Coverage Report

Created: 2023-06-07 06:10

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