1 /*
2  * Copyright (c) 2023 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/kernel.h>
8 #include <zephyr/sys/util.h>
9 #include <zephyr/settings/settings.h>
10 #include <zephyr/mgmt/mcumgr/mgmt/mgmt.h>
11 #include <zephyr/mgmt/mcumgr/smp/smp.h>
12 #include <zephyr/mgmt/mcumgr/mgmt/handlers.h>
13 #include <zephyr/mgmt/mcumgr/mgmt/callbacks.h>
14 #include <zephyr/mgmt/mcumgr/grp/settings_mgmt/settings_mgmt.h>
15 #include <mgmt/mcumgr/util/zcbor_bulk.h>
16 #include <assert.h>
17 #include <limits.h>
18 #include <string.h>
19 #include <stdlib.h>
20 
21 #include <zcbor_common.h>
22 #include <zcbor_decode.h>
23 #include <zcbor_encode.h>
24 
25 #define LOG_LEVEL CONFIG_MCUMGR_LOG_LEVEL
26 #include <zephyr/logging/log.h>
27 LOG_MODULE_REGISTER(settings_mgmt);
28 
29 /**
30  * Command handler: settings read
31  */
settings_mgmt_read(struct smp_streamer * ctxt)32 static int settings_mgmt_read(struct smp_streamer *ctxt)
33 {
34 	int rc;
35 	zcbor_state_t *zse = ctxt->writer->zs;
36 	zcbor_state_t *zsd = ctxt->reader->zs;
37 	bool ok;
38 	size_t decoded;
39 	struct zcbor_string key = { 0 };
40 	uint32_t max_size = CONFIG_MCUMGR_GRP_SETTINGS_VALUE_LEN;
41 	bool limited_size = false;
42 
43 #ifdef CONFIG_MCUMGR_GRP_SETTINGS_BUFFER_TYPE_HEAP
44 	char *key_name = NULL;
45 	uint8_t *data = NULL;
46 #else
47 	char key_name[CONFIG_MCUMGR_GRP_SETTINGS_NAME_LEN] = { 0 };
48 	uint8_t data[CONFIG_MCUMGR_GRP_SETTINGS_VALUE_LEN] = { 0 };
49 #endif
50 
51 	struct zcbor_map_decode_key_val settings_read_decode[] = {
52 		ZCBOR_MAP_DECODE_KEY_DECODER("name", zcbor_tstr_decode, &key),
53 		ZCBOR_MAP_DECODE_KEY_DECODER("max_size", zcbor_uint32_decode, &max_size),
54 	};
55 
56 	ok = zcbor_map_decode_bulk(zsd, settings_read_decode, ARRAY_SIZE(settings_read_decode),
57 				   &decoded) == 0;
58 
59 	if (!ok || key.len == 0) {
60 		return MGMT_ERR_EINVAL;
61 	}
62 
63 	/* Check if the length of the user supplied key is too large for the configuration
64 	 * to allow and return an error if so
65 	 */
66 	if (key.len >= CONFIG_MCUMGR_GRP_SETTINGS_NAME_LEN) {
67 		ok = smp_add_cmd_err(zse, MGMT_GROUP_ID_SETTINGS,
68 				     SETTINGS_MGMT_ERR_KEY_TOO_LONG);
69 		goto end;
70 	}
71 
72 	if (max_size > CONFIG_MCUMGR_GRP_SETTINGS_VALUE_LEN) {
73 		max_size = CONFIG_MCUMGR_GRP_SETTINGS_VALUE_LEN;
74 		limited_size = true;
75 	}
76 
77 #ifdef CONFIG_MCUMGR_GRP_SETTINGS_BUFFER_TYPE_HEAP
78 	key_name = (char *)malloc(key.len + 1);
79 	data = (uint8_t *)malloc(max_size);
80 
81 	if (data == NULL || key_name == NULL) {
82 		if (key_name != NULL) {
83 			free(key_name);
84 		}
85 
86 		return MGMT_ERR_ENOMEM;
87 	}
88 #endif
89 
90 	memcpy(key_name, key.value, key.len);
91 	key_name[key.len] = 0;
92 
93 	if (IS_ENABLED(CONFIG_MCUMGR_GRP_SETTINGS_ACCESS_HOOK)) {
94 		/* Send request to application to check if access should be allowed or not */
95 		struct settings_mgmt_access settings_access_data = {
96 			.access = SETTINGS_ACCESS_READ,
97 			.name = key_name,
98 		};
99 
100 		enum mgmt_cb_return status;
101 		int32_t ret_rc;
102 		uint16_t ret_group;
103 
104 		status = mgmt_callback_notify(MGMT_EVT_OP_SETTINGS_MGMT_ACCESS,
105 					      &settings_access_data, sizeof(settings_access_data),
106 					      &ret_rc, &ret_group);
107 
108 		if (status != MGMT_CB_OK) {
109 			if (status == MGMT_CB_ERROR_RC) {
110 				return ret_rc;
111 			}
112 
113 			ok = smp_add_cmd_err(zse, ret_group, (uint16_t)ret_rc);
114 			goto end;
115 		}
116 	}
117 
118 	/* Read the settings key */
119 	rc = settings_runtime_get(key_name, data, max_size);
120 
121 	if (rc < 0) {
122 		if (rc == -EINVAL) {
123 			rc = SETTINGS_MGMT_ERR_ROOT_KEY_NOT_FOUND;
124 		} else if (rc == -ENOENT) {
125 			rc = SETTINGS_MGMT_ERR_KEY_NOT_FOUND;
126 		} else if (rc == -ENOTSUP) {
127 			rc = SETTINGS_MGMT_ERR_READ_NOT_SUPPORTED;
128 		} else {
129 			rc = SETTINGS_MGMT_ERR_UNKNOWN;
130 		}
131 
132 		ok = smp_add_cmd_err(zse, MGMT_GROUP_ID_SETTINGS, (uint16_t)rc);
133 		goto end;
134 	}
135 
136 	ok = zcbor_tstr_put_lit(zse, "val")		&&
137 	     zcbor_bstr_encode_ptr(zse, data, rc);
138 
139 	if (ok && limited_size) {
140 		ok = zcbor_tstr_put_lit(zse, "max_size")				&&
141 		     zcbor_uint32_put(zse, CONFIG_MCUMGR_GRP_SETTINGS_VALUE_LEN);
142 	}
143 
144 end:
145 #ifdef CONFIG_MCUMGR_GRP_SETTINGS_BUFFER_TYPE_HEAP
146 	free(key_name);
147 	free(data);
148 #endif
149 
150 	return MGMT_RETURN_CHECK(ok);
151 }
152 
153 /**
154  * Command handler: settings write
155  */
settings_mgmt_write(struct smp_streamer * ctxt)156 static int settings_mgmt_write(struct smp_streamer *ctxt)
157 {
158 	int rc;
159 	zcbor_state_t *zse = ctxt->writer->zs;
160 	zcbor_state_t *zsd = ctxt->reader->zs;
161 	bool ok;
162 	struct zcbor_string data = { 0 };
163 	size_t decoded;
164 	struct zcbor_string key = { 0 };
165 
166 #ifdef CONFIG_MCUMGR_GRP_SETTINGS_BUFFER_TYPE_HEAP
167 	char *key_name = NULL;
168 #else
169 	char key_name[CONFIG_MCUMGR_GRP_SETTINGS_NAME_LEN];
170 #endif
171 
172 	struct zcbor_map_decode_key_val settings_write_decode[] = {
173 		ZCBOR_MAP_DECODE_KEY_DECODER("name", zcbor_tstr_decode, &key),
174 		ZCBOR_MAP_DECODE_KEY_DECODER("val", zcbor_bstr_decode, &data),
175 	};
176 
177 	ok = zcbor_map_decode_bulk(zsd, settings_write_decode, ARRAY_SIZE(settings_write_decode),
178 				   &decoded) == 0;
179 
180 	if (!ok || key.len == 0) {
181 		return MGMT_ERR_EINVAL;
182 	}
183 
184 	/* Check if the length of the user supplied key is too large for the configuration
185 	 * to allow and return an error if so
186 	 */
187 	if (key.len >= CONFIG_MCUMGR_GRP_SETTINGS_NAME_LEN) {
188 		ok = smp_add_cmd_err(zse, MGMT_GROUP_ID_SETTINGS,
189 				     SETTINGS_MGMT_ERR_KEY_TOO_LONG);
190 		goto end;
191 	}
192 
193 #ifdef CONFIG_MCUMGR_GRP_SETTINGS_BUFFER_TYPE_HEAP
194 	key_name = (char *)malloc(key.len + 1);
195 
196 	if (key_name == NULL) {
197 		return MGMT_ERR_ENOMEM;
198 	}
199 #endif
200 
201 	memcpy(key_name, key.value, key.len);
202 	key_name[key.len] = 0;
203 
204 	if (IS_ENABLED(CONFIG_MCUMGR_GRP_SETTINGS_ACCESS_HOOK)) {
205 		/* Send request to application to check if access should be allowed or not */
206 		struct settings_mgmt_access settings_access_data = {
207 			.access = SETTINGS_ACCESS_WRITE,
208 			.name = key_name,
209 			.val = data.value,
210 			.val_length = &data.len,
211 		};
212 
213 		enum mgmt_cb_return status;
214 		int32_t ret_rc;
215 		uint16_t ret_group;
216 
217 		status = mgmt_callback_notify(MGMT_EVT_OP_SETTINGS_MGMT_ACCESS,
218 					      &settings_access_data, sizeof(settings_access_data),
219 					      &ret_rc, &ret_group);
220 
221 		if (status != MGMT_CB_OK) {
222 			if (status == MGMT_CB_ERROR_RC) {
223 				return ret_rc;
224 			}
225 
226 			ok = smp_add_cmd_err(zse, ret_group, (uint16_t)ret_rc);
227 			goto end;
228 		}
229 	}
230 
231 	/* Update the settings data */
232 	rc = settings_runtime_set(key_name, data.value, data.len);
233 
234 	if (rc < 0) {
235 		if (rc == -EINVAL) {
236 			rc = SETTINGS_MGMT_ERR_ROOT_KEY_NOT_FOUND;
237 		} else if (rc == -ENOENT) {
238 			rc = SETTINGS_MGMT_ERR_KEY_NOT_FOUND;
239 		} else if (rc == -ENOTSUP) {
240 			rc = SETTINGS_MGMT_ERR_WRITE_NOT_SUPPORTED;
241 		} else {
242 			rc = SETTINGS_MGMT_ERR_UNKNOWN;
243 		}
244 
245 		ok = smp_add_cmd_err(zse, MGMT_GROUP_ID_SETTINGS, (uint16_t)rc);
246 	}
247 
248 end:
249 #ifdef CONFIG_MCUMGR_GRP_SETTINGS_BUFFER_TYPE_HEAP
250 	free(key_name);
251 #endif
252 
253 	return MGMT_RETURN_CHECK(ok);
254 }
255 
256 /**
257  * Command handler: settings delete
258  */
settings_mgmt_delete(struct smp_streamer * ctxt)259 static int settings_mgmt_delete(struct smp_streamer *ctxt)
260 {
261 	zcbor_state_t *zse = ctxt->writer->zs;
262 	zcbor_state_t *zsd = ctxt->reader->zs;
263 	int rc;
264 	bool ok;
265 	size_t decoded;
266 	struct zcbor_string key = { 0 };
267 
268 #ifdef CONFIG_MCUMGR_GRP_SETTINGS_BUFFER_TYPE_HEAP
269 	char *key_name = NULL;
270 #else
271 	char key_name[CONFIG_MCUMGR_GRP_SETTINGS_NAME_LEN];
272 #endif
273 
274 	struct zcbor_map_decode_key_val settings_delete_decode[] = {
275 		ZCBOR_MAP_DECODE_KEY_DECODER("name", zcbor_tstr_decode, &key),
276 	};
277 
278 	ok = zcbor_map_decode_bulk(zsd, settings_delete_decode, ARRAY_SIZE(settings_delete_decode),
279 				   &decoded) == 0;
280 
281 	if (!ok || key.len == 0) {
282 		return MGMT_ERR_EINVAL;
283 	}
284 
285 	/* Check if the length of the user supplied key is too large for the configuration
286 	 * to allow and return an error if so
287 	 */
288 	if (key.len >= CONFIG_MCUMGR_GRP_SETTINGS_NAME_LEN) {
289 		ok = smp_add_cmd_err(zse, MGMT_GROUP_ID_SETTINGS,
290 				     SETTINGS_MGMT_ERR_KEY_TOO_LONG);
291 		goto end;
292 	}
293 
294 #ifdef CONFIG_MCUMGR_GRP_SETTINGS_BUFFER_TYPE_HEAP
295 	key_name = (char *)malloc(key.len + 1);
296 
297 	if (key_name == NULL) {
298 		return MGMT_ERR_ENOMEM;
299 	}
300 #endif
301 
302 	memcpy(key_name, key.value, key.len);
303 	key_name[key.len] = 0;
304 
305 	if (IS_ENABLED(CONFIG_MCUMGR_GRP_SETTINGS_ACCESS_HOOK)) {
306 		/* Send request to application to check if access should be allowed or not */
307 		struct settings_mgmt_access settings_access_data = {
308 			.access = SETTINGS_ACCESS_DELETE,
309 			.name = key_name,
310 		};
311 
312 		enum mgmt_cb_return status;
313 		int32_t ret_rc;
314 		uint16_t ret_group;
315 
316 		status = mgmt_callback_notify(MGMT_EVT_OP_SETTINGS_MGMT_ACCESS,
317 					      &settings_access_data, sizeof(settings_access_data),
318 					      &ret_rc, &ret_group);
319 
320 		if (status != MGMT_CB_OK) {
321 			if (status == MGMT_CB_ERROR_RC) {
322 				return ret_rc;
323 			}
324 
325 			ok = smp_add_cmd_err(zse, ret_group, (uint16_t)ret_rc);
326 			goto end;
327 		}
328 	}
329 
330 	/* Delete requested key from settings */
331 	rc = settings_delete(key_name);
332 
333 #ifdef CONFIG_MCUMGR_GRP_SETTINGS_BUFFER_TYPE_HEAP
334 	free(key_name);
335 #endif
336 
337 	if (rc < 0) {
338 		if (rc == -EINVAL) {
339 			rc = SETTINGS_MGMT_ERR_ROOT_KEY_NOT_FOUND;
340 		} else if (rc == -ENOENT) {
341 			rc = SETTINGS_MGMT_ERR_KEY_NOT_FOUND;
342 		} else if (rc == -ENOTSUP) {
343 			rc = SETTINGS_MGMT_ERR_DELETE_NOT_SUPPORTED;
344 		} else {
345 			rc = SETTINGS_MGMT_ERR_UNKNOWN;
346 		}
347 
348 		ok = smp_add_cmd_err(zse, MGMT_GROUP_ID_SETTINGS, (uint16_t)rc);
349 	}
350 
351 end:
352 	return MGMT_RETURN_CHECK(ok);
353 }
354 
355 /**
356  * Command handler: settings commit
357  */
settings_mgmt_commit(struct smp_streamer * ctxt)358 static int settings_mgmt_commit(struct smp_streamer *ctxt)
359 {
360 	bool ok = true;
361 
362 	if (IS_ENABLED(CONFIG_MCUMGR_GRP_SETTINGS_ACCESS_HOOK)) {
363 		/* Send request to application to check if access should be allowed or not */
364 		struct settings_mgmt_access settings_access_data = {
365 			.access = SETTINGS_ACCESS_COMMIT,
366 		};
367 
368 		zcbor_state_t *zse = ctxt->writer->zs;
369 		enum mgmt_cb_return status;
370 		int32_t ret_rc;
371 		uint16_t ret_group;
372 
373 		status = mgmt_callback_notify(MGMT_EVT_OP_SETTINGS_MGMT_ACCESS,
374 					      &settings_access_data, sizeof(settings_access_data),
375 					      &ret_rc, &ret_group);
376 
377 		if (status != MGMT_CB_OK) {
378 			if (status == MGMT_CB_ERROR_RC) {
379 				return ret_rc;
380 			}
381 
382 			ok = smp_add_cmd_err(zse, ret_group, (uint16_t)ret_rc);
383 			goto end;
384 		}
385 	}
386 
387 	settings_commit();
388 
389 end:
390 	return MGMT_RETURN_CHECK(ok);
391 }
392 
393 /**
394  * Command handler: settings load
395  */
settings_mgmt_load(struct smp_streamer * ctxt)396 static int settings_mgmt_load(struct smp_streamer *ctxt)
397 {
398 	bool ok = true;
399 
400 	if (IS_ENABLED(CONFIG_MCUMGR_GRP_SETTINGS_ACCESS_HOOK)) {
401 		/* Send request to application to check if access should be allowed or not */
402 		struct settings_mgmt_access settings_access_data = {
403 			.access = SETTINGS_ACCESS_LOAD,
404 		};
405 
406 		zcbor_state_t *zse = ctxt->writer->zs;
407 		enum mgmt_cb_return status;
408 		int32_t ret_rc;
409 		uint16_t ret_group;
410 
411 		status = mgmt_callback_notify(MGMT_EVT_OP_SETTINGS_MGMT_ACCESS,
412 					      &settings_access_data, sizeof(settings_access_data),
413 					      &ret_rc, &ret_group);
414 
415 		if (status != MGMT_CB_OK) {
416 			if (status == MGMT_CB_ERROR_RC) {
417 				return ret_rc;
418 			}
419 
420 			ok = smp_add_cmd_err(zse, ret_group, (uint16_t)ret_rc);
421 			goto end;
422 		}
423 	}
424 
425 	settings_load();
426 
427 end:
428 	return MGMT_RETURN_CHECK(ok);
429 }
430 
431 /**
432  * Command handler: settings save
433  */
settings_mgmt_save(struct smp_streamer * ctxt)434 static int settings_mgmt_save(struct smp_streamer *ctxt)
435 {
436 	bool ok = true;
437 
438 	if (IS_ENABLED(CONFIG_MCUMGR_GRP_SETTINGS_ACCESS_HOOK)) {
439 		/* Send request to application to check if access should be allowed or not */
440 		struct settings_mgmt_access settings_access_data = {
441 			.access = SETTINGS_ACCESS_SAVE,
442 		};
443 
444 		zcbor_state_t *zse = ctxt->writer->zs;
445 		enum mgmt_cb_return status;
446 		int32_t ret_rc;
447 		uint16_t ret_group;
448 
449 		status = mgmt_callback_notify(MGMT_EVT_OP_SETTINGS_MGMT_ACCESS,
450 					      &settings_access_data, sizeof(settings_access_data),
451 					      &ret_rc, &ret_group);
452 
453 		if (status != MGMT_CB_OK) {
454 			if (status == MGMT_CB_ERROR_RC) {
455 				return ret_rc;
456 			}
457 
458 			ok = smp_add_cmd_err(zse, ret_group, (uint16_t)ret_rc);
459 			goto end;
460 		}
461 	}
462 
463 	settings_save();
464 
465 end:
466 	return MGMT_RETURN_CHECK(ok);
467 }
468 
469 #ifdef CONFIG_MCUMGR_SMP_SUPPORT_ORIGINAL_PROTOCOL
settings_mgmt_translate_error_code(uint16_t ret)470 static int settings_mgmt_translate_error_code(uint16_t ret)
471 {
472 	int rc;
473 
474 	switch (ret) {
475 	case SETTINGS_MGMT_ERR_KEY_TOO_LONG:
476 		rc = MGMT_ERR_EINVAL;
477 		break;
478 
479 	case SETTINGS_MGMT_ERR_KEY_NOT_FOUND:
480 	case SETTINGS_MGMT_ERR_READ_NOT_SUPPORTED:
481 		rc = MGMT_ERR_ENOENT;
482 		break;
483 
484 	case SETTINGS_MGMT_ERR_UNKNOWN:
485 	default:
486 		rc = MGMT_ERR_EUNKNOWN;
487 	}
488 
489 	return rc;
490 }
491 #endif
492 
493 static const struct mgmt_handler settings_mgmt_handlers[] = {
494 	[SETTINGS_MGMT_ID_READ_WRITE] = {
495 		.mh_read = settings_mgmt_read,
496 		.mh_write = settings_mgmt_write,
497 	},
498 	[SETTINGS_MGMT_ID_DELETE] = {
499 		.mh_read = NULL,
500 		.mh_write = settings_mgmt_delete,
501 	},
502 	[SETTINGS_MGMT_ID_COMMIT] = {
503 		.mh_read = NULL,
504 		.mh_write = settings_mgmt_commit,
505 	},
506 	[SETTINGS_MGMT_ID_LOAD_SAVE] = {
507 		.mh_read = settings_mgmt_load,
508 		.mh_write = settings_mgmt_save,
509 	},
510 };
511 
512 static struct mgmt_group settings_mgmt_group = {
513 	.mg_handlers = settings_mgmt_handlers,
514 	.mg_handlers_count = ARRAY_SIZE(settings_mgmt_handlers),
515 	.mg_group_id = MGMT_GROUP_ID_SETTINGS,
516 #ifdef CONFIG_MCUMGR_SMP_SUPPORT_ORIGINAL_PROTOCOL
517 	.mg_translate_error = settings_mgmt_translate_error_code,
518 #endif
519 #ifdef CONFIG_MCUMGR_GRP_ENUM_DETAILS_NAME
520 	.mg_group_name = "settings mgmt",
521 #endif
522 };
523 
settings_mgmt_register_group(void)524 static void settings_mgmt_register_group(void)
525 {
526 	mgmt_register_group(&settings_mgmt_group);
527 }
528 
529 MCUMGR_HANDLER_DEFINE(settings_mgmt, settings_mgmt_register_group);
530