Coverage Report

Created: 2026-05-29 06:46

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