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)338 static int write_validate_cb(uint16_t obj_inst_id, uint16_t res_id, uint16_t res_inst_id,
339 			     uint8_t *data, uint16_t data_len, bool last_block, size_t total_size)
340 {
341 	/* validates and removes acl instances for non-existing servers */
342 
343 	if (res_inst_id == 0) {
344 		return 0;
345 	}
346 
347 	/* If there is a server instance with ssid == res_inst_id, return */
348 	if (lwm2m_server_short_id_to_inst(res_inst_id) >= 0) {
349 		return 0;
350 	}
351 
352 	/* if that res inst id does not match any ssid's, remove it */
353 	int idx = -1;
354 
355 	for (int i = 0; i < ARRAY_SIZE(inst); i++) {
356 		if (inst[i].obj && inst[i].obj_inst_id == obj_inst_id) {
357 			idx = i;
358 			break;
359 		}
360 	}
361 
362 	if (idx < 0) {
363 		LOG_ERR("Object instance not found - %u", obj_inst_id);
364 		return -ENOENT;
365 	}
366 
367 	for (int i = 0; i < ARRAY_SIZE(inst); i++) {
368 		if (res_inst[idx][ACCESS_CONTROL_ACL_ID + i].res_inst_id == res_inst_id) {
369 			res_inst[idx][ACCESS_CONTROL_ACL_ID + i].res_inst_id =
370 				RES_INSTANCE_NOT_CREATED;
371 			break;
372 		}
373 	}
374 	return 0;
375 }
376 
ac_create(uint16_t obj_inst_id)377 static struct lwm2m_engine_obj_inst *ac_create(uint16_t obj_inst_id)
378 {
379 	int index, avail = -1, i = 0, j = 0;
380 
381 	/* Check that there is no other instance with this ID */
382 	for (index = 0; index < ARRAY_SIZE(inst); index++) {
383 		if (inst[index].obj && inst[index].obj_inst_id == obj_inst_id) {
384 			LOG_ERR("Can not create access control instance - "
385 				"already existing: %u",
386 				obj_inst_id);
387 			return NULL;
388 		}
389 
390 		/* Save first available slot index */
391 		if (avail < 0 && !inst[index].obj) {
392 			avail = index;
393 		}
394 	}
395 
396 	if (avail < 0) {
397 		LOG_ERR("Can not create access control instance - no more room: %u", obj_inst_id);
398 		return NULL;
399 	}
400 
401 	/* Set default values */
402 	(void)memset(res[avail], 0, sizeof(res[avail][0]) * ARRAY_SIZE(res[avail]));
403 	init_res_instance(res_inst[avail], ARRAY_SIZE(res_inst[avail]));
404 
405 	/* initialize instance resource data */
406 	INIT_OBJ_RES_DATA(ACCESS_CONTROL_OBJECT_ID, res[avail], i, res_inst[avail], j,
407 			  &ac_data[avail].obj_id, sizeof(ac_data[avail].obj_id));
408 	INIT_OBJ_RES_DATA(ACCESS_CONTROL_OBJECT_INSTANCE_ID, res[avail], i, res_inst[avail], j,
409 			  &ac_data[avail].obj_inst_id, sizeof(ac_data[avail].obj_inst_id));
410 	INIT_OBJ_RES(ACCESS_CONTROL_ACL_ID, res[avail], i, res_inst[avail], j, MAX_SERVER_COUNT + 1,
411 		     true, false, ac_data[avail].acl, sizeof(ac_data[avail].acl[0]), NULL, NULL,
412 		     write_validate_cb, NULL, NULL);
413 	INIT_OBJ_RES_DATA(ACCESS_CONTROL_ACCESS_CONTROL_OWNER, res[avail], i, res_inst[avail], j,
414 			  &ac_data[avail].ac_owner, sizeof(ac_data[avail].ac_owner));
415 
416 	inst[avail].resources = res[avail];
417 	inst[avail].resource_count = i;
418 
419 	LOG_DBG("Create access control instance: %d", obj_inst_id);
420 	return &inst[avail];
421 }
422 
ac_control_init(void)423 static int ac_control_init(void)
424 {
425 	ac_obj.obj_id = LWM2M_OBJECT_ACCESS_CONTROL_ID;
426 	ac_obj.version_major = ACCESS_CONTROL_VERSION_MAJOR;
427 	ac_obj.version_minor = ACCESS_CONTROL_VERSION_MINOR;
428 	ac_obj.is_core = true;
429 	ac_obj.fields = fields;
430 	ac_obj.field_count = ARRAY_SIZE(fields);
431 	ac_obj.max_instance_count = ARRAY_SIZE(inst);
432 	ac_obj.create_cb = ac_create;
433 	lwm2m_register_obj(&ac_obj);
434 
435 	if (!IS_ENABLED(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)) {
436 		/* add the objects/object instances that were created before the ac control */
437 		add_existing_objects();
438 	}
439 	return 0;
440 }
441 
442 LWM2M_CORE_INIT(ac_control_init);
443