Coverage Report

Created: 2023-09-19 06:58

/src/mosquitto/plugins/dynamic-security/groups.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 <uthash.h>
24
25
#include "mosquitto.h"
26
#include "mosquitto_broker.h"
27
#include "json_help.h"
28
29
#include "dynamic_security.h"
30
31
/* ################################################################
32
 * #
33
 * # Plugin global variables
34
 * #
35
 * ################################################################ */
36
37
/* ################################################################
38
 * #
39
 * # Function declarations
40
 * #
41
 * ################################################################ */
42
43
static int dynsec__remove_all_clients_from_group(struct dynsec__group *group);
44
static int dynsec__remove_all_roles_from_group(struct dynsec__group *group);
45
static cJSON *add_group_to_json(struct dynsec__group *group);
46
47
48
/* ################################################################
49
 * #
50
 * # Local variables
51
 * #
52
 * ################################################################ */
53
54
/* ################################################################
55
 * #
56
 * # Utility functions
57
 * #
58
 * ################################################################ */
59
60
static void group__kick_all(struct dynsec__data *data, struct dynsec__group *group)
61
0
{
62
0
  if(group == data->anonymous_group){
63
0
    dynsec_kicklist__add(data, NULL);
64
0
  }
65
0
  dynsec_clientlist__kick_all(data, group->clientlist);
66
0
}
67
68
69
static int group_cmp(void *a, void *b)
70
169k
{
71
169k
  struct dynsec__group *group_a = a;
72
169k
  struct dynsec__group *group_b = b;
73
74
169k
  return strcmp(group_a->groupname, group_b->groupname);
75
169k
}
76
77
78
struct dynsec__group *dynsec_groups__find(struct dynsec__data *data, const char *groupname)
79
104k
{
80
104k
  struct dynsec__group *group = NULL;
81
82
104k
  if(groupname){
83
104k
    HASH_FIND(hh, data->groups, groupname, strlen(groupname), group);
84
104k
  }
85
104k
  return group;
86
104k
}
87
88
static void group__free_item(struct dynsec__data *data, struct dynsec__group *group)
89
29.0k
{
90
29.0k
  struct dynsec__group *found_group = NULL;
91
92
29.0k
  if(group == NULL) return;
93
94
29.0k
  found_group = dynsec_groups__find(data, group->groupname);
95
29.0k
  if(found_group){
96
29.0k
    HASH_DEL(data->groups, found_group);
97
29.0k
  }
98
29.0k
  dynsec__remove_all_clients_from_group(group);
99
29.0k
  mosquitto_free(group->text_name);
100
29.0k
  mosquitto_free(group->text_description);
101
29.0k
  dynsec_rolelist__cleanup(&group->rolelist);
102
29.0k
  mosquitto_free(group);
103
29.0k
}
104
105
int dynsec_groups__process_add_role(struct dynsec__data *data, struct mosquitto_control_cmd *cmd, struct mosquitto *context)
106
0
{
107
0
  const char *groupname, *rolename;
108
0
  struct dynsec__group *group;
109
0
  struct dynsec__role *role;
110
0
  int priority;
111
0
  const char *admin_clientid, *admin_username;
112
0
  int rc;
113
114
0
  if(json_get_string(cmd->j_command, "groupname", &groupname, false) != MOSQ_ERR_SUCCESS){
115
0
    mosquitto_control_command_reply(cmd, "Invalid/missing groupname");
116
0
    return MOSQ_ERR_INVAL;
117
0
  }
118
0
  if(mosquitto_validate_utf8(groupname, (int)strlen(groupname)) != MOSQ_ERR_SUCCESS){
119
0
    mosquitto_control_command_reply(cmd, "Group name not valid UTF-8");
120
0
    return MOSQ_ERR_INVAL;
121
0
  }
122
123
0
  if(json_get_string(cmd->j_command, "rolename", &rolename, false) != MOSQ_ERR_SUCCESS){
124
0
    mosquitto_control_command_reply(cmd, "Invalid/missing rolename");
125
0
    return MOSQ_ERR_INVAL;
126
0
  }
127
0
  if(mosquitto_validate_utf8(rolename, (int)strlen(rolename)) != MOSQ_ERR_SUCCESS){
128
0
    mosquitto_control_command_reply(cmd, "Role name not valid UTF-8");
129
0
    return MOSQ_ERR_INVAL;
130
0
  }
131
0
  json_get_int(cmd->j_command, "priority", &priority, true, -1);
132
133
0
  group = dynsec_groups__find(data, groupname);
134
0
  if(group == NULL){
135
0
    mosquitto_control_command_reply(cmd, "Group not found");
136
0
    return MOSQ_ERR_SUCCESS;
137
0
  }
138
139
0
  role = dynsec_roles__find(data, rolename);
140
0
  if(role == NULL){
141
0
    mosquitto_control_command_reply(cmd, "Role not found");
142
0
    return MOSQ_ERR_SUCCESS;
143
0
  }
144
145
0
  admin_clientid = mosquitto_client_id(context);
146
0
  admin_username = mosquitto_client_username(context);
147
148
0
  rc = dynsec_rolelist__group_add(group, role, priority);
149
0
  if(rc == MOSQ_ERR_SUCCESS){
150
    /* Continue */
151
0
  }else if(rc == MOSQ_ERR_ALREADY_EXISTS){
152
0
    mosquitto_control_command_reply(cmd, "Group is already in this role");
153
0
    return MOSQ_ERR_ALREADY_EXISTS;
154
0
  }else{
155
0
    mosquitto_control_command_reply(cmd, "Internal error");
156
0
    return MOSQ_ERR_UNKNOWN;
157
0
  }
158
159
0
  mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | addGroupRole | groupname=%s | rolename=%s | priority=%d",
160
0
      admin_clientid, admin_username, groupname, rolename, priority);
161
162
0
  dynsec__config_batch_save(data);
163
0
  mosquitto_control_command_reply(cmd, NULL);
164
165
  /* Enforce any changes */
166
0
  group__kick_all(data, group);
167
168
0
  return MOSQ_ERR_SUCCESS;
169
0
}
170
171
172
void dynsec_groups__cleanup(struct dynsec__data *data)
173
4.81k
{
174
4.81k
  struct dynsec__group *group, *group_tmp = NULL;
175
176
29.0k
  HASH_ITER(hh, data->groups, group, group_tmp){
177
29.0k
    group__free_item(data, group);
178
29.0k
  }
179
4.81k
  data->anonymous_group = NULL;
180
4.81k
}
181
182
183
/* ################################################################
184
 * #
185
 * # Config file load
186
 * #
187
 * ################################################################ */
