1 /*
2 * Copyright (c) 2022 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define LOG_MODULE_NAME net_lwm2m_ac_control
8 #define LOG_LEVEL CONFIG_LWM2M_LOG_LEVEL
9
10 #include <zephyr/logging/log.h>
11 LOG_MODULE_REGISTER(LOG_MODULE_NAME);
12
13 #include "lwm2m_obj_access_control.h"
14
15 #include <stdint.h>
16
17 #include <zephyr/init.h>
18
19 #define READ BIT(0)
20 #define WRITE BIT(1)
21 #define ACEXEC BIT(2)
22 #define DELETE BIT(3)
23 #define CREATE BIT(4)
24
25 /* For compatibility with lwm2m_op_perms */
26 #define WRITE_ATTR BIT(8)
27 #define DISCOVER BIT(9)
28
operation_to_acperm(int operation)29 static int operation_to_acperm(int operation)
30 {
31 switch (operation) {
32 case LWM2M_OP_READ:
33 return READ;
34
35 case LWM2M_OP_WRITE:
36 return WRITE;
37
38 case LWM2M_OP_EXECUTE:
39 return ACEXEC;
40
41 case LWM2M_OP_DELETE:
42 return DELETE;
43
44 case LWM2M_OP_CREATE:
45 return CREATE;
46
47 case LWM2M_OP_WRITE_ATTR:
48 return WRITE_ATTR;
49
50 case LWM2M_OP_DISCOVER:
51 return DISCOVER;
52 default:
53 return 0;
54 }
55 }
56
57 #define ACCESS_CONTROL_VERSION_MAJOR 1
58 #define ACCESS_CONTROL_VERSION_MINOR 0
59 #define AC_OBJ_ID LWM2M_OBJECT_ACCESS_CONTROL_ID
60 #define MAX_SERVER_COUNT CONFIG_LWM2M_SERVER_INSTANCE_COUNT
61 #define MAX_INSTANCE_COUNT CONFIG_LWM2M_ACCESS_CONTROL_INSTANCE_COUNT
62 #define OBJ_LVL_MAX_ID 65535
63
64 #define ACCESS_CONTROL_OBJECT_ID 0
65 #define ACCESS_CONTROL_OBJECT_INSTANCE_ID 1
66 #define ACCESS_CONTROL_ACL_ID 2
67 #define ACCESS_CONTROL_ACCESS_CONTROL_OWNER 3
68 #define ACCESS_CONTROL_MAX_ID 4
69
70
71 struct ac_data {
72 uint16_t obj_id;
73 uint16_t obj_inst_id;
74 int16_t acl[MAX_SERVER_COUNT + 1];
75 uint16_t ac_owner;
76 };
77
78 static struct ac_data ac_data[MAX_INSTANCE_COUNT];
79
80 static struct lwm2m_engine_obj ac_obj;
81 static struct lwm2m_engine_obj_field fields[] = {
82 OBJ_FIELD_DATA(ACCESS_CONTROL_OBJECT_ID, RW, U16),
83 OBJ_FIELD_DATA(ACCESS_CONTROL_OBJECT_INSTANCE_ID, RW, U16),
84 /* Mark obj id and obj_inst id is RO, but needs to be written to by bootstrap server */
85 OBJ_FIELD_DATA(ACCESS_CONTROL_ACL_ID, RW_OPT, U16),
86 OBJ_FIELD_DATA(ACCESS_CONTROL_ACCESS_CONTROL_OWNER, RW, U16),
87 };
88
89 static struct lwm2m_engine_obj_inst inst[MAX_INSTANCE_COUNT];
90 static struct lwm2m_engine_res res[MAX_INSTANCE_COUNT][ACCESS_CONTROL_MAX_ID];
91 /* Calculated as follows:
92 * + ACCESS_CONTROL_MAX_ID - 1 (not counting the acl instance)
93 * + MAX_SERVER_COUNT + 1 (one acl for every server plus default)
94 */
95 static struct lwm2m_engine_res_inst res_inst[MAX_INSTANCE_COUNT]
96 [MAX_SERVER_COUNT + ACCESS_CONTROL_MAX_ID];
97
obj_inst_to_index(uint16_t obj_id,uint16_t obj_inst_id)98 static int obj_inst_to_index(uint16_t obj_id, uint16_t obj_inst_id)
99 {
100 int i, ret = -ENOENT;
101
102 for (i = 0; i < ARRAY_SIZE(inst); i++) {
103 if (inst[i].obj && ac_data[i].obj_id == obj_id &&
104 ac_data[i].obj_inst_id == obj_inst_id) {
105 ret = i;
106 break;
107 }
108 }
109 return ret;
110 }
111
available_obj_inst_id(int obj_inst_id)112 static bool available_obj_inst_id(int obj_inst_id)
113 {
114 for (int index = 0; index < ARRAY_SIZE(inst); index++) {
115 if (inst[index].obj && inst[index].obj_inst_id == obj_inst_id) {
116 return false;
117 }
118 }
119 return true;
120 }
121
access_control_add(uint16_t obj_id,uint16_t obj_inst_id,int server_obj_inst_id)122 void access_control_add(uint16_t obj_id, uint16_t obj_inst_id, int server_obj_inst_id)
123 {
124 /* If ac_obj not created */
125 if (!ac_obj.fields) {
126 return;
127 }
128
129 if (obj_id == AC_OBJ_ID) {
130 return;
131 }
132
133 if (obj_inst_to_index(obj_id, obj_inst_id) >= 0) {
134 LOG_DBG("Access control for obj_inst /%d/%d already exist", obj_id, obj_inst_id);
135 return;
136 }
137
138 int index, avail = -1;
139
140 for (index = 0; index < ARRAY_SIZE(inst); index++) {
141 /* Save first available slot index */
142 if (avail < 0 && !inst[index].obj) {
143 avail = index;
144 }
145 }
146
147 if (avail < 0) {
148 LOG_ERR("Can not create access control instance - no more room: %u", obj_inst_id);
149 return;
150 }
151
152 int ssid;
153
154 if (server_obj_inst_id < 0) {
155 ssid = CONFIG_LWM2M_SERVER_DEFAULT_SSID;
156 } else {
157 ssid = lwm2m_server_get_ssid(server_obj_inst_id);
158 }
159
160 if (ssid < 0) {
161 LOG_DBG("No server object instance %d - using default", server_obj_inst_id);
162 ssid = CONFIG_LWM2M_SERVER_DEFAULT_SSID;
163 }
164
165 int ac_obj_inst_id = avail;
166
167 while (!available_obj_inst_id(ac_obj_inst_id)) {
168 ac_obj_inst_id++;
169 }
170 struct lwm2m_engine_obj_inst *obj_inst = NULL;
171
172 lwm2m_create_obj_inst(AC_OBJ_ID, ac_obj_inst_id, &obj_inst);
173 ac_data[avail].obj_id = obj_id;
174 ac_data[avail].obj_inst_id = obj_inst_id;
175 ac_data[avail].ac_owner = ssid;
176 }
177
access_control_add_obj(uint16_t obj_id,int server_obj_inst_id)178 void access_control_add_obj(uint16_t obj_id, int server_obj_inst_id)
179 {
180 access_control_add(obj_id, OBJ_LVL_MAX_ID, server_obj_inst_id);
181 }
182
access_control_remove(uint16_t obj_id,uint16_t obj_inst_id)183 void access_control_remove(uint16_t obj_id, uint16_t obj_inst_id)
184 {
185 /* If ac_obj not created */
186 if (!ac_obj.fields) {
187 return;
188 }
189
190 if (obj_id == AC_OBJ_ID) {
191 return;
192 }
193
194 int idx = obj_inst_to_index(obj_id, obj_inst_id);
195
196 if (idx < 0) {
197 LOG_DBG("Cannot remove access control for /%d/%d - not found", obj_id, obj_inst_id);
198 return;
199 }
200
201 ac_data[idx].obj_id = 0;
202 ac_data[idx].obj_inst_id = 0;
203 ac_data[idx].ac_owner = 0;
204 for (int i = 0; i < MAX_SERVER_COUNT + 1; i++) {
205 ac_data[idx].acl[i] = 0;
206 }
207 lwm2m_delete_obj_inst(AC_OBJ_ID, idx);
208 }
209
access_control_remove_obj(uint16_t obj_id)210 void access_control_remove_obj(uint16_t obj_id)
211 {
212 access_control_remove(obj_id, OBJ_LVL_MAX_ID);
213 }
214
check_acl_table(uint16_t obj_id,uint16_t obj_inst_id,uint16_t short_server_id,uint16_t access)215 static bool check_acl_table(uint16_t obj_id, uint16_t obj_inst_id, uint16_t short_server_id,
216 uint16_t access)
217 {
218 /* Get the index of the ac instance regarding obj_id and obj_inst_id */
219 int idx = obj_inst_to_index(obj_id, obj_inst_id);
220
221 if (idx < 0) {
222 LOG_DBG("Access control for obj_inst /%d/%d not found", obj_id, obj_inst_id);
223 return false;
224 }
225
226 uint16_t access_rights = 0;
227 uint16_t default_rights = 0;
228 bool server_has_acl = false;
229
230 for (int i = 0; i < MAX_SERVER_COUNT + 1; i++) {
231 int res_inst_id = res_inst[idx][ACCESS_CONTROL_ACL_ID + i].res_inst_id;
232 /* If server has access or if default exist */
233 if (res_inst_id == short_server_id) {
234 access_rights |= ac_data[idx].acl[i];
235 server_has_acl = true;
236 } else if (res_inst_id == 0) {
237 default_rights |= ac_data[idx].acl[i];
238 }
239 }
240
241 if (server_has_acl) {
242 return (access_rights & access) == access;
243 }
244
245 /* Full access if server is the ac_owner and no acl is specified for that server */
246 if (ac_data[idx].ac_owner == short_server_id) {
247 return true;
248 }
249
250 /* Return default rights */
251 return (default_rights & access) == access;
252 }
253
access_control_check_access(uint16_t obj_id,uint16_t obj_inst_id,uint16_t server_obj_inst,uint16_t operation,bool bootstrap_mode)254 int access_control_check_access(uint16_t obj_id, uint16_t obj_inst_id, uint16_t server_obj_inst,
255 uint16_t operation, bool bootstrap_mode)
256 {
257 #if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)
258 if (bootstrap_mode) {
259 return 0; /* Full access for bootstrap servers */
260 }
261 #else
262 ARG_UNUSED(bootstrap_mode);
263 #endif
264 /* If ac_obj not created */
265 if (!ac_obj.fields) {
266 return 0;
267 }
268 uint16_t access = operation_to_acperm(operation);
269 int short_server_id = lwm2m_server_get_ssid(server_obj_inst);
270
271 if (short_server_id < 0) {
272 LOG_ERR("No server obj instance %u exist", server_obj_inst);
273 return -EACCES;
274 }
275
276 if (obj_id == AC_OBJ_ID) {
277 switch (access) {
278 case READ:
279 return 0;
280 case ACEXEC:
281 case DELETE:
282 case CREATE:
283 return -EPERM; /* Method not allowed */
284 case WRITE: /* Only ac_owner can write to ac_obj */
285 for (int index = 0; index < ARRAY_SIZE(inst); index++) {
286 if (inst[index].obj && inst[index].obj_inst_id == obj_inst_id) {
287 if (ac_data[index].ac_owner == short_server_id) {
288 return 0;
289 }
290 }
291 }
292 return -EACCES;
293
294 default:
295 return -EACCES;
296 }
297 }
298
299 /* only DISCOVER, WRITE_ATTR and CREATE allowed on object */
300 if (obj_inst_id == OBJ_LVL_MAX_ID) {
301 if (access == DISCOVER || access == WRITE_ATTR) {
302 return 0;
303 }
304
305 if (access != CREATE) {
306 return -EACCES;
307 }
308 }
309
310 if (access == CREATE) {
311 obj_inst_id = OBJ_LVL_MAX_ID;
312 }
313
314 if (check_acl_table(obj_id, obj_inst_id, short_server_id, access)) {
315 return 0;
316 }
317
318 return -EACCES;
319 }
320
add_existing_objects(void)321 static void add_existing_objects(void)
322 {
323 /* register all objects in the sys-list */
324 struct lwm2m_engine_obj *obj;
325
326 SYS_SLIST_FOR_EACH_CONTAINER(lwm2m_engine_obj_list(), obj, node) {
327 access_control_add_obj(obj->obj_id, -1);
328 }
329
330 /* register all object instances in the sys-list */
331 struct lwm2m_engine_obj_inst *obj_inst;
332
333 SYS_SLIST_FOR_EACH_CONTAINER(lwm2m_engine_obj_inst_list(), obj_inst, node) {
334 access_control_add(obj_inst->obj->obj_id, obj_inst->obj_inst_id, -1);
335 }
336 }
337
write_validate_cb(uint16_t obj_inst_id,uint16_t res_id,uint16_t res_inst_id,uint8_t * data,uint16_t data_len,bool last_block,size_t total_size,size_t offset)338 static int write_validate_cb(uint16_t obj_inst_id, uint16_t res_id,
339 uint16_t res_inst_id, uint8_t *data,
340 uint16_t data_len, bool last_block,
341 size_t total_size, size_t offset)
342 {
343 /* validates and removes acl instances for non-existing servers */
344
345 if (res_inst_id == 0) {
346 return 0;
347 }
348
349 /* If there is a server instance with ssid == res_inst_id, return */
350 if (lwm2m_server_short_id_to_inst(res_inst_id) >= 0) {
351 return 0;
352 }
353
354 /* if that res inst id does not match any ssid's, remove it */
355 int idx = -1;
356
357 for (int i = 0; i < ARRAY_SIZE(inst); i++) {
358 if (inst[i].obj && inst[i].obj_inst_id == obj_inst_id) {
359 idx = i;
360 break;
361 }
362 }
363
364 if (idx < 0) {
365 LOG_ERR("Object instance not found - %u", obj_inst_id);
366 return -ENOENT;
367 }
368
369 for (int i = 0; i < ARRAY_SIZE(inst); i++) {
370 if (res_inst[idx][ACCESS_CONTROL_ACL_ID + i].res_inst_id == res_inst_id) {
371 res_inst[idx][ACCESS_CONTROL_ACL_ID + i].res_inst_id =
372 RES_INSTANCE_NOT_CREATED;
373 break;
374 }
375 }
376 return 0;
377 }
378
ac_create(uint16_t obj_inst_id)379 static struct lwm2m_engine_obj_inst *ac_create(uint16_t obj_inst_id)
380 {
381 int index, avail = -1, i = 0, j = 0;
382
383 /* Check that there is no other instance with this ID */
384 for (index = 0; index < ARRAY_SIZE(inst); index++) {
385 if (inst[index].obj && inst[index].obj_inst_id == obj_inst_id) {
386 LOG_ERR("Can not create access control instance - "
387 "already existing: %u",
388 obj_inst_id);
389 return NULL;
390 }
391
392 /* Save first available slot index */
393 if (avail < 0 && !inst[index].obj) {
394 avail = index;
395 }
396 }
397
398 if (avail < 0) {
399 LOG_ERR("Can not create access control instance - no more room: %u", obj_inst_id);
400 return NULL;
401 }
402
403 /* Set default values */
404 (void)memset(res[avail], 0, sizeof(res[avail][0]) * ARRAY_SIZE(res[avail]));
405 init_res_instance(res_inst[avail], ARRAY_SIZE(res_inst[avail]));
406
407 /* initialize instance resource data */
408 INIT_OBJ_RES_DATA(ACCESS_CONTROL_OBJECT_ID, res[avail], i, res_inst[avail], j,
409 &ac_data[avail].obj_id, sizeof(ac_data[avail].obj_id));
410 INIT_OBJ_RES_DATA(ACCESS_CONTROL_OBJECT_INSTANCE_ID, res[avail], i, res_inst[avail], j,
411 &ac_data[avail].obj_inst_id, sizeof(ac_data[avail].obj_inst_id));
412 INIT_OBJ_RES(ACCESS_CONTROL_ACL_ID, res[avail], i, res_inst[avail], j, MAX_SERVER_COUNT + 1,
413 true, false, ac_data[avail].acl, sizeof(ac_data[avail].acl[0]), NULL, NULL,
414 write_validate_cb, NULL, NULL);
415 INIT_OBJ_RES_DATA(ACCESS_CONTROL_ACCESS_CONTROL_OWNER, res[avail], i, res_inst[avail], j,
416 &ac_data[avail].ac_owner, sizeof(ac_data[avail].ac_owner));
417
418 inst[avail].resources = res[avail];
419 inst[avail].resource_count = i;
420
421 LOG_DBG("Create access control instance: %d", obj_inst_id);
422 return &inst[avail];
423 }
424
ac_control_init(void)425 static int ac_control_init(void)
426 {
427 ac_obj.obj_id = LWM2M_OBJECT_ACCESS_CONTROL_ID;
428 ac_obj.version_major = ACCESS_CONTROL_VERSION_MAJOR;
429 ac_obj.version_minor = ACCESS_CONTROL_VERSION_MINOR;
430 ac_obj.is_core = true;
431 ac_obj.fields = fields;
432 ac_obj.field_count = ARRAY_SIZE(fields);
433 ac_obj.max_instance_count = ARRAY_SIZE(inst);
434 ac_obj.create_cb = ac_create;
435 lwm2m_register_obj(&ac_obj);
436
437 if (!IS_ENABLED(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)) {
438 /* add the objects/object instances that were created before the ac control */
439 add_existing_objects();
440 }
441 return 0;
442 }
443
444 LWM2M_CORE_INIT(ac_control_init);
445