Coverage Report

Created: 2026-02-14 06:46

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/mosquitto/plugins/dynamic-security/rolelist.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
#include "dynamic_security.h"
28
#include "json_help.h"
29
30
31
/* ################################################################
32
 * #
33
 * # Utility functions
34
 * #
35
 * ################################################################ */
36
37
38
static int rolelist_cmp(void *a, void *b)
39
684k
{
40
684k
  int prio;
41
684k
  struct dynsec__rolelist *rolelist_a = a;
42
684k
  struct dynsec__rolelist *rolelist_b = b;
43
44
684k
  prio = rolelist_b->priority - rolelist_a->priority;
45
684k
  if(prio == 0){
46
683k
    return strcmp(rolelist_a->rolename, rolelist_b->rolename);
47
683k
  }else{
48
1.03k
    return prio;
49
1.03k
  }
50
684k
}
51
52
53
static void dynsec_rolelist__free_item(struct dynsec__rolelist **base_rolelist, struct dynsec__rolelist *rolelist)
54
38.9k
{
55
38.9k
  HASH_DELETE(hh, *base_rolelist, rolelist);
56
38.9k
  mosquitto_free(rolelist);
57
38.9k
}
58
59
60
void dynsec_rolelist__cleanup(struct dynsec__rolelist **base_rolelist)
61
70.7k
{
62
70.7k
  struct dynsec__rolelist *rolelist, *rolelist_tmp;
63
64
70.7k
  HASH_ITER(hh, *base_rolelist, rolelist, rolelist_tmp){
65
38.9k
    dynsec_rolelist__free_item(base_rolelist, rolelist);
66
38.9k
  }
67
70.7k
}
68
69
70
static int dynsec_rolelist__remove_role(struct dynsec__rolelist **base_rolelist, const struct dynsec__role *role)
71
0
{
72
0
  struct dynsec__rolelist *found_rolelist;
73
74
0
  HASH_FIND(hh, *base_rolelist, role->rolename, strlen(role->rolename), found_rolelist);
75
0
  if(found_rolelist){
76
0
    dynsec_rolelist__free_item(base_rolelist, found_rolelist);
77
0
    return MOSQ_ERR_SUCCESS;
78
0
  }else{
79
0
    return MOSQ_ERR_NOT_FOUND;
80
0
  }
81
0
}
82
83
84
int dynsec_rolelist__client_remove(struct dynsec__client *client, struct dynsec__role *role)
85
0
{
86
0
  int rc;
87
0
  struct dynsec__clientlist *found_clientlist;
88
89
0
  rc = dynsec_rolelist__remove_role(&client->rolelist, role);
90
0
  if(rc){
91
0
    return rc;
92
0
  }
93
94
0
  HASH_FIND(hh, role->clientlist, client->username, strlen(client->username), found_clientlist);
95
0
  if(found_clientlist){
96
0
    HASH_DELETE(hh, role->clientlist, found_clientlist);
97
0
    mosquitto_free(found_clientlist);
98
0
    return MOSQ_ERR_SUCCESS;
99
0
  }else{
100
0
    return MOSQ_ERR_NOT_FOUND;
101
0
  }
102
0
}
103
104
105
void dynsec_rolelist__group_remove(struct dynsec__group *group, struct dynsec__role *role)
106
0
{
107
0
  dynsec_rolelist__remove_role(&group->rolelist, role);
108
0
  dynsec_grouplist__remove(&role->grouplist, group);
109
0
}
110
111
112
static int dynsec_rolelist__add(struct dynsec__rolelist **base_rolelist, struct dynsec__role *role, int priority)
113
58.4k
{
114
58.4k
  struct dynsec__rolelist *rolelist;
115
58.4k
  size_t rolename_len;
116
117
58.4k
  if(role == NULL){
118
12.1k
    return MOSQ_ERR_INVAL;
119
12.1k
  }
120
46.2k
  rolename_len = strlen(role->rolename);
121
46.2k
  if(rolename_len == 0){
122
0
    return MOSQ_ERR_INVAL;
123
0
  }
124
125
46.2k
  HASH_FIND(hh, *base_rolelist, role->rolename, rolename_len, rolelist);
126
46.2k
  if(rolelist){
127
7.35k
    return MOSQ_ERR_ALREADY_EXISTS;
128
38.9k
  }else{
129
38.9k
    rolelist = mosquitto_calloc(1, sizeof(struct dynsec__rolelist) + rolename_len + 1);
130
38.9k
    if(rolelist == NULL){
131
0
      return MOSQ_ERR_NOMEM;
132
0
    }
133
134
38.9k
    rolelist->role = role;
135
38.9k
    rolelist->priority = priority;
136
38.9k
    strncpy(rolelist->rolename, role->rolename, rolename_len+1);
137
38.9k
    HASH_ADD_INORDER(hh, *base_rolelist, rolename, rolename_len, rolelist, rolelist_cmp);
138
38.9k
    return MOSQ_ERR_SUCCESS;
139
38.9k
  }
140
46.2k
}
141
142
143
int dynsec_rolelist__client_add(struct dynsec__client *client, struct dynsec__role *role, int priority)
144
42.1k
{
145
42.1k
  struct dynsec__rolelist *rolelist;
146
42.1k
  int rc;
147
148
42.1k
  rc = dynsec_rolelist__add(&client->rolelist, role, priority);
149
42.1k
  if(rc){
150
15.2k
    return rc;
151
15.2k
  }
152
153
26.8k
  HASH_FIND(hh, client->rolelist, role->rolename, strlen(role->rolename), rolelist);
154
26.8k
  if(rolelist == NULL){
155
    /* This should never happen because the above add_role succeeded. */
156
0
    return MOSQ_ERR_UNKNOWN;
157
0
  }
158
159
26.8k
  rc = dynsec_clientlist__add(&role->clientlist, client, priority);
160
26.8k
  if(rc){
161
0
    dynsec_rolelist__remove_role(&client->rolelist, role);
162
0
  }
163
164
26.8k
  return rc;
165
26.8k
}
166
167
168
int dynsec_rolelist__group_add(struct dynsec__group *group, struct dynsec__role *role, int priority)
169
16.2k
{
170
16.2k
  int rc;
171
172
16.2k
  rc = dynsec_rolelist__add(&group->rolelist, role, priority);
173
16.2k
  if(rc){
174
4.25k
    return rc;
175
4.25k
  }
176
177
12.0k
  rc = dynsec_grouplist__add(&role->grouplist, group, priority);
178
12.0k
  if(rc){
179
0
    dynsec_rolelist__remove_role(&group->rolelist, role);
180
0
  }
181
12.0k
  return rc;
182
16.2k
}
183
184
185
int dynsec_rolelist__load_from_json(struct dynsec__data *data, cJSON *command, struct dynsec__rolelist **rolelist)
186
0
{
187
0
  cJSON *j_roles, *j_role;
188
0
  int priority;
189
0
  struct dynsec__role *role;
190
0
  const char *rolename;
191
192
0
  j_roles = cJSON_GetObjectItem(command, "roles");
193
0
  if(j_roles){
194
0
    if(cJSON_IsArray(j_roles)){
195
0
      cJSON_ArrayForEach(j_role, j_roles){
196
0
        if(json_get_string(j_role, "rolename", &rolename, false) == MOSQ_ERR_SUCCESS){
197
0
          json_get_int(j_role, "priority", &priority, true, -1);
198
0
          if(priority > PRIORITY_MAX){
199
0
            priority = PRIORITY_MAX;
200
0
          }
201
0
          role = dynsec_roles__find(data, rolename);
202
0
          if(role){
203
0
            dynsec_rolelist__add(rolelist, role, priority);
204
0
          }else{
205
0
            dynsec_rolelist__cleanup(rolelist);
206
0
            return MOSQ_ERR_NOT_FOUND;
207
0
          }
208
0
        }else{
209
0
          return MOSQ_ERR_INVAL;
210
0
        }
211
0
      }
212
0
      return MOSQ_ERR_SUCCESS;
213
0
    }else{
214
0
      return MOSQ_ERR_INVAL;
215
0
    }
216
0
  }else{
217
0
    return ERR_LIST_NOT_FOUND;
218
0
  }
219
0
}
220
221
222
cJSON *dynsec_rolelist__all_to_json(struct dynsec__rolelist *base_rolelist)
223
0
{
224
0
  struct dynsec__rolelist *rolelist, *rolelist_tmp;
225
0
  cJSON *j_roles, *j_role;
226
227
0
  j_roles = cJSON_CreateArray();
228
0
  if(j_roles == NULL){
229
0
    return NULL;
230
0
  }
231
232
0
  HASH_ITER(hh, base_rolelist, rolelist, rolelist_tmp){
233
0
    j_role = cJSON_CreateObject();
234
0
    if(j_role == NULL){
235
0
      cJSON_Delete(j_roles);
236
0
      return NULL;
237
0
    }
238
0
    cJSON_AddItemToArray(j_roles, j_role);
239
240
0
    if(cJSON_AddStringToObject(j_role, "rolename", rolelist->role->rolename) == NULL
241
0
        || (rolelist->priority != -1 && cJSON_AddIntToObject(j_role, "priority", rolelist->priority) == NULL)
242
0
        ){
243
244
0
      cJSON_Delete(j_roles);
245
0
      return NULL;
246
0
    }
247
0
  }
248
0
  return j_roles;
249
0
}