188
189
int dynsec_groups__config_load(struct dynsec__data *data, cJSON *tree)
190
4.67k
{
191
4.67k
  cJSON *j_groups, *j_group;
192
4.67k
  cJSON *j_clientlist;
193
4.67k
  cJSON *j_roles;
194
195
4.67k
  struct dynsec__group *group;
196
4.67k
  struct dynsec__role *role;
197
4.67k
  int priority;
198
4.67k
  const char *groupname;
199
4.67k
  size_t groupname_len;
200
201
4.67k
  j_groups = cJSON_GetObjectItem(tree, "groups");
202
4.67k
  if(j_groups == NULL){
203
2.78k
    return 0;
204
2.78k
  }
205
206
1.89k
  if(cJSON_IsArray(j_groups) == false){
207
1
    return 1;
208
1
  }
209
210
47.3k
  cJSON_ArrayForEach(j_group, j_groups){
211
47.3k
    if(cJSON_IsObject(j_group) == true){
212
      /* Group name */
213
46.7k
      const char *groupname;
214
46.7k
      if(json_get_string(j_group, "groupname", &groupname, false) != MOSQ_ERR_SUCCESS){
215
5.93k
        continue;
216
5.93k
      }
217
40.8k
      groupname_len = strlen(groupname);
218
40.8k
      if(groupname_len == 0){
219
205
        continue;
220
205
      }
221
40.6k
      if(dynsec_groups__find(data, groupname)){
222
11.5k
        continue;
223
11.5k
      }
224
225
29.0k
      group = mosquitto_calloc(1, sizeof(struct dynsec__group) + groupname_len + 1);
226
29.0k
      if(group == NULL){
227
0
        return MOSQ_ERR_NOMEM;
228
0
      }
229
29.0k
      strncpy(group->groupname, groupname, groupname_len+1);
230
231
      /* Text name */
232
29.0k
      const char *textname;
233
29.0k
      if(json_get_string(j_group, "textname", &textname, false) == MOSQ_ERR_SUCCESS){
234
1.82k
        if(textname){
235
1.82k
          group->text_name = strdup(textname);
236
1.82k
          if(group->text_name == NULL){
237
0
            mosquitto_free(group);
238
0
            continue;
239
0
          }
240
1.82k
        }
241
1.82k
      }
242
243
      /* Text description */
244
29.0k
      const char *textdescription;
245
29.0k
      if(json_get_string(j_group, "textdescription", &textdescription, false) == MOSQ_ERR_SUCCESS){
246
102
        if(textdescription){
247
102
          group->text_description = strdup(textdescription);
248
102
          if(group->text_description == NULL){
249
0
            mosquitto_free(group->text_name);
250
0
            mosquitto_free(group);
251
0
            continue;
252
0
          }
253
102
        }
254
102
      }
255
256
      /* Roles */
257
29.0k
      j_roles = cJSON_GetObjectItem(j_group, "roles");
258
29.0k
      if(j_roles && cJSON_IsArray(j_roles)){
259
4.00k
        cJSON *j_role;
260
261
26.0k
        cJSON_ArrayForEach(j_role, j_roles){
262
26.0k
          if(cJSON_IsObject(j_role)){
263
25.2k
            const char *rolename;
264
25.2k
            if(json_get_string(j_role, "rolename", &rolename, false) == MOSQ_ERR_SUCCESS){
265
20.0k
              json_get_int(j_role, "priority", &priority, true, -1);
266
20.0k
              role = dynsec_roles__find(data, rolename);
267
20.0k
              dynsec_rolelist__group_add(group, role, priority);
268
20.0k
            }
269
25.2k
          }
270
26.0k
        }
271
4.00k
      }
272
273
      /* This must go before clients are loaded, otherwise the group won't be found */
274
29.0k
      HASH_ADD(hh, data->groups, groupname, groupname_len, group);
275
276
      /* Clients */
277
29.0k
      j_clientlist = cJSON_GetObjectItem(j_group, "clients");
278
29.0k
      if(j_clientlist && cJSON_IsArray(j_clientlist)){
279
4.47k
        cJSON *j_client;
280
49.5k
        cJSON_ArrayForEach(j_client, j_clientlist){
281
49.5k
          if(cJSON_IsObject(j_client)){
282
49.1k
            const char *username;
283
49.1k
            if(json_get_string(j_client, "username", &username, false) == MOSQ_ERR_SUCCESS){
284
40.6k
              json_get_int(j_client, "priority", &priority, true, -1);
285
40.6k
              dynsec_groups__add_client(data, username, group->groupname, priority, false);
286
40.6k
            }
287
49.1k
          }
288
49.5k
        }
289
4.47k
      }
290
29.0k
    }
291
47.3k
  }
292
1.89k
  HASH_SORT(data->groups, group_cmp);
293
294
1.89k
  if(json_get_string(tree, "anonymousGroup", &groupname, false) == MOSQ_ERR_SUCCESS){
295
16
    data->anonymous_group = dynsec_groups__find(data, groupname);
296
16
  }
297
298
1.89k
  return 0;
299
1.89k
}
300
301
302
/* ################################################################
303
 * #
304
 * # Config load and save
305
 * #
306
 * ################################################################ */
