/src/mosquitto/plugins/dynamic-security/roles.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 <stdio.h> |
23 | | #include <string.h> |
24 | | #include <uthash.h> |
25 | | #include <utlist.h> |
26 | | |
27 | | #ifndef WIN32 |
28 | | # include <strings.h> |
29 | | #endif |
30 | | |
31 | | #include "dynamic_security.h" |
32 | | #include "json_help.h" |
33 | | |
34 | | static cJSON *add_role_to_json(struct dynsec__role *role, bool verbose); |
35 | | static void role__remove_all_clients(struct dynsec__data *data, struct dynsec__role *role); |
36 | | |
37 | | /* ################################################################ |
38 | | * # |
39 | | * # Utility functions |
40 | | * # |
41 | | * ################################################################ */ |
42 | | |
43 | | static int role_cmp(void *a, void *b) |
44 | 264k | { |
45 | 264k | struct dynsec__role *role_a = a; |
46 | 264k | struct dynsec__role *role_b = b; |
47 | | |
48 | 264k | return strcmp(role_a->rolename, role_b->rolename); |
49 | 264k | } |
50 | | |
51 | | |
52 | | static void role__free_acl(struct dynsec__acl **acl, struct dynsec__acl *item) |
53 | 22.6k | { |
54 | 22.6k | HASH_DELETE(hh, *acl, item); |
55 | 22.6k | mosquitto_free(item); |
56 | 22.6k | } |
57 | | |
58 | | static void role__free_all_acls(struct dynsec__acl **acl) |
59 | 268k | { |
60 | 268k | struct dynsec__acl *iter, *tmp = NULL; |
61 | | |
62 | 268k | HASH_ITER(hh, *acl, iter, tmp){ |
63 | 22.6k | role__free_acl(acl, iter); |
64 | 22.6k | } |
65 | 268k | } |
66 | | |
67 | | static void role__free_item(struct dynsec__data *data, struct dynsec__role *role, bool remove_from_hash) |
68 | 44.7k | { |
69 | 44.7k | if(remove_from_hash){ |
70 | 44.7k | HASH_DEL(data->roles, role); |
71 | 44.7k | } |
72 | 44.7k | dynsec_clientlist__cleanup(&role->clientlist); |
73 | 44.7k | dynsec_grouplist__cleanup(&role->grouplist); |
74 | 44.7k | mosquitto_free(role->text_name); |
75 | 44.7k | mosquitto_free(role->text_description); |
76 | 44.7k | role__free_all_acls(&role->acls.publish_c_send); |
77 | 44.7k | role__free_all_acls(&role->acls.publish_c_recv); |
78 | 44.7k | role__free_all_acls(&role->acls.subscribe_literal); |
79 | 44.7k | role__free_all_acls(&role->acls.subscribe_pattern); |
80 | 44.7k | role__free_all_acls(&role->acls.unsubscribe_literal); |
81 | 44.7k | role__free_all_acls(&role->acls.unsubscribe_pattern); |
82 | 44.7k | mosquitto_free(role); |
83 | 44.7k | } |
84 | | |
85 | | struct dynsec__role *dynsec_roles__find(struct dynsec__data *data, const char *rolename) |
86 | 121k | { |
87 | 121k | struct dynsec__role *role = NULL; |
88 | | |
89 | 121k | if(rolename){ |
90 | 121k | HASH_FIND(hh, data->roles, rolename, strlen(rolename), role); |
91 | 121k | } |
92 | 121k | return role; |
93 | 121k | } |
94 | | |
95 | | |
96 | | void dynsec_roles__cleanup(struct dynsec__data *data) |
97 | 5.25k | { |
98 | 5.25k | struct dynsec__role *role, *role_tmp = NULL; |
99 | | |
100 | 44.7k | HASH_ITER(hh, data->roles, role, role_tmp){ |
101 | 44.7k | role__free_item(data, role, true); |
102 | 44.7k | } |
103 | 5.25k | } |
104 | | |
105 | | |
106 | | static void role__kick_all(struct dynsec__data *data, struct dynsec__role *role) |
107 | 0 | { |
108 | 0 | struct dynsec__grouplist *grouplist, *grouplist_tmp = NULL; |
109 | |
|
110 | 0 | dynsec_clientlist__kick_all(data, role->clientlist); |
111 | |
|
112 | 0 | HASH_ITER(hh, role->grouplist, grouplist, grouplist_tmp){ |
113 | 0 | if(grouplist->group == data->anonymous_group){ |
114 | 0 | dynsec_kicklist__add(data, NULL); |
115 | 0 | } |
116 | 0 | dynsec_clientlist__kick_all(data, grouplist->group->clientlist); |
117 | 0 | } |
118 | 0 | } |
119 | | |
120 | | |
121 | | /* ################################################################ |
122 | | * # |
123 | | * # Config file load and save |
124 | | * # |
125 | | * ################################################################ */ |
126 | | |
127 | | |
128 | | static int add_single_acl_to_json(cJSON *j_array, const char *acl_type, struct dynsec__acl *acl) |
129 | 0 | { |
130 | 0 | struct dynsec__acl *iter, *tmp = NULL; |
131 | 0 | cJSON *j_acl; |
132 | |
|
133 | 0 | HASH_ITER(hh, acl, iter, tmp){ |
134 | 0 | j_acl = cJSON_CreateObject(); |
135 | 0 | if(j_acl == NULL){ |
136 | 0 | return 1; |
137 | 0 | } |
138 | 0 | cJSON_AddItemToArray(j_array, j_acl); |
139 | |
|
140 | 0 | if(cJSON_AddStringToObject(j_acl, "acltype", acl_type) == NULL |
141 | 0 | || cJSON_AddStringToObject(j_acl, "topic", iter->topic) == NULL |
142 | 0 | || cJSON_AddIntToObject(j_acl, "priority", iter->priority) == NULL |
143 | 0 | || cJSON_AddBoolToObject(j_acl, "allow", iter->allow) == NULL |
144 | 0 | ){ |
145 | |
|
146 | 0 | return 1; |
147 | 0 | } |
148 | 0 | } |
149 | | |
150 | | |
151 | 0 | return 0; |
152 | 0 | } |
153 | | |
154 | | static int add_acls_to_json(cJSON *j_role, struct dynsec__role *role) |
155 | 0 | { |
156 | 0 | cJSON *j_acls; |
157 | |
|
158 | 0 | if((j_acls = cJSON_AddArrayToObject(j_role, "acls")) == NULL){ |
159 | 0 | return 1; |
160 | 0 | } |
161 | | |
162 | 0 | if(add_single_acl_to_json(j_acls, ACL_TYPE_PUB_C_SEND, role->acls.publish_c_send) != MOSQ_ERR_SUCCESS |
163 | 0 | || add_single_acl_to_json(j_acls, ACL_TYPE_PUB_C_RECV, role->acls.publish_c_recv) != MOSQ_ERR_SUCCESS |
164 | 0 | || add_single_acl_to_json(j_acls, ACL_TYPE_SUB_LITERAL, role->acls.subscribe_literal) != MOSQ_ERR_SUCCESS |
165 | 0 | || add_single_acl_to_json(j_acls, ACL_TYPE_SUB_PATTERN, role->acls.subscribe_pattern) != MOSQ_ERR_SUCCESS |
166 | 0 | || add_single_acl_to_json(j_acls, ACL_TYPE_UNSUB_LITERAL, role->acls.unsubscribe_literal) != MOSQ_ERR_SUCCESS |
167 | 0 | || add_single_acl_to_json(j_acls, ACL_TYPE_UNSUB_PATTERN, role->acls.unsubscribe_pattern) != MOSQ_ERR_SUCCESS |
168 | 0 | ){ |
169 | |
|
170 | 0 | return 1; |
171 | 0 | } |
172 | 0 | return 0; |
173 | 0 | } |
174 | | |
175 | | int dynsec_roles__config_save(struct dynsec__data *data, cJSON *tree) |
176 | 0 | { |
177 | 0 | cJSON *j_roles, *j_role; |
178 | 0 | struct dynsec__role *role, *role_tmp = NULL; |
179 | |
|
180 | 0 | if((j_roles = cJSON_AddArrayToObject(tree, "roles")) == NULL){ |
181 | 0 | return 1; |
182 | 0 | } |
183 | | |
184 | 0 | HASH_ITER(hh, data->roles, role, role_tmp){ |
185 | 0 | j_role = add_role_to_json(role, true); |
186 | 0 | if(j_role == NULL){ |
187 | 0 | return 1; |
188 | 0 | } |
189 | 0 | cJSON_AddItemToArray(j_roles, j_role); |
190 | 0 | } |
191 | | |
192 | 0 | return 0; |
193 | 0 | } |
194 | | |
195 | | |
196 | | static int insert_acl_cmp(struct dynsec__acl *a, struct dynsec__acl *b) |
197 | 407k | { |
198 | 407k | return b->priority - a->priority; |
199 | 407k | } |
200 | | |
201 | | |
202 | | static int dynsec_roles__acl_load(cJSON *j_acls, const char *key, struct dynsec__acl **acllist) |
203 | 13.9k | { |
204 | 13.9k | cJSON *j_acl; |
205 | 13.9k | struct dynsec__acl *acl; |
206 | | |
207 | 226k | cJSON_ArrayForEach(j_acl, j_acls){ |
208 | 226k | const char *acltype; |
209 | 226k | const char *topic; |
210 | 226k | size_t topic_len; |
211 | | |
212 | 226k | if(json_get_string(j_acl, "acltype", &acltype, false) != MOSQ_ERR_SUCCESS){ |
213 | 28.6k | continue; |
214 | 28.6k | } |
215 | 198k | if(strcasecmp(acltype, key) != 0){ |
216 | 171k | continue; |
217 | 171k | } |
218 | 27.1k | if(json_get_string(j_acl, "topic", &topic, false) != MOSQ_ERR_SUCCESS){ |
219 | 895 | continue; |
220 | 895 | } |
221 | | |
222 | 26.2k | topic_len = strlen(topic); |
223 | 26.2k | if(topic_len == 0){ |
224 | 197 | continue; |
225 | 197 | } |
226 | | |
227 | 26.0k | HASH_FIND(hh, *acllist, topic, strlen(topic), acl); |
228 | 26.0k | if(acl){ |
229 | 3.33k | continue; |
230 | 3.33k | } |
231 | | |
232 | 22.6k | acl = mosquitto_calloc(1, sizeof(struct dynsec__acl) + topic_len + 1); |
233 | 22.6k | if(acl == NULL){ |
234 | 0 | return 1; |
235 | 0 | } |
236 | 22.6k | strncpy(acl->topic, topic, topic_len+1); |
237 | | |
238 | 22.6k | json_get_int(j_acl, "priority", &acl->priority, true, 0); |
239 | 22.6k | if(acl->priority > PRIORITY_MAX) acl->priority = PRIORITY_MAX; |
240 | 22.6k | if(acl->priority < -PRIORITY_MAX) acl->priority = -PRIORITY_MAX; |
241 | 22.6k | json_get_bool(j_acl, "allow", &acl->allow, true, false); |
242 | | |
243 | 22.6k | bool allow; |
244 | 22.6k | if(json_get_bool(j_acl, "allow", &allow, false, false) == MOSQ_ERR_SUCCESS){ |
245 | 926 | acl->allow = allow; |
246 | 926 | } |
247 | | |
248 | 22.6k | HASH_ADD_INORDER(hh, *acllist, topic, topic_len, acl, insert_acl_cmp); |
249 | 22.6k | } |
250 | | |
251 | 13.9k | return 0; |
252 | 13.9k | } |
253 | | |
254 | | |
255 | | int dynsec_roles__config_load(struct dynsec__data *data, cJSON *tree) |
256 | 5.12k | { |
257 | 5.12k | cJSON *j_roles, *j_role, *j_acls; |
258 | 5.12k | struct dynsec__role *role; |
259 | 5.12k | size_t rolename_len; |
260 | | |
261 | 5.12k | j_roles = cJSON_GetObjectItem(tree, "roles"); |
262 | 5.12k | if(j_roles == NULL){ |
263 | 2.31k | return 0; |
264 | 2.31k | } |
265 | | |
266 | 2.81k | if(cJSON_IsArray(j_roles) == false){ |
267 | 1 | return 1; |
268 | 1 | } |
269 | | |
270 | 60.5k | cJSON_ArrayForEach(j_role, j_roles){ |
271 | 60.5k | if(cJSON_IsObject(j_role) == true){ |
272 | | /* Role name */ |
273 | 60.0k | const char *rolename; |
274 | 60.0k | if(json_get_string(j_role, "rolename", &rolename, false) != MOSQ_ERR_SUCCESS){ |
275 | 7.77k | continue; |
276 | 7.77k | } |
277 | 52.2k | rolename_len = strlen(rolename); |
278 | 52.2k | if(rolename_len == 0){ |
279 | 224 | continue; |
280 | 224 | } |
281 | 52.0k | if(dynsec_roles__find(data, rolename)){ |
282 | 7.31k | continue; |
283 | 7.31k | } |
284 | | |
285 | 44.7k | role = mosquitto_calloc(1, sizeof(struct dynsec__role) + rolename_len + 1); |
286 | 44.7k | if(role == NULL){ |
287 | 0 | return MOSQ_ERR_NOMEM; |
288 | 0 | } |
289 | 44.7k | strncpy(role->rolename, rolename, rolename_len+1); |
290 | | |
291 | | /* Text name */ |
292 | 44.7k | const char *textname; |
293 | 44.7k | if(json_get_string(j_role, "textname", &textname, false) == MOSQ_ERR_SUCCESS){ |
294 | 199 | role->text_name = mosquitto_strdup(textname); |
295 | 199 | if(role->text_name == NULL){ |
296 | 0 | mosquitto_free(role); |
297 | 0 | continue; |
298 | 0 | } |
299 | 199 | } |
300 | | |
301 | | /* Text description */ |
302 | 44.7k | const char *textdescription; |
303 | 44.7k | if(json_get_string(j_role, "textdescription", &textdescription, false) == MOSQ_ERR_SUCCESS){ |
304 | 293 | role->text_description = mosquitto_strdup(textdescription); |
305 | 293 | if(role->text_description == NULL){ |
306 | 0 | mosquitto_free(role->text_name); |
307 | 0 | mosquitto_free(role); |
308 | 0 | continue; |
309 | 0 | } |
310 | 293 | } |
311 | | |
312 | | /* Allow wildcard subs */ |
313 | 44.7k | json_get_bool(j_role, "allowwildcardsubs", &role->allow_wildcard_subs, true, true); |
314 | | |
315 | | /* ACLs */ |
316 | 44.7k | j_acls = cJSON_GetObjectItem(j_role, "acls"); |
317 | 44.7k | if(j_acls && cJSON_IsArray(j_acls)){ |
318 | 2.33k | if(dynsec_roles__acl_load(j_acls, ACL_TYPE_PUB_C_SEND, &role->acls.publish_c_send) != 0 |
319 | 2.33k | || dynsec_roles__acl_load(j_acls, ACL_TYPE_PUB_C_RECV, &role->acls.publish_c_recv) != 0 |
320 | 2.33k | || dynsec_roles__acl_load(j_acls, ACL_TYPE_SUB_LITERAL, &role->acls.subscribe_literal) != 0 |
321 | 2.33k | || dynsec_roles__acl_load(j_acls, ACL_TYPE_SUB_PATTERN, &role->acls.subscribe_pattern) != 0 |
322 | 2.33k | || dynsec_roles__acl_load(j_acls, ACL_TYPE_UNSUB_LITERAL, &role->acls.unsubscribe_literal) != 0 |
323 | 2.33k | || dynsec_roles__acl_load(j_acls, ACL_TYPE_UNSUB_PATTERN, &role->acls.unsubscribe_pattern) != 0 |
324 | 2.33k | ){ |
325 | |
|
326 | 0 | mosquitto_free(role->text_name); |
327 | 0 | mosquitto_free(role->text_description); |
328 | 0 | mosquitto_free(role); |
329 | 0 | continue; |
330 | 0 | } |
331 | 2.33k | } |
332 | | |
333 | 44.7k | HASH_ADD(hh, data->roles, rolename, rolename_len, role); |
334 | 44.7k | } |
335 | 60.5k | } |
336 | 2.80k | HASH_SORT(data->roles, role_cmp); |
337 | | |
338 | 2.80k | return 0; |
339 | 2.80k | } |
340 | | |
341 | | |
342 | | int dynsec_roles__process_create(struct dynsec__data *data, struct mosquitto_control_cmd *cmd) |
343 | 0 | { |
344 | 0 | const char *rolename; |
345 | 0 | const char *text_name, *text_description; |
346 | 0 | bool allow_wildcard_subs; |
347 | 0 | struct dynsec__role *role; |
348 | 0 | int rc = MOSQ_ERR_SUCCESS; |
349 | 0 | cJSON *j_acls; |
350 | 0 | const char *admin_clientid, *admin_username; |
351 | 0 | size_t rolename_len; |
352 | |
|
353 | 0 | if(json_get_string(cmd->j_command, "rolename", &rolename, false) != MOSQ_ERR_SUCCESS){ |
354 | 0 | mosquitto_control_command_reply(cmd, "Invalid/missing rolename"); |
355 | 0 | return MOSQ_ERR_INVAL; |
356 | 0 | } |
357 | 0 | rolename_len = strlen(rolename); |
358 | 0 | if(rolename_len == 0){ |
359 | 0 | mosquitto_control_command_reply(cmd, "Empty rolename"); |
360 | 0 | return MOSQ_ERR_INVAL; |
361 | 0 | } |
362 | 0 | if(mosquitto_validate_utf8(rolename, (int)rolename_len) != MOSQ_ERR_SUCCESS){ |
363 | 0 | mosquitto_control_command_reply(cmd, "Role name not valid UTF-8"); |
364 | 0 | return MOSQ_ERR_INVAL; |
365 | 0 | } |
366 | | |
367 | 0 | if(json_get_string(cmd->j_command, "textname", &text_name, true) != MOSQ_ERR_SUCCESS){ |
368 | 0 | mosquitto_control_command_reply(cmd, "Invalid/missing textname"); |
369 | 0 | return MOSQ_ERR_INVAL; |
370 | 0 | } |
371 | | |
372 | 0 | if(json_get_string(cmd->j_command, "textdescription", &text_description, true) != MOSQ_ERR_SUCCESS){ |
373 | 0 | mosquitto_control_command_reply(cmd, "Invalid/missing textdescription"); |
374 | 0 | return MOSQ_ERR_INVAL; |
375 | 0 | } |
376 | | |
377 | 0 | if(json_get_bool(cmd->j_command, "allowwildcardsubs", &allow_wildcard_subs, true, true) != MOSQ_ERR_SUCCESS){ |
378 | 0 | mosquitto_control_command_reply(cmd, "Invalid allowwildcardsubs"); |
379 | 0 | return MOSQ_ERR_INVAL; |
380 | 0 | } |
381 | | |
382 | 0 | role = dynsec_roles__find(data, rolename); |
383 | 0 | if(role){ |
384 | 0 | mosquitto_control_command_reply(cmd, "Role already exists"); |
385 | 0 | return MOSQ_ERR_SUCCESS; |
386 | 0 | } |
387 | | |
388 | 0 | role = mosquitto_calloc(1, sizeof(struct dynsec__role) + rolename_len + 1); |
389 | 0 | if(role == NULL){ |
390 | 0 | mosquitto_control_command_reply(cmd, "Internal error"); |
391 | 0 | return MOSQ_ERR_NOMEM; |
392 | 0 | } |
393 | 0 | strncpy(role->rolename, rolename, rolename_len+1); |
394 | 0 | if(text_name){ |
395 | 0 | role->text_name = mosquitto_strdup(text_name); |
396 | 0 | if(role->text_name == NULL){ |
397 | 0 | mosquitto_control_command_reply(cmd, "Internal error"); |
398 | 0 | rc = MOSQ_ERR_NOMEM; |
399 | 0 | goto error; |
400 | 0 | } |
401 | 0 | } |
402 | 0 | if(text_description){ |
403 | 0 | role->text_description = mosquitto_strdup(text_description); |
404 | 0 | if(role->text_description == NULL){ |
405 | 0 | mosquitto_control_command_reply(cmd, "Internal error"); |
406 | 0 | rc = MOSQ_ERR_NOMEM; |
407 | 0 | goto error; |
408 | 0 | } |
409 | 0 | } |
410 | 0 | role->allow_wildcard_subs = allow_wildcard_subs; |
411 | | |
412 | | /* ACLs */ |
413 | 0 | j_acls = cJSON_GetObjectItem(cmd->j_command, "acls"); |
414 | 0 | if(j_acls && cJSON_IsArray(j_acls)){ |
415 | 0 | if(dynsec_roles__acl_load(j_acls, ACL_TYPE_PUB_C_SEND, &role->acls.publish_c_send) != 0 |
416 | 0 | || dynsec_roles__acl_load(j_acls, ACL_TYPE_PUB_C_RECV, &role->acls.publish_c_recv) != 0 |
417 | 0 | || dynsec_roles__acl_load(j_acls, ACL_TYPE_SUB_LITERAL, &role->acls.subscribe_literal) != 0 |
418 | 0 | || dynsec_roles__acl_load(j_acls, ACL_TYPE_SUB_PATTERN, &role->acls.subscribe_pattern) != 0 |
419 | 0 | || dynsec_roles__acl_load(j_acls, ACL_TYPE_UNSUB_LITERAL, &role->acls.unsubscribe_literal) != 0 |
420 | 0 | || dynsec_roles__acl_load(j_acls, ACL_TYPE_UNSUB_PATTERN, &role->acls.unsubscribe_pattern) != 0 |
421 | 0 | ){ |
422 | |
|
423 | 0 | mosquitto_control_command_reply(cmd, "Internal error"); |
424 | 0 | rc = MOSQ_ERR_NOMEM; |
425 | 0 | goto error; |
426 | 0 | } |
427 | 0 | } |
428 | | |
429 | | |
430 | 0 | HASH_ADD_INORDER(hh, data->roles, rolename, rolename_len, role, role_cmp); |
431 | | |
432 | 0 | dynsec__config_batch_save(data); |
433 | |
|
434 | 0 | mosquitto_control_command_reply(cmd, NULL); |
435 | |
|
436 | 0 | admin_clientid = mosquitto_client_id(cmd->client); |
437 | 0 | admin_username = mosquitto_client_username(cmd->client); |
438 | 0 | mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | createRole | rolename=%s", |
439 | 0 | admin_clientid, admin_username, rolename); |
440 | |
|
441 | 0 | return MOSQ_ERR_SUCCESS; |
442 | 0 | error: |
443 | 0 | if(role){ |
444 | 0 | role__free_item(data, role, false); |
445 | 0 | } |
446 | 0 | return rc; |
447 | 0 | } |
448 | | |
449 | | |
450 | | static void role__remove_all_clients(struct dynsec__data *data, struct dynsec__role *role) |
451 | 0 | { |
452 | 0 | struct dynsec__clientlist *clientlist, *clientlist_tmp = NULL; |
453 | |
|
454 | 0 | HASH_ITER(hh, role->clientlist, clientlist, clientlist_tmp){ |
455 | 0 | dynsec_kicklist__add(data, clientlist->client->username); |
456 | |
|
457 | 0 | dynsec_rolelist__client_remove(clientlist->client, role); |
458 | 0 | } |
459 | 0 | } |
460 | | |
461 | | static void role__remove_all_groups(struct dynsec__data *data, struct dynsec__role *role) |
462 | 0 | { |
463 | 0 | struct dynsec__grouplist *grouplist, *grouplist_tmp = NULL; |
464 | |
|
465 | 0 | HASH_ITER(hh, role->grouplist, grouplist, grouplist_tmp){ |
466 | 0 | if(grouplist->group == data->anonymous_group){ |
467 | 0 | dynsec_kicklist__add(data, NULL); |
468 | 0 | } |
469 | 0 | dynsec_clientlist__kick_all(data, grouplist->group->clientlist); |
470 | |
|
471 | 0 | dynsec_rolelist__group_remove(grouplist->group, role); |
472 | 0 | } |
473 | 0 | } |
474 | | |
475 | | int dynsec_roles__process_delete(struct dynsec__data *data, struct mosquitto_control_cmd *cmd) |
476 | 0 | { |
477 | 0 | const char *rolename; |
478 | 0 | struct dynsec__role *role; |
479 | 0 | const char *admin_clientid, *admin_username; |
480 | |
|
481 | 0 | if(json_get_string(cmd->j_command, "rolename", &rolename, false) != MOSQ_ERR_SUCCESS){ |
482 | 0 | mosquitto_control_command_reply(cmd, "Invalid/missing rolename"); |
483 | 0 | return MOSQ_ERR_INVAL; |
484 | 0 | } |
485 | 0 | if(mosquitto_validate_utf8(rolename, (int)strlen(rolename)) != MOSQ_ERR_SUCCESS){ |
486 | 0 | mosquitto_control_command_reply(cmd, "Role name not valid UTF-8"); |
487 | 0 | return MOSQ_ERR_INVAL; |
488 | 0 | } |
489 | | |
490 | 0 | role = dynsec_roles__find(data, rolename); |
491 | 0 | if(role){ |
492 | 0 | role__remove_all_clients(data, role); |
493 | 0 | role__remove_all_groups(data, role); |
494 | 0 | role__free_item(data, role, true); |
495 | 0 | dynsec__config_batch_save(data); |
496 | 0 | mosquitto_control_command_reply(cmd, NULL); |
497 | |
|
498 | 0 | admin_clientid = mosquitto_client_id(cmd->client); |
499 | 0 | admin_username = mosquitto_client_username(cmd->client); |
500 | 0 | mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | deleteRole | rolename=%s", |
501 | 0 | admin_clientid, admin_username, rolename); |
502 | |
|
503 | 0 | return MOSQ_ERR_SUCCESS; |
504 | 0 | }else{ |
505 | 0 | mosquitto_control_command_reply(cmd, "Role not found"); |
506 | 0 | return MOSQ_ERR_SUCCESS; |
507 | 0 | } |
508 | 0 | } |
509 | | |
510 | | |
511 | | static cJSON *add_role_to_json(struct dynsec__role *role, bool verbose) |
512 | 0 | { |
513 | 0 | cJSON *j_role = NULL; |
514 | |
|
515 | 0 | if(verbose){ |
516 | 0 | j_role = cJSON_CreateObject(); |
517 | 0 | if(j_role == NULL){ |
518 | 0 | return NULL; |
519 | 0 | } |
520 | | |
521 | 0 | if(cJSON_AddStringToObject(j_role, "rolename", role->rolename) == NULL |
522 | 0 | || (role->text_name && cJSON_AddStringToObject(j_role, "textname", role->text_name) == NULL) |
523 | 0 | || (role->text_description && cJSON_AddStringToObject(j_role, "textdescription", role->text_description) == NULL) |
524 | 0 | || cJSON_AddBoolToObject(j_role, "allowwildcardsubs", role->allow_wildcard_subs) == NULL |
525 | 0 | ){ |
526 | |
|
527 | 0 | cJSON_Delete(j_role); |
528 | 0 | return NULL; |
529 | 0 | } |
530 | 0 | if(add_acls_to_json(j_role, role)){ |
531 | 0 | cJSON_Delete(j_role); |
532 | 0 | return NULL; |
533 | 0 | } |
534 | 0 | }else{ |
535 | 0 | j_role = cJSON_CreateString(role->rolename); |
536 | 0 | if(j_role == NULL){ |
537 | 0 | return NULL; |
538 | 0 | } |
539 | 0 | } |
540 | 0 | return j_role; |
541 | 0 | } |
542 | | |
543 | | int dynsec_roles__process_list(struct dynsec__data *data, struct mosquitto_control_cmd *cmd) |
544 | 0 | { |
545 | 0 | bool verbose; |
546 | 0 | struct dynsec__role *role, *role_tmp = NULL; |
547 | 0 | cJSON *tree, *j_roles, *j_role, *j_data; |
548 | 0 | int i, count, offset; |
549 | 0 | const char *admin_clientid, *admin_username; |
550 | |
|
551 | 0 | json_get_bool(cmd->j_command, "verbose", &verbose, true, false); |
552 | 0 | json_get_int(cmd->j_command, "count", &count, true, -1); |
553 | 0 | json_get_int(cmd->j_command, "offset", &offset, true, 0); |
554 | |
|
555 | 0 | tree = cJSON_CreateObject(); |
556 | 0 | if(tree == NULL){ |
557 | 0 | mosquitto_control_command_reply(cmd, "Internal error"); |
558 | 0 | return MOSQ_ERR_NOMEM; |
559 | 0 | } |
560 | | |
561 | 0 | if(cJSON_AddStringToObject(tree, "command", "listRoles") == NULL |
562 | 0 | || (j_data = cJSON_AddObjectToObject(tree, "data")) == NULL |
563 | 0 | || cJSON_AddIntToObject(j_data, "totalCount", (int)HASH_CNT(hh, data->roles)) == NULL |
564 | 0 | || (j_roles = cJSON_AddArrayToObject(j_data, "roles")) == NULL |
565 | 0 | || (cmd->correlation_data && cJSON_AddStringToObject(tree, "correlationData", cmd->correlation_data) == NULL) |
566 | 0 | ){ |
567 | |
|
568 | 0 | cJSON_Delete(tree); |
569 | 0 | mosquitto_control_command_reply(cmd, "Internal error"); |
570 | 0 | return MOSQ_ERR_NOMEM; |
571 | 0 | } |
572 | | |
573 | 0 | i = 0; |
574 | 0 | HASH_ITER(hh, data->roles, role, role_tmp){ |
575 | 0 | if(i>=offset){ |
576 | 0 | j_role = add_role_to_json(role, verbose); |
577 | 0 | if(j_role == NULL){ |
578 | 0 | cJSON_Delete(tree); |
579 | 0 | mosquitto_control_command_reply(cmd, "Internal error"); |
580 | 0 | return MOSQ_ERR_NOMEM; |
581 | 0 | } |
582 | 0 | cJSON_AddItemToArray(j_roles, j_role); |
583 | |
|
584 | 0 | if(count >= 0){ |
585 | 0 | count--; |
586 | 0 | if(count <= 0){ |
587 | 0 | break; |
588 | 0 | } |
589 | 0 | } |
590 | 0 | } |
591 | 0 | i++; |
592 | 0 | } |
593 | | |
594 | 0 | cJSON_AddItemToArray(cmd->j_responses, tree); |
595 | |
|
596 | 0 | admin_clientid = mosquitto_client_id(cmd->client); |
597 | 0 | admin_username = mosquitto_client_username(cmd->client); |
598 | 0 | mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | listRoles | verbose=%s | count=%d | offset=%d", |
599 | 0 | admin_clientid, admin_username, verbose?"true":"false", count, offset); |
600 | |
|
601 | 0 | return MOSQ_ERR_SUCCESS; |
602 | 0 | } |
603 | | |
604 | | |
605 | | int dynsec_roles__process_add_acl(struct dynsec__data *data, struct mosquitto_control_cmd *cmd) |
606 | 0 | { |
607 | 0 | const char *rolename; |
608 | 0 | struct dynsec__role *role; |
609 | 0 | struct dynsec__acl **acllist, *acl; |
610 | 0 | int rc; |
611 | 0 | const char *admin_clientid, *admin_username; |
612 | 0 | const char *topic; |
613 | 0 | size_t topic_len; |
614 | 0 | const char *acltype; |
615 | |
|
616 | 0 | if(json_get_string(cmd->j_command, "rolename", &rolename, false) != MOSQ_ERR_SUCCESS){ |
617 | 0 | mosquitto_control_command_reply(cmd, "Invalid/missing rolename"); |
618 | 0 | return MOSQ_ERR_INVAL; |
619 | 0 | } |
620 | 0 | if(mosquitto_validate_utf8(rolename, (int)strlen(rolename)) != MOSQ_ERR_SUCCESS){ |
621 | 0 | mosquitto_control_command_reply(cmd, "Role name not valid UTF-8"); |
622 | 0 | return MOSQ_ERR_INVAL; |
623 | 0 | } |
624 | | |
625 | 0 | role = dynsec_roles__find(data, rolename); |
626 | 0 | if(role == NULL){ |
627 | 0 | mosquitto_control_command_reply(cmd, "Role not found"); |
628 | 0 | return MOSQ_ERR_SUCCESS; |
629 | 0 | } |
630 | | |
631 | 0 | if(json_get_string(cmd->j_command, "acltype", &acltype, false) != MOSQ_ERR_SUCCESS){ |
632 | 0 | mosquitto_control_command_reply(cmd, "Invalid/missing acltype"); |
633 | 0 | return MOSQ_ERR_SUCCESS; |
634 | 0 | } |
635 | 0 | if(!strcasecmp(acltype, ACL_TYPE_PUB_C_SEND)){ |
636 | 0 | acllist = &role->acls.publish_c_send; |
637 | 0 | }else if(!strcasecmp(acltype, ACL_TYPE_PUB_C_RECV)){ |
638 | 0 | acllist = &role->acls.publish_c_recv; |
639 | 0 | }else if(!strcasecmp(acltype, ACL_TYPE_SUB_LITERAL)){ |
640 | 0 | acllist = &role->acls.subscribe_literal; |
641 | 0 | }else if(!strcasecmp(acltype, ACL_TYPE_SUB_PATTERN)){ |
642 | 0 | acllist = &role->acls.subscribe_pattern; |
643 | 0 | }else if(!strcasecmp(acltype, ACL_TYPE_UNSUB_LITERAL)){ |
644 | 0 | acllist = &role->acls.unsubscribe_literal; |
645 | 0 | }else if(!strcasecmp(acltype, ACL_TYPE_UNSUB_PATTERN)){ |
646 | 0 | acllist = &role->acls.unsubscribe_pattern; |
647 | 0 | }else{ |
648 | 0 | mosquitto_control_command_reply(cmd, "Unknown acltype"); |
649 | 0 | return MOSQ_ERR_SUCCESS; |
650 | 0 | } |
651 | | |
652 | 0 | if(json_get_string(cmd->j_command, "topic", &topic, false) == MOSQ_ERR_SUCCESS){ |
653 | 0 | topic_len = strlen(topic); |
654 | 0 | if(mosquitto_validate_utf8(topic, (int)topic_len) != MOSQ_ERR_SUCCESS){ |
655 | 0 | mosquitto_control_command_reply(cmd, "Topic not valid UTF-8"); |
656 | 0 | return MOSQ_ERR_INVAL; |
657 | 0 | } |
658 | 0 | rc = mosquitto_sub_topic_check(topic); |
659 | 0 | if(rc != MOSQ_ERR_SUCCESS){ |
660 | 0 | mosquitto_control_command_reply(cmd, "Invalid ACL topic"); |
661 | 0 | return MOSQ_ERR_INVAL; |
662 | 0 | } |
663 | 0 | }else{ |
664 | 0 | mosquitto_control_command_reply(cmd, "Invalid/missing topic"); |
665 | 0 | return MOSQ_ERR_SUCCESS; |
666 | 0 | } |
667 | | |
668 | 0 | HASH_FIND(hh, *acllist, topic, topic_len, acl); |
669 | 0 | if(acl){ |
670 | 0 | mosquitto_control_command_reply(cmd, "ACL with this topic already exists"); |
671 | 0 | return MOSQ_ERR_SUCCESS; |
672 | 0 | } |
673 | | |
674 | 0 | acl = mosquitto_calloc(1, sizeof(struct dynsec__acl) + topic_len + 1); |
675 | 0 | if(acl == NULL){ |
676 | 0 | mosquitto_control_command_reply(cmd, "Internal error"); |
677 | 0 | return MOSQ_ERR_SUCCESS; |
678 | 0 | } |
679 | 0 | strncpy(acl->topic, topic, topic_len+1); |
680 | |
|
681 | 0 | json_get_int(cmd->j_command, "priority", &acl->priority, true, 0); |
682 | 0 | if(acl->priority > PRIORITY_MAX) acl->priority = PRIORITY_MAX; |
683 | 0 | if(acl->priority < -PRIORITY_MAX) acl->priority = -PRIORITY_MAX; |
684 | 0 | json_get_bool(cmd->j_command, "allow", &acl->allow, true, false); |
685 | |
|
686 | 0 | HASH_ADD_INORDER(hh, *acllist, topic, topic_len, acl, insert_acl_cmp); |
687 | 0 | dynsec__config_batch_save(data); |
688 | 0 | mosquitto_control_command_reply(cmd, NULL); |
689 | |
|
690 | 0 | role__kick_all(data, role); |
691 | |
|
692 | 0 | admin_clientid = mosquitto_client_id(cmd->client); |
693 | 0 | admin_username = mosquitto_client_username(cmd->client); |
694 | 0 | mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | addRoleACL | rolename=%s | acltype=%s | topic=%s | priority=%d | allow=%s", |
695 | 0 | admin_clientid, admin_username, rolename, acltype, topic, acl->priority, acl->allow?"true":"false"); |
696 | |
|
697 | 0 | return MOSQ_ERR_SUCCESS; |
698 | 0 | } |
699 | | |
700 | | |
701 | | int dynsec_roles__process_remove_acl(struct dynsec__data *data, struct mosquitto_control_cmd *cmd) |
702 | 0 | { |
703 | 0 | const char *rolename; |
704 | 0 | struct dynsec__role *role; |
705 | 0 | struct dynsec__acl **acllist, *acl; |
706 | 0 | const char *topic; |
707 | 0 | int rc; |
708 | 0 | const char *admin_clientid, *admin_username; |
709 | 0 | const char *acltype; |
710 | |
|
711 | 0 | if(json_get_string(cmd->j_command, "rolename", &rolename, false) != MOSQ_ERR_SUCCESS){ |
712 | 0 | mosquitto_control_command_reply(cmd, "Invalid/missing rolename"); |
713 | 0 | return MOSQ_ERR_INVAL; |
714 | 0 | } |
715 | 0 | if(mosquitto_validate_utf8(rolename, (int)strlen(rolename)) != MOSQ_ERR_SUCCESS){ |
716 | 0 | mosquitto_control_command_reply(cmd, "Role name not valid UTF-8"); |
717 | 0 | return MOSQ_ERR_INVAL; |
718 | 0 | } |
719 | | |
720 | 0 | role = dynsec_roles__find(data, rolename); |
721 | 0 | if(role == NULL){ |
722 | 0 | mosquitto_control_command_reply(cmd, "Role not found"); |
723 | 0 | return MOSQ_ERR_SUCCESS; |
724 | 0 | } |
725 | | |
726 | 0 | if(json_get_string(cmd->j_command, "acltype", &acltype, false) != MOSQ_ERR_SUCCESS){ |
727 | 0 | mosquitto_control_command_reply(cmd, "Invalid/missing acltype"); |
728 | 0 | return MOSQ_ERR_SUCCESS; |
729 | 0 | } |
730 | 0 | if(!strcasecmp(acltype, ACL_TYPE_PUB_C_SEND)){ |
731 | 0 | acllist = &role->acls.publish_c_send; |
732 | 0 | }else if(!strcasecmp(acltype, ACL_TYPE_PUB_C_RECV)){ |
733 | 0 | acllist = &role->acls.publish_c_recv; |
734 | 0 | }else if(!strcasecmp(acltype, ACL_TYPE_SUB_LITERAL)){ |
735 | 0 | acllist = &role->acls.subscribe_literal; |
736 | 0 | }else if(!strcasecmp(acltype, ACL_TYPE_SUB_PATTERN)){ |
737 | 0 | acllist = &role->acls.subscribe_pattern; |
738 | 0 | }else if(!strcasecmp(acltype, ACL_TYPE_UNSUB_LITERAL)){ |
739 | 0 | acllist = &role->acls.unsubscribe_literal; |
740 | 0 | }else if(!strcasecmp(acltype, ACL_TYPE_UNSUB_PATTERN)){ |
741 | 0 | acllist = &role->acls.unsubscribe_pattern; |
742 | 0 | }else{ |
743 | 0 | mosquitto_control_command_reply(cmd, "Unknown acltype"); |
744 | 0 | return MOSQ_ERR_SUCCESS; |
745 | 0 | } |
746 | | |
747 | 0 | if(json_get_string(cmd->j_command, "topic", &topic, false)){ |
748 | 0 | mosquitto_control_command_reply(cmd, "Invalid/missing topic"); |
749 | 0 | return MOSQ_ERR_SUCCESS; |
750 | 0 | } |
751 | 0 | if(mosquitto_validate_utf8(topic, (int)strlen(topic)) != MOSQ_ERR_SUCCESS){ |
752 | 0 | mosquitto_control_command_reply(cmd, "Topic not valid UTF-8"); |
753 | 0 | return MOSQ_ERR_INVAL; |
754 | 0 | } |
755 | 0 | rc = mosquitto_sub_topic_check(topic); |
756 | 0 | if(rc != MOSQ_ERR_SUCCESS){ |
757 | 0 | mosquitto_control_command_reply(cmd, "Invalid ACL topic"); |
758 | 0 | return MOSQ_ERR_INVAL; |
759 | 0 | } |
760 | | |
761 | 0 | HASH_FIND(hh, *acllist, topic, strlen(topic), acl); |
762 | 0 | if(acl){ |
763 | 0 | role__free_acl(acllist, acl); |
764 | 0 | dynsec__config_batch_save(data); |
765 | 0 | mosquitto_control_command_reply(cmd, NULL); |
766 | |
|
767 | 0 | role__kick_all(data, role); |
768 | |
|
769 | 0 | admin_clientid = mosquitto_client_id(cmd->client); |
770 | 0 | admin_username = mosquitto_client_username(cmd->client); |
771 | 0 | mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | removeRoleACL | rolename=%s | acltype=%s | topic=%s", |
772 | 0 | admin_clientid, admin_username, rolename, acltype, topic); |
773 | |
|
774 | 0 | }else{ |
775 | 0 | mosquitto_control_command_reply(cmd, "ACL not found"); |
776 | 0 | } |
777 | |
|
778 | 0 | return MOSQ_ERR_SUCCESS; |
779 | 0 | } |
780 | | |
781 | | |
782 | | int dynsec_roles__process_get(struct dynsec__data *data, struct mosquitto_control_cmd *cmd) |
783 | 0 | { |
784 | 0 | const char *rolename; |
785 | 0 | struct dynsec__role *role; |
786 | 0 | cJSON *tree, *j_role, *j_data; |
787 | 0 | const char *admin_clientid, *admin_username; |
788 | |
|
789 | 0 | if(json_get_string(cmd->j_command, "rolename", &rolename, false) != MOSQ_ERR_SUCCESS){ |
790 | 0 | mosquitto_control_command_reply(cmd, "Invalid/missing rolename"); |
791 | 0 | return MOSQ_ERR_INVAL; |
792 | 0 | } |
793 | 0 | if(mosquitto_validate_utf8(rolename, (int)strlen(rolename)) != MOSQ_ERR_SUCCESS){ |
794 | 0 | mosquitto_control_command_reply(cmd, "Role name not valid UTF-8"); |
795 | 0 | return MOSQ_ERR_INVAL; |
796 | 0 | } |
797 | | |
798 | 0 | role = dynsec_roles__find(data, rolename); |
799 | 0 | if(role == NULL){ |
800 | 0 | mosquitto_control_command_reply(cmd, "Role not found"); |
801 | 0 | return MOSQ_ERR_SUCCESS; |
802 | 0 | } |
803 | | |
804 | 0 | tree = cJSON_CreateObject(); |
805 | 0 | if(tree == NULL){ |
806 | 0 | mosquitto_control_command_reply(cmd, "Internal error"); |
807 | 0 | return MOSQ_ERR_NOMEM; |
808 | 0 | } |
809 | | |
810 | 0 | if(cJSON_AddStringToObject(tree, "command", "getRole") == NULL |
811 | 0 | || (j_data = cJSON_AddObjectToObject(tree, "data")) == NULL |
812 | 0 | || (cmd->correlation_data && cJSON_AddStringToObject(tree, "correlationData", cmd->correlation_data) == NULL) |
813 | 0 | ){ |
814 | |
|
815 | 0 | cJSON_Delete(tree); |
816 | 0 | mosquitto_control_command_reply(cmd, "Internal error"); |
817 | 0 | return MOSQ_ERR_NOMEM; |
818 | 0 | } |
819 | | |
820 | 0 | j_role = add_role_to_json(role, true); |
821 | 0 | if(j_role == NULL){ |
822 | 0 | cJSON_Delete(tree); |
823 | 0 | mosquitto_control_command_reply(cmd, "Internal error"); |
824 | 0 | return MOSQ_ERR_NOMEM; |
825 | 0 | } |
826 | 0 | cJSON_AddItemToObject(j_data, "role", j_role); |
827 | 0 | cJSON_AddItemToArray(cmd->j_responses, tree); |
828 | |
|
829 | 0 | admin_clientid = mosquitto_client_id(cmd->client); |
830 | 0 | admin_username = mosquitto_client_username(cmd->client); |
831 | 0 | mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | getRole | rolename=%s", |
832 | 0 | admin_clientid, admin_username, rolename); |
833 | |
|
834 | 0 | return MOSQ_ERR_SUCCESS; |
835 | 0 | } |
836 | | |
837 | | |
838 | | int dynsec_roles__process_modify(struct dynsec__data *data, struct mosquitto_control_cmd *cmd) |
839 | 0 | { |
840 | 0 | const char *rolename; |
841 | 0 | const char *text_name, *text_description; |
842 | 0 | struct dynsec__role *role; |
843 | 0 | cJSON *j_acls; |
844 | 0 | bool allow_wildcard_subs; |
845 | 0 | bool do_kick = false; |
846 | 0 | struct dynsec__acl *tmp_publish_c_send = NULL, *tmp_publish_c_recv = NULL; |
847 | 0 | struct dynsec__acl *tmp_subscribe_literal = NULL, *tmp_subscribe_pattern = NULL; |
848 | 0 | struct dynsec__acl *tmp_unsubscribe_literal = NULL, *tmp_unsubscribe_pattern = NULL; |
849 | 0 | const char *admin_clientid, *admin_username; |
850 | |
|
851 | 0 | if(json_get_string(cmd->j_command, "rolename", &rolename, false) != MOSQ_ERR_SUCCESS){ |
852 | 0 | mosquitto_control_command_reply(cmd, "Invalid/missing rolename"); |
853 | 0 | return MOSQ_ERR_INVAL; |
854 | 0 | } |
855 | 0 | if(mosquitto_validate_utf8(rolename, (int)strlen(rolename)) != MOSQ_ERR_SUCCESS){ |
856 | 0 | mosquitto_control_command_reply(cmd, "Role name not valid UTF-8"); |
857 | 0 | return MOSQ_ERR_INVAL; |
858 | 0 | } |
859 | | |
860 | 0 | role = dynsec_roles__find(data, rolename); |
861 | 0 | if(role == NULL){ |
862 | 0 | mosquitto_control_command_reply(cmd, "Role does not exist"); |
863 | 0 | return MOSQ_ERR_INVAL; |
864 | 0 | } |
865 | | |
866 | 0 | if(json_get_string(cmd->j_command, "textname", &text_name, false) == MOSQ_ERR_SUCCESS){ |
867 | 0 | char *str = mosquitto_strdup(text_name); |
868 | 0 | if(str == NULL){ |
869 | 0 | mosquitto_control_command_reply(cmd, "Internal error"); |
870 | 0 | return MOSQ_ERR_NOMEM; |
871 | 0 | } |
872 | 0 | mosquitto_free(role->text_name); |
873 | 0 | role->text_name = str; |
874 | 0 | } |
875 | | |
876 | 0 | if(json_get_string(cmd->j_command, "textdescription", &text_description, false) == MOSQ_ERR_SUCCESS){ |
877 | 0 | char *str = mosquitto_strdup(text_description); |
878 | 0 | if(str == NULL){ |
879 | 0 | mosquitto_control_command_reply(cmd, "Internal error"); |
880 | 0 | return MOSQ_ERR_NOMEM; |
881 | 0 | } |
882 | 0 | mosquitto_free(role->text_description); |
883 | 0 | role->text_description = str; |
884 | 0 | } |
885 | | |
886 | 0 | if(json_get_bool(cmd->j_command, "allowwildcardsubs", &allow_wildcard_subs, false, true) == MOSQ_ERR_SUCCESS){ |
887 | 0 | if(role->allow_wildcard_subs != allow_wildcard_subs){ |
888 | 0 | role->allow_wildcard_subs = allow_wildcard_subs; |
889 | 0 | do_kick = true; |
890 | 0 | } |
891 | 0 | } |
892 | |
|
893 | 0 | j_acls = cJSON_GetObjectItem(cmd->j_command, "acls"); |
894 | 0 | if(j_acls && cJSON_IsArray(j_acls)){ |
895 | 0 | if(dynsec_roles__acl_load(j_acls, ACL_TYPE_PUB_C_SEND, &tmp_publish_c_send) != 0 |
896 | 0 | || dynsec_roles__acl_load(j_acls, ACL_TYPE_PUB_C_RECV, &tmp_publish_c_recv) != 0 |
897 | 0 | || dynsec_roles__acl_load(j_acls, ACL_TYPE_SUB_LITERAL, &tmp_subscribe_literal) != 0 |
898 | 0 | || dynsec_roles__acl_load(j_acls, ACL_TYPE_SUB_PATTERN, &tmp_subscribe_pattern) != 0 |
899 | 0 | || dynsec_roles__acl_load(j_acls, ACL_TYPE_UNSUB_LITERAL, &tmp_unsubscribe_literal) != 0 |
900 | 0 | || dynsec_roles__acl_load(j_acls, ACL_TYPE_UNSUB_PATTERN, &tmp_unsubscribe_pattern) != 0 |
901 | 0 | ){ |
902 | | |
903 | | /* Free any that were successful */ |
904 | 0 | role__free_all_acls(&tmp_publish_c_send); |
905 | 0 | role__free_all_acls(&tmp_publish_c_recv); |
906 | 0 | role__free_all_acls(&tmp_subscribe_literal); |
907 | 0 | role__free_all_acls(&tmp_subscribe_pattern); |
908 | 0 | role__free_all_acls(&tmp_unsubscribe_literal); |
909 | 0 | role__free_all_acls(&tmp_unsubscribe_pattern); |
910 | |
|
911 | 0 | mosquitto_control_command_reply(cmd, "Internal error"); |
912 | 0 | return MOSQ_ERR_NOMEM; |
913 | 0 | } |
914 | | |
915 | 0 | role__free_all_acls(&role->acls.publish_c_send); |
916 | 0 | role__free_all_acls(&role->acls.publish_c_recv); |
917 | 0 | role__free_all_acls(&role->acls.subscribe_literal); |
918 | 0 | role__free_all_acls(&role->acls.subscribe_pattern); |
919 | 0 | role__free_all_acls(&role->acls.unsubscribe_literal); |
920 | 0 | role__free_all_acls(&role->acls.unsubscribe_pattern); |
921 | |
|
922 | 0 | role->acls.publish_c_send = tmp_publish_c_send; |
923 | 0 | role->acls.publish_c_recv = tmp_publish_c_recv; |
924 | 0 | role->acls.subscribe_literal = tmp_subscribe_literal; |
925 | 0 | role->acls.subscribe_pattern = tmp_subscribe_pattern; |
926 | 0 | role->acls.unsubscribe_literal = tmp_unsubscribe_literal; |
927 | 0 | role->acls.unsubscribe_pattern = tmp_unsubscribe_pattern; |
928 | 0 | do_kick = true; |
929 | 0 | } |
930 | | |
931 | 0 | if(do_kick){ |
932 | 0 | role__kick_all(data, role); |
933 | 0 | } |
934 | 0 | dynsec__config_batch_save(data); |
935 | |
|
936 | 0 | mosquitto_control_command_reply(cmd, NULL); |
937 | |
|
938 | 0 | admin_clientid = mosquitto_client_id(cmd->client); |
939 | 0 | admin_username = mosquitto_client_username(cmd->client); |
940 | 0 | mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | modifyRole | rolename=%s", |
941 | 0 | admin_clientid, admin_username, rolename); |
942 | |
|
943 | 0 | return MOSQ_ERR_SUCCESS; |
944 | 0 | } |