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