307
308
309
static int dynsec__config_add_groups(struct dynsec__data *data, cJSON *j_groups)
310
0
{
311
0
  struct dynsec__group *group, *group_tmp = NULL;
312
0
  cJSON *j_group, *j_clients, *j_roles;
313
314
0
  HASH_ITER(hh, data->groups, group, group_tmp){
315
0
    j_group = cJSON_CreateObject();
316
0
    if(j_group == NULL) return 1;
317
0
    cJSON_AddItemToArray(j_groups, j_group);
318
319
0
    if(cJSON_AddStringToObject(j_group, "groupname", group->groupname) == NULL
320
0
        || (group->text_name && cJSON_AddStringToObject(j_group, "textname", group->text_name) == NULL)
321
0
        || (group->text_description && cJSON_AddStringToObject(j_group, "textdescription", group->text_description) == NULL)
322
0
        ){
323
324
0
      return 1;
325
0
    }
326
327
0
    j_roles = dynsec_rolelist__all_to_json(group->rolelist);
328
0
    if(j_roles == NULL){
329
0
      return 1;
330
0
    }
331
0
    cJSON_AddItemToObject(j_group, "roles", j_roles);
332
333
0
    j_clients = dynsec_clientlist__all_to_json(group->clientlist);
334
0
    if(j_clients == NULL){
335
0
      return 1;
336
0
    }
337
0
    cJSON_AddItemToObject(j_group, "clients", j_clients);
338
0
  }
339
340
0
  return 0;
341
0
}
342
343
344
int dynsec_groups__config_save(struct dynsec__data *data, cJSON *tree)
345
0
{
346
0
  cJSON *j_groups;
347
348
0
  j_groups = cJSON_CreateArray();
349
0
  if(j_groups == NULL){
350
0
    return 1;
351
0
  }
352
0
  cJSON_AddItemToObject(tree, "groups", j_groups);
353
0
  if(dynsec__config_add_groups(data, j_groups)){
354
0
    return 1;
355
0
  }
356
357
0
  if(data->anonymous_group
358
0
      && cJSON_AddStringToObject(tree, "anonymousGroup", data->anonymous_group->groupname) == NULL){
359
360
0
    return 1;
361
0
  }
362
363
0
  return 0;
364
0
}
365
366
367
int dynsec_groups__process_create(struct dynsec__data *data, struct mosquitto_control_cmd *cmd, struct mosquitto *context)
368
0
{
369
0
  const char *groupname, *text_name, *text_description;
370
0
  struct dynsec__group *group = NULL;
371
0
  int rc = MOSQ_ERR_SUCCESS;
372
0
  const char *admin_clientid, *admin_username;
373
0
  size_t groupname_len;
374
375
0
  if(json_get_string(cmd->j_command, "groupname", &groupname, false) != MOSQ_ERR_SUCCESS){
376
0
    mosquitto_control_command_reply(cmd, "Invalid/missing groupname");
377
0
    return MOSQ_ERR_INVAL;
378
0
  }
379
0
  groupname_len = strlen(groupname);
380
0
  if(groupname_len == 0){
381
0
    mosquitto_control_command_reply(cmd, "Empty groupname");
382
0
    return MOSQ_ERR_INVAL;
383
0
  }
384
0
  if(mosquitto_validate_utf8(groupname, (int)groupname_len) != MOSQ_ERR_SUCCESS){
385
0
    mosquitto_control_command_reply(cmd, "Group name not valid UTF-8");
386
0
    return MOSQ_ERR_INVAL;
387
0
  }
388
389
0
  if(json_get_string(cmd->j_command, "textname", &text_name, true) != MOSQ_ERR_SUCCESS){
390
0
    mosquitto_control_command_reply(cmd, "Invalid/missing textname");
391
0
    return MOSQ_ERR_INVAL;
392
0
  }
393
394
0
  if(json_get_string(cmd->j_command, "textdescription", &text_description, true) != MOSQ_ERR_SUCCESS){
395
0
    mosquitto_control_command_reply(cmd, "Invalid/missing textdescription");
396
0
    return MOSQ_ERR_INVAL;
397
0
  }
398
399
0
  group = dynsec_groups__find(data, groupname);
400
0
  if(group){
401
0
    mosquitto_control_command_reply(cmd, "Group already exists");
402
0
    return MOSQ_ERR_SUCCESS;
403
0
  }
404
405
0
  group = mosquitto_calloc(1, sizeof(struct dynsec__group) + groupname_len + 1);
406
0
  if(group == NULL){
407
0
    mosquitto_control_command_reply(cmd, "Internal error");
408
0
    return MOSQ_ERR_NOMEM;
409
0
  }
410
0
  strncpy(group->groupname, groupname, groupname_len+1);
411
0
  if(text_name){
412
0
    group->text_name = strdup(text_name);
413
0
    if(group->text_name == NULL){
414
0
      mosquitto_control_command_reply(cmd, "Internal error");
415
0
      group__free_item(data, group);
416
0
      return MOSQ_ERR_NOMEM;
417
0
    }
418
0
  }
419
0
  if(text_description){
420
0
    group->text_description = strdup(text_description);
421
0
    if(group->text_description == NULL){
422
0
      mosquitto_control_command_reply(cmd, "Internal error");
423
0
      group__free_item(data, group);
424
0
      return MOSQ_ERR_NOMEM;
425
0
    }
426
0
  }
427
428
0
  rc = dynsec_rolelist__load_from_json(data, cmd->j_command, &group->rolelist);
429
0
  if(rc == MOSQ_ERR_SUCCESS || rc == ERR_LIST_NOT_FOUND){
430
0
  }else if(rc == MOSQ_ERR_NOT_FOUND){
431
0
    mosquitto_control_command_reply(cmd, "Role not found");
432
0
    group__free_item(data, group);
433
0
    return MOSQ_ERR_INVAL;
434
0
  }else{
435
0
    mosquitto_control_command_reply(cmd, "Internal error");
436
0
    group__free_item(data, group);
437
0
    return MOSQ_ERR_INVAL;
438
0
  }
439
440
0
  HASH_ADD_INORDER(hh, data->groups, groupname, groupname_len, group, group_cmp);
441
442
0
  admin_clientid = mosquitto_client_id(context);
443
0
  admin_username = mosquitto_client_username(context);
444
0
  mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | createGroup | groupname=%s",
445
0
      admin_clientid, admin_username, groupname);
446
447
0
  dynsec__config_batch_save(data);
448
0
  mosquitto_control_command_reply(cmd, NULL);
449
0
  return MOSQ_ERR_SUCCESS;
450
0
}
451
452
453
int dynsec_groups__process_delete(struct dynsec__data *data, struct mosquitto_control_cmd *cmd, struct mosquitto *context)
454
0
{
455
0
  const char *groupname;
456
0
  struct dynsec__group *group;
457
0
  const char *admin_clientid, *admin_username;
458
459
0
  if(json_get_string(cmd->j_command, "groupname", &groupname, false) != MOSQ_ERR_SUCCESS){
460
0
    mosquitto_control_command_reply(cmd, "Invalid/missing groupname");
461
0
    return MOSQ_ERR_INVAL;
462
0
  }
463
0
  if(mosquitto_validate_utf8(groupname, (int)strlen(groupname)) != MOSQ_ERR_SUCCESS){
464
0
    mosquitto_control_command_reply(cmd, "Group name not valid UTF-8");
465
0
    return MOSQ_ERR_INVAL;
466
0
  }
467
468
0
  group = dynsec_groups__find(data, groupname);
469
0
  if(group){
470
0
    if(group == data->anonymous_group){
471
0
      mosquitto_control_command_reply(cmd, "Deleting the anonymous group is forbidden");
472
0
      return MOSQ_ERR_INVAL;
473
0
    }
474
475
    /* Enforce any changes */
476
0
    group__kick_all(data, group);
477
478
0
    dynsec__remove_all_roles_from_group(group);
479
0
    group__free_item(data, group);
480
0
    dynsec__config_batch_save(data);
481
0
    mosquitto_control_command_reply(cmd, NULL);
482
483
0
    admin_clientid = mosquitto_client_id(context);
484
0
    admin_username = mosquitto_client_username(context);
485
0
    mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | deleteGroup | groupname=%s",
486
0
        admin_clientid, admin_username, groupname);
487
488
0
    return MOSQ_ERR_SUCCESS;
489
0
  }else{
490
0
    mosquitto_control_command_reply(cmd, "Group not found");
491
0
    return MOSQ_ERR_SUCCESS;
492
0
  }
493
0
}
494
495
496
int dynsec_groups__add_client(struct dynsec__data *data, const char *username, const char *groupname, int priority, bool update_config)
497
40.6k
{
498
40.6k
  struct dynsec__client *client;
499
40.6k
  struct dynsec__clientlist *clientlist;
500
40.6k
  struct dynsec__group *group;
501
40.6k
  int rc;
502
503
40.6k
  client = dynsec_clients__find(data, username);
504
40.6k
  if(client == NULL){
505
5.50k
    return ERR_USER_NOT_FOUND;
506
5.50k
  }
507
508
35.1k
  group = dynsec_groups__find(data, groupname);
509
35.1k
  if(group == NULL){
510
0
    return ERR_GROUP_NOT_FOUND;
511
0
  }
512
513
35.1k
  HASH_FIND(hh, group->clientlist, username, strlen(username), clientlist);
514
35.1k
  if(clientlist != NULL){
515
    /* Client is already in the group */
516
9.73k
    return MOSQ_ERR_ALREADY_EXISTS;
517
9.73k
  }
518
519
25.4k
  rc = dynsec_clientlist__add(&group->clientlist, client, priority);
520
25.4k
  if(rc){
521
0
    return rc;
522
0
  }
523
25.4k
  rc = dynsec_grouplist__add(&client->grouplist, group, priority);
524
25.4k
  if(rc){
525
0
    dynsec_clientlist__remove(&group->clientlist, client);
526
0
    return rc;
527
0
  }
528
529
25.4k
  if(update_config){
530
0
    dynsec__config_batch_save(data);
531
0
  }
532
533
25.4k
  return MOSQ_ERR_SUCCESS;
534
25.4k
}
535
536
537
int dynsec_groups__process_add_client(struct dynsec__data *data, struct mosquitto_control_cmd *cmd, struct mosquitto *context)
538
0
{
539
0
  const char *username, *groupname;
540
0
  int rc;
541
0
  int priority;
542
0
  const char *admin_clientid, *admin_username;
543
544
0
  if(json_get_string(cmd->j_command, "username", &username, false) != MOSQ_ERR_SUCCESS){
545
0
    mosquitto_control_command_reply(cmd, "Invalid/missing username");
546
0
    return MOSQ_ERR_INVAL;
547
0
  }
548
0
  if(mosquitto_validate_utf8(username, (int)strlen(username)) != MOSQ_ERR_SUCCESS){
549
0
    mosquitto_control_command_reply(cmd, "Username not valid UTF-8");
550
0
    return MOSQ_ERR_INVAL;
551
0
  }
552
553
0
  if(json_get_string(cmd->j_command, "groupname", &groupname, false) != MOSQ_ERR_SUCCESS){
554
0
    mosquitto_control_command_reply(cmd, "Invalid/missing groupname");
555
0
    return MOSQ_ERR_INVAL;
556
0
  }
557
0
  if(mosquitto_validate_utf8(groupname, (int)strlen(groupname)) != MOSQ_ERR_SUCCESS){
558
0
    mosquitto_control_command_reply(cmd, "Group name not valid UTF-8");
559
0
    return MOSQ_ERR_INVAL;
560
0
  }
561
562
0
  json_get_int(cmd->j_command, "priority", &priority, true, -1);
563
564
0
  rc = dynsec_groups__add_client(data, username, groupname, priority, true);
565
0
  if(rc == MOSQ_ERR_SUCCESS){
566
0
    admin_clientid = mosquitto_client_id(context);
567
0
    admin_username = mosquitto_client_username(context);
568
0
    mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | addGroupClient | groupname=%s | username=%s | priority=%d",
569
0
        admin_clientid, admin_username, groupname, username, priority);
570
571
0
    mosquitto_control_command_reply(cmd, NULL);
572
0
  }else if(rc == ERR_USER_NOT_FOUND){
573
0
    mosquitto_control_command_reply(cmd, "Client not found");
574
0
  }else if(rc == ERR_GROUP_NOT_FOUND){
575
0
    mosquitto_control_command_reply(cmd, "Group not found");
576
0
  }else if(rc == MOSQ_ERR_ALREADY_EXISTS){
577
0
    mosquitto_control_command_reply(cmd, "Client is already in this group");
578
0
  }else{
579
0
    mosquitto_control_command_reply(cmd, "Internal error");
580
0
  }
581
582
  /* Enforce any changes */
583
0
  dynsec_kicklist__add(data, username);
584
585
0
  return rc;
586
0
}
587
588
589
static int dynsec__remove_all_clients_from_group(struct dynsec__group *group)
590
29.0k
{
591
29.0k
  struct dynsec__clientlist *clientlist, *clientlist_tmp = NULL;
592
593
29.0k
  HASH_ITER(hh, group->clientlist, clientlist, clientlist_tmp){
594
    /* Remove client stored group reference */
595
25.4k
    dynsec_grouplist__remove(&clientlist->client->grouplist, group);
596
597
25.4k
    HASH_DELETE(hh, group->clientlist, clientlist);
598
25.4k
    mosquitto_free(clientlist);
599
25.4k
  }
600
601
29.0k
  return MOSQ_ERR_SUCCESS;
602
29.0k
}
603
604
static int dynsec__remove_all_roles_from_group(struct dynsec__group *group)
605
0
{
606
0
  struct dynsec__rolelist *rolelist, *rolelist_tmp = NULL;
607
608
0
  HASH_ITER(hh, group->rolelist, rolelist, rolelist_tmp){
609
0
    dynsec_rolelist__group_remove(group, rolelist->role);
610
0
  }
611
612
0
  return MOSQ_ERR_SUCCESS;
613
0
}
614
615
int dynsec_groups__remove_client(struct dynsec__data *data, const char *username, const char *groupname, bool update_config)
616
0
{
617
0
  struct dynsec__client *client;
618
0
  struct dynsec__group *group;
619
620
0
  client = dynsec_clients__find(data, username);
621
0
  if(client == NULL){
622
0
    return ERR_USER_NOT_FOUND;
623
0
  }
624
625
0
  group = dynsec_groups__find(data, groupname);
626
0
  if(group == NULL){
627
0
    return ERR_GROUP_NOT_FOUND;
628
0
  }
629
630
0
  dynsec_clientlist__remove(&group->clientlist, client);
631
0
  dynsec_grouplist__remove(&client->grouplist, group);
632
633
0
  if(update_config){
634
0
    dynsec__config_batch_save(data);
635
0
  }
636
0
  return MOSQ_ERR_SUCCESS;
637
0
}
638
639
int dynsec_groups__process_remove_client(struct dynsec__data *data, struct mosquitto_control_cmd *cmd, struct mosquitto *context)
640
0
{
641
0
  const char *username, *groupname;
642
0
  int rc;
643
0
  const char *admin_clientid, *admin_username;
644
645
0
  if(json_get_string(cmd->j_command, "username", &username, false) != MOSQ_ERR_SUCCESS){
646
0
    mosquitto_control_command_reply(cmd, "Invalid/missing username");
647
0
    return MOSQ_ERR_INVAL;
648
0
  }
649
0
  if(mosquitto_validate_utf8(username, (int)strlen(username)) != MOSQ_ERR_SUCCESS){
650
0
    mosquitto_control_command_reply(cmd, "Username not valid UTF-8");
651
0
    return MOSQ_ERR_INVAL;
652
0
  }
653
654
0
  if(json_get_string(cmd->j_command, "groupname", &groupname, false) != MOSQ_ERR_SUCCESS){
655
0
    mosquitto_control_command_reply(cmd, "Invalid/missing groupname");
656
0
    return MOSQ_ERR_INVAL;
657
0
  }
658
0
  if(mosquitto_validate_utf8(groupname, (int)strlen(groupname)) != MOSQ_ERR_SUCCESS){
659
0
    mosquitto_control_command_reply(cmd, "Group name not valid UTF-8");
660
0
    return MOSQ_ERR_INVAL;
661
0
  }
662
663
0
  rc = dynsec_groups__remove_client(data, username, groupname, true);
664
0
  if(rc == MOSQ_ERR_SUCCESS){
665
0
    admin_clientid = mosquitto_client_id(context);
666
0
    admin_username = mosquitto_client_username(context);
667
0
    mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | removeGroupClient | groupname=%s | username=%s",
668
0
        admin_clientid, admin_username, groupname, username);
669
670
0
    mosquitto_control_command_reply(cmd, NULL);
671
0
  }else if(rc == ERR_USER_NOT_FOUND){
672
0
    mosquitto_control_command_reply(cmd, "Client not found");
673
0
  }else if(rc == ERR_GROUP_NOT_FOUND){
674
0
    mosquitto_control_command_reply(cmd, "Group not found");
675
0
  }else{
676
0
    mosquitto_control_command_reply(cmd, "Internal error");
677
0
  }
678
679
  /* Enforce any changes */
680
0
  dynsec_kicklist__add(data, username);
681
682
0
  return rc;
683
0
}
684
685
686
static cJSON *add_group_to_json(struct dynsec__group *group)
687
0
{
688
0
  cJSON *j_group, *jtmp, *j_clientlist, *j_client, *j_rolelist;
689
0
  struct dynsec__clientlist *clientlist, *clientlist_tmp = NULL;
690
691
0
  j_group = cJSON_CreateObject();
692
0
  if(j_group == NULL){
693
0
    return NULL;
694
0
  }
695
696
0
  if(cJSON_AddStringToObject(j_group, "groupname", group->groupname) == NULL
697
0
      || (group->text_name && cJSON_AddStringToObject(j_group, "textname", group->text_name) == NULL)
698
0
      || (group->text_description && cJSON_AddStringToObject(j_group, "textdescription", group->text_description) == NULL)
699
0
      || (j_clientlist = cJSON_AddArrayToObject(j_group, "clients")) == NULL
700
0
      ){
701
702
0
    cJSON_Delete(j_group);
703
0
    return NULL;
704
0
  }
705
706
0
  HASH_ITER(hh, group->clientlist, clientlist, clientlist_tmp){
707
0
    j_client = cJSON_CreateObject();
708
0
    if(j_client == NULL){
709
0
      cJSON_Delete(j_group);
710
0
      return NULL;
711
0
    }
712
0
    cJSON_AddItemToArray(j_clientlist, j_client);
713
714
0
    jtmp = cJSON_CreateStringReference(clientlist->client->username);
715
0
    if(jtmp == NULL){
716
0
      cJSON_Delete(j_group);
717
0
      return NULL;
718
0
    }
719
0
    cJSON_AddItemToObject(j_client, "username", jtmp);
720
0
  }
721
722
0
  j_rolelist = dynsec_rolelist__all_to_json(group->rolelist);
723
0
  if(j_rolelist == NULL){
724
0
    cJSON_Delete(j_group);
725
0
    return NULL;
726
0
  }
727
0
  cJSON_AddItemToObject(j_group, "roles", j_rolelist);
728
729
0
  return j_group;
730
0
}
731
732
733
int dynsec_groups__process_list(struct dynsec__data *data, struct mosquitto_control_cmd *cmd, struct mosquitto *context)
734
0
{
735
0
  bool verbose;
736
0
  cJSON *tree, *j_groups, *j_group, *j_data;
737
0
  struct dynsec__group *group, *group_tmp = NULL;
738
0
  int i, count, offset;
739
0
  const char *admin_clientid, *admin_username;
740
741
0
  json_get_bool(cmd->j_command, "verbose", &verbose, true, false);
742
0
  json_get_int(cmd->j_command, "count", &count, true, -1);
743
0
  json_get_int(cmd->j_command, "offset", &offset, true, 0);
744
745
0
  tree = cJSON_CreateObject();
746
0
  if(tree == NULL){
747
0
    mosquitto_control_command_reply(cmd, "Internal error");
748
0
    return MOSQ_ERR_NOMEM;
749
0
  }
750
751
0
  if(cJSON_AddStringToObject(tree, "command", "listGroups") == NULL
752
0
      || (j_data = cJSON_AddObjectToObject(tree, "data")) == NULL
753
0
      || cJSON_AddIntToObject(j_data, "totalCount", (int)HASH_CNT(hh, data->groups)) == NULL
754
0
      || (j_groups = cJSON_AddArrayToObject(j_data, "groups")) == NULL
755
0
      || (cmd->correlation_data && cJSON_AddStringToObject(tree, "correlationData", cmd->correlation_data) == NULL)
756
0
      ){
757
758
0
    cJSON_Delete(tree);
759
0
    mosquitto_control_command_reply(cmd, "Internal error");
760
0
    return MOSQ_ERR_NOMEM;
761
0
  }
762
763
0
  i = 0;
764
0
  HASH_ITER(hh, data->groups, group, group_tmp){
765
0
    if(i>=offset){
766
0
      if(verbose){
767
0
        j_group = add_group_to_json(group);
768
0
        if(j_group == NULL){
769
0
          cJSON_Delete(tree);
770
0
          mosquitto_control_command_reply(cmd, "Internal error");
771
0
          return MOSQ_ERR_NOMEM;
772
0
        }
773
0
        cJSON_AddItemToArray(j_groups, j_group);
774
775
0
      }else{
776
0
        j_group = cJSON_CreateString(group->groupname);
777
0
        if(j_group){
778
0
          cJSON_AddItemToArray(j_groups, j_group);
779
0
        }else{
780
0
          cJSON_Delete(tree);
781
0
          mosquitto_control_command_reply(cmd, "Internal error");
782
0
          return MOSQ_ERR_NOMEM;
783
0
        }
784
0
      }
785
786
0
      if(count >= 0){
787
0
        count--;
788
0
        if(count <= 0){
789
0
          break;
790
0
        }
791
0
      }
792
0
    }
793
0
    i++;
794
0
  }
795
796
0
  cJSON_AddItemToArray(cmd->j_responses, tree);
797
798
0
  admin_clientid = mosquitto_client_id(context);
799
0
  admin_username = mosquitto_client_username(context);
800
0
  mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | listGroups | verbose=%s | count=%d | offset=%d",
801
0
      admin_clientid, admin_username, verbose?"true":"false", count, offset);
802
803
0
  return MOSQ_ERR_SUCCESS;
804
0
}
805
806
807
int dynsec_groups__process_get(struct dynsec__data *data, struct mosquitto_control_cmd *cmd, struct mosquitto *context)
808
0
{
809
0
  const char *groupname;
810
0
  cJSON *tree, *j_group, *j_data;
811
0
  struct dynsec__group *group;
812
0
  const char *admin_clientid, *admin_username;
813
814
0
  if(json_get_string(cmd->j_command, "groupname", &groupname, false) != MOSQ_ERR_SUCCESS){
815
0
    mosquitto_control_command_reply(cmd, "Invalid/missing groupname");
816
0
    return MOSQ_ERR_INVAL;
817
0
  }
818
0
  if(mosquitto_validate_utf8(groupname, (int)strlen(groupname)) != MOSQ_ERR_SUCCESS){
819
0
    mosquitto_control_command_reply(cmd, "Group name not valid UTF-8");
820
0
    return MOSQ_ERR_INVAL;
821
0
  }
822
823
0
  tree = cJSON_CreateObject();
824
0
  if(tree == NULL){
825
0
    mosquitto_control_command_reply(cmd, "Internal error");
826
0
    return MOSQ_ERR_NOMEM;
827
0
  }
828
829
0
  if(cJSON_AddStringToObject(tree, "command", "getGroup") == NULL
830
0
      || (j_data = cJSON_AddObjectToObject(tree, "data")) == NULL
831
0
      || (cmd->correlation_data && cJSON_AddStringToObject(tree, "correlationData", cmd->correlation_data) == NULL)
832
0
      ){
833
834
0
    cJSON_Delete(tree);
835
0
    mosquitto_control_command_reply(cmd, "Internal error");
836
0
    return MOSQ_ERR_NOMEM;
837
0
  }
838
839
0
  group = dynsec_groups__find(data, groupname);
840
0
  if(group){
841
0
    j_group = add_group_to_json(group);
842
0
    if(j_group == NULL){
843
0
      cJSON_Delete(tree);
844
0
      mosquitto_control_command_reply(cmd, "Internal error");
845
0
      return MOSQ_ERR_NOMEM;
846
0
    }
847
0
    cJSON_AddItemToObject(j_data, "group", j_group);
848
0
  }else{
849
0
    cJSON_Delete(tree);
850
0
    mosquitto_control_command_reply(cmd, "Group not found");
851
0
    return MOSQ_ERR_NOMEM;
852
0
  }
853
854
0
  cJSON_AddItemToArray(cmd->j_responses, tree);
855
856
0
  admin_clientid = mosquitto_client_id(context);
857
0
  admin_username = mosquitto_client_username(context);
858
0
  mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | getGroup | groupname=%s",
859
0
      admin_clientid, admin_username, groupname);
860
861
0
  return MOSQ_ERR_SUCCESS;
862
0
}
863
864
865
int dynsec_groups__process_remove_role(struct dynsec__data *data, struct mosquitto_control_cmd *cmd, struct mosquitto *context)
866
0
{
867
0
  const char *groupname, *rolename;
868
0
  struct dynsec__group *group;
869
0
  struct dynsec__role *role;
870
0
  const char *admin_clientid, *admin_username;
871
872
0
  if(json_get_string(cmd->j_command, "groupname", &groupname, false) != MOSQ_ERR_SUCCESS){
873
0
    mosquitto_control_command_reply(cmd, "Invalid/missing groupname");
874
0
    return MOSQ_ERR_INVAL;
875
0
  }
876
0
  if(mosquitto_validate_utf8(groupname, (int)strlen(groupname)) != MOSQ_ERR_SUCCESS){
877
0
    mosquitto_control_command_reply(cmd, "Group name not valid UTF-8");
878
0
    return MOSQ_ERR_INVAL;
879
0
  }
880
881
0
  if(json_get_string(cmd->j_command, "rolename", &rolename, false) != MOSQ_ERR_SUCCESS){
882
0
    mosquitto_control_command_reply(cmd, "Invalid/missing rolename");
883
0
    return MOSQ_ERR_INVAL;
884
0
  }
885
0
  if(mosquitto_validate_utf8(rolename, (int)strlen(rolename)) != MOSQ_ERR_SUCCESS){
886
0
    mosquitto_control_command_reply(cmd, "Role name not valid UTF-8");
887
0
    return MOSQ_ERR_INVAL;
888
0
  }
889
890
0
  group = dynsec_groups__find(data, groupname);
891
0
  if(group == NULL){
892
0
    mosquitto_control_command_reply(cmd, "Group not found");
893
0
    return MOSQ_ERR_SUCCESS;
894
0
  }
895
896
0
  role = dynsec_roles__find(data, rolename);
897
0
  if(role == NULL){
898
0
    mosquitto_control_command_reply(cmd, "Role not found");
899
0
    return MOSQ_ERR_SUCCESS;
900
0
  }
901
902
0
  dynsec_rolelist__group_remove(group, role);
903
0
  dynsec__config_batch_save(data);
904
0
  mosquitto_control_command_reply(cmd, NULL);
905
906
  /* Enforce any changes */
907
0
  group__kick_all(data, group);
908
909
0
  admin_clientid = mosquitto_client_id(context);
910
0
  admin_username = mosquitto_client_username(context);
911
0
  mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | removeGroupRole | groupname=%s | rolename=%s",
912
0
      admin_clientid, admin_username, groupname, rolename);
913
914
0
  return MOSQ_ERR_SUCCESS;
915
0
}
916
917
918
int dynsec_groups__process_modify(struct dynsec__data *data, struct mosquitto_control_cmd *cmd, struct mosquitto *context)
919
0
{
920
0
  const char *groupname = NULL;
921
0
  char *text_name = NULL, *text_description = NULL;
922
0
  struct dynsec__client *client = NULL;
923
0
  struct dynsec__group *group = NULL;
924
0
  struct dynsec__rolelist *rolelist = NULL;
925
0
  bool have_text_name = false, have_text_description = false, have_rolelist = false;
926
0
  const char *str;
927
0
  int rc;
928
0
  int priority;
929
0
  cJSON *j_client, *j_clients;
930
0
  const char *admin_clientid, *admin_username;
931
932
0
  if(json_get_string(cmd->j_command, "groupname", &groupname, false) != MOSQ_ERR_SUCCESS){
933
0
    mosquitto_control_command_reply(cmd, "Invalid/missing groupname");
934
0
    return MOSQ_ERR_INVAL;
935
0
  }
936
0
  if(mosquitto_validate_utf8(groupname, (int)strlen(groupname)) != MOSQ_ERR_SUCCESS){
937
0
    mosquitto_control_command_reply(cmd, "Group name not valid UTF-8");
938
0
    return MOSQ_ERR_INVAL;
939
0
  }
940
941
0
  group = dynsec_groups__find(data, groupname);
942
0
  if(group == NULL){
943
0
    mosquitto_control_command_reply(cmd, "Group not found");
944
0
    return MOSQ_ERR_INVAL;
945
0
  }
946
947
0
  if(json_get_string(cmd->j_command, "textname", &str, false) == MOSQ_ERR_SUCCESS){
948
0
    have_text_name = true;
949
0
    text_name = mosquitto_strdup(str);
950
0
    if(text_name == NULL){
951
0
      mosquitto_control_command_reply(cmd, "Internal error");
952
0
      rc = MOSQ_ERR_NOMEM;
953
0
      goto error;
954
0
    }
955
0
  }
956
957
0
  if(json_get_string(cmd->j_command, "textdescription", &str, false) == MOSQ_ERR_SUCCESS){
958
0
    have_text_description = true;
959
0
    text_description = mosquitto_strdup(str);
960
0
    if(text_description == NULL){
961
0
      mosquitto_control_command_reply(cmd, "Internal error");
962
0
      rc = MOSQ_ERR_NOMEM;
963
0
      goto error;
964
0
    }
965
0
  }
966
967
0
  rc = dynsec_rolelist__load_from_json(data, cmd->j_command, &rolelist);
968
0
  if(rc == MOSQ_ERR_SUCCESS){
969
    /* Apply changes below */
970
0
    have_rolelist = true;
971
0
  }else if(rc == ERR_LIST_NOT_FOUND){
972
    /* There was no list in the JSON, so no modification */
973
0
    rolelist = NULL;
974
0
  }else if(rc == MOSQ_ERR_NOT_FOUND){
975
0
    mosquitto_control_command_reply(cmd, "Role not found");
976
0
    rc = MOSQ_ERR_INVAL;
977
0
    goto error;
978
0
  }else{
979
0
    if(rc == MOSQ_ERR_INVAL){
980
0
      mosquitto_control_command_reply(cmd, "'roles' not an array or missing/invalid rolename");
981
0
    }else{
982
0
      mosquitto_control_command_reply(cmd, "Internal error");
983
0
    }
984
0
    rc = MOSQ_ERR_INVAL;
985
0
    goto error;
986
0
  }
987
988
0
  j_clients = cJSON_GetObjectItem(cmd->j_command, "clients");
989
0
  if(j_clients && cJSON_IsArray(j_clients)){
990
0
    const char *username;
991
    /* Iterate over array to check clients are valid before proceeding */
992
0
    cJSON_ArrayForEach(j_client, j_clients){
993
0
      if(cJSON_IsObject(j_client)){
994
0
        if(json_get_string(j_client, "username", &username, false) == MOSQ_ERR_SUCCESS){
995
0
          client = dynsec_clients__find(data, username);
996
0
          if(client == NULL){
997
0
            mosquitto_control_command_reply(cmd, "'clients' contains an object with a 'username' that does not exist");
998
0
            rc = MOSQ_ERR_INVAL;
999
0
            goto error;
1000
0
          }
1001
0
        }else{
1002
0
          mosquitto_control_command_reply(cmd, "'clients' contains an object with an invalid 'username'");
1003
0
          rc = MOSQ_ERR_INVAL;
1004
0
          goto error;
1005
0
        }
1006
0
      }
1007
0
    }
1008
1009
    /* Kick all clients in the *current* group */
1010
0
    group__kick_all(data, group);
1011
0
    dynsec__remove_all_clients_from_group(group);
1012
1013
    /* Now we can add the new clients to the group */
1014
0
    cJSON_ArrayForEach(j_client, j_clients){
1015
0
      if(cJSON_IsObject(j_client)){
1016
0
        if(json_get_string(j_client, "username", &username, false) == MOSQ_ERR_SUCCESS){
1017
0
          json_get_int(j_client, "priority", &priority, true, -1);
1018
0
          dynsec_groups__add_client(data, username, groupname, priority, false);
1019
0
        }
1020
0
      }
1021
0
    }
1022
0
  }
1023
1024
  /* Apply remaining changes to group, note that user changes are already applied */
1025
0
  if(have_text_name){
1026
0
    mosquitto_free(group->text_name);
1027
0
    group->text_name = text_name;
1028
0
  }
1029
1030
0
  if(have_text_description){
1031
0
    mosquitto_free(group->text_description);
1032
0
    group->text_description = text_description;
1033
0
  }
1034
1035
0
  if(have_rolelist){
1036
0
    dynsec_rolelist__cleanup(&group->rolelist);
1037
0
    group->rolelist = rolelist;
1038
0
  }
1039
1040
  /* And save */
1041
0
  dynsec__config_batch_save(data);
1042
1043
0
  mosquitto_control_command_reply(cmd, NULL);
1044
1045
  /* Enforce any changes - kick any clients in the *new* group */
1046
0
  group__kick_all(data, group);
1047
1048
0
  admin_clientid = mosquitto_client_id(context);
1049
0
  admin_username = mosquitto_client_username(context);
1050
0
  mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | modifyGroup | groupname=%s",
1051
0
      admin_clientid, admin_username, groupname);
1052
1053
0
  return MOSQ_ERR_SUCCESS;
1054
0
error:
1055
0
  mosquitto_free(text_name);
1056
0
  mosquitto_free(text_description);
1057
0
  dynsec_rolelist__cleanup(&rolelist);
1058
1059
0
  admin_clientid = mosquitto_client_id(context);
1060
0
  admin_username = mosquitto_client_username(context);
1061
0
  mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | modifyGroup | groupname=%s",
1062
0
      admin_clientid, admin_username, groupname);
1063
1064
0
  return rc;
1065
0
}
1066
1067
1068
int dynsec_groups__process_set_anonymous_group(struct dynsec__data *data, struct mosquitto_control_cmd *cmd, struct mosquitto *context)
1069
0
{
1070
0
  const char *groupname;
1071
0
  struct dynsec__group *group = NULL;
1072
0
  const char *admin_clientid, *admin_username;
1073
1074
0
  if(json_get_string(cmd->j_command, "groupname", &groupname, false) != MOSQ_ERR_SUCCESS){
1075
0
    mosquitto_control_command_reply(cmd, "Invalid/missing groupname");
1076
0
    return MOSQ_ERR_INVAL;
1077
0
  }
1078
0
  if(mosquitto_validate_utf8(groupname, (int)strlen(groupname)) != MOSQ_ERR_SUCCESS){
1079
0
    mosquitto_control_command_reply(cmd, "Group name not valid UTF-8");
1080
0
    return MOSQ_ERR_INVAL;
1081
0
  }
1082
1083
0
  group = dynsec_groups__find(data, groupname);
1084
0
  if(group == NULL){
1085
0
    mosquitto_control_command_reply(cmd, "Group not found");
1086
0
    return MOSQ_ERR_SUCCESS;
1087
0
  }
1088
1089
0
  data->anonymous_group = group;
1090
1091
0
  dynsec__config_batch_save(data);
1092
0
  mosquitto_control_command_reply(cmd, NULL);
1093
1094
  /* Enforce any changes */
1095
0
  dynsec_kicklist__add(data, NULL);
1096
1097
0
  admin_clientid = mosquitto_client_id(context);
1098
0
  admin_username = mosquitto_client_username(context);
1099
0
  mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | setAnonymousGroup | groupname=%s",
1100
0
      admin_clientid, admin_username, groupname);
1101
1102
0
  return MOSQ_ERR_SUCCESS;
1103
0
}
1104
1105
int dynsec_groups__process_get_anonymous_group(struct dynsec__data *data, struct mosquitto_control_cmd *cmd, struct mosquitto *context)
1106
0
{
1107
0
  cJSON *tree, *j_data, *j_group;
1108
0
  const char *groupname;
1109
0
  const char *admin_clientid, *admin_username;
1110
1111
0
  tree = cJSON_CreateObject();
1112
0
  if(tree == NULL){
1113
0
    mosquitto_control_command_reply(cmd, "Internal error");
1114
0
    return MOSQ_ERR_NOMEM;
1115
0
  }
1116
1117
0
  if(data->anonymous_group){
1118
0
    groupname = data->anonymous_group->groupname;
1119
0
  }else{
1120
0
    groupname = "";
1121
0
  }
1122
1123
0
  if(cJSON_AddStringToObject(tree, "command", "getAnonymousGroup") == NULL
1124
0
      || (j_data = cJSON_AddObjectToObject(tree, "data")) == NULL
1125
0
      || (j_group = cJSON_AddObjectToObject(j_data, "group")) == NULL
1126
0
      || cJSON_AddStringToObject(j_group, "groupname", groupname) == NULL
1127
0
      || (cmd->correlation_data && cJSON_AddStringToObject(tree, "correlationData", cmd->correlation_data) == NULL)
1128
0
      ){
1129
1130
0
    cJSON_Delete(tree);
1131
0
    mosquitto_control_command_reply(cmd, "Internal error");
1132
0
    return MOSQ_ERR_NOMEM;
1133
0
  }
1134
1135
0
  cJSON_AddItemToArray(cmd->j_responses, tree);
1136
1137
0
  admin_clientid = mosquitto_client_id(context);
1138
0
  admin_username = mosquitto_client_username(context);
1139
0
  mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | getAnonymousGroup",
1140
0
      admin_clientid, admin_username);
1141
1142
0
  return MOSQ_ERR_SUCCESS;
1143
0
}