Contiki-NG
lwm2m-security.c
Go to the documentation of this file.
1/*
2 * Copyright (c) 2015-2018, Yanzi Networks AB.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the copyright holder nor the names of its
14 * contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS
18 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
20 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
21 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
28 * OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31/**
32 * \addtogroup lwm2m
33 * @{
34 *
35 */
36
37/**
38 * \file
39 * Implementation of the Contiki OMA LWM2M security
40 * \author
41 * Joakim Eriksson <joakime@sics.se>
42 * Niclas Finne <nfi@sics.se>
43 */
44
45#include <stdint.h>
46#include <string.h>
47#include <inttypes.h>
48#include "lwm2m-object.h"
49#include "lwm2m-engine.h"
50#include "lwm2m-server.h"
51#include "lwm2m-security.h"
52#include "coap-keystore.h"
53#include "lib/list.h"
54
55/* Log configuration */
56#include "coap-log.h"
57#define LOG_MODULE "lwm2m-sec"
58#define LOG_LEVEL LOG_LEVEL_LWM2M
59
60#define MAX_COUNT LWM2M_SERVER_MAX_COUNT
61
62static lwm2m_status_t lwm2m_callback(lwm2m_object_instance_t *object,
63 lwm2m_context_t *ctx);
64
65static lwm2m_object_instance_t *get_by_id(uint16_t instance_id,
66 lwm2m_status_t *status);
67
68static const lwm2m_resource_id_t resources[] = {
69 LWM2M_SECURITY_SERVER_URI_ID, LWM2M_SECURITY_BOOTSTRAP_SERVER_ID,
70 LWM2M_SECURITY_MODE_ID, LWM2M_SECURITY_CLIENT_PKI_ID,
71 LWM2M_SECURITY_SERVER_PKI_ID, LWM2M_SECURITY_KEY_ID,
72 LWM2M_SECURITY_SHORT_SERVER_ID
73};
74
75LIST(instances_list);
76static lwm2m_security_server_t instances[MAX_COUNT];
77/*---------------------------------------------------------------------------*/
78static lwm2m_object_instance_t *
79create_instance(uint16_t instance_id, lwm2m_status_t *status)
80{
81 lwm2m_object_instance_t *instance;
82 int i;
83
84 instance = get_by_id(instance_id, NULL);
85 if(instance != NULL) {
86 /* An instance with this id is already registered */
87 if(status) {
88 *status = LWM2M_STATUS_OPERATION_NOT_ALLOWED;
89 }
90 return NULL;
91 }
92
93 for(i = 0; i < MAX_COUNT; i++) {
94 if(instances[i].instance.instance_id == LWM2M_OBJECT_INSTANCE_NONE) {
95 memset(&instances[i], 0, sizeof(instances[i]));
96 instances[i].instance.callback = lwm2m_callback;
97 instances[i].instance.object_id = LWM2M_OBJECT_SECURITY_ID;
98 instances[i].instance.instance_id = instance_id;
99 instances[i].instance.resource_ids = resources;
100 instances[i].instance.resource_count =
101 sizeof(resources) / sizeof(lwm2m_resource_id_t);
102 list_add(instances_list, &instances[i].instance);
103
104 LOG_DBG("Create new security instance %u\n", instance_id);
105 return &instances[i].instance;
106 }
107 }
108
109 if(status) {
110 *status = LWM2M_STATUS_SERVICE_UNAVAILABLE;
111 }
112
113 return NULL;
114}
115/*---------------------------------------------------------------------------*/
116static int
117delete_instance(uint16_t instance_id, lwm2m_status_t *status)
118{
119 lwm2m_object_instance_t *instance;
120
121 if(instance_id == LWM2M_OBJECT_INSTANCE_NONE) {
122 /* Remove all instances */
123 while((instance = list_pop(instances_list)) != NULL) {
124 instance->instance_id = LWM2M_OBJECT_INSTANCE_NONE;
125 }
126 return 1;
127 }
128
129 instance = get_by_id(instance_id, NULL);
130 if(instance != NULL) {
131 instance->instance_id = LWM2M_OBJECT_INSTANCE_NONE;
132 list_remove(instances_list, instance);
133 return 1;
134 }
135
136 return 0;
137}
138/*---------------------------------------------------------------------------*/
139static lwm2m_object_instance_t *
140get_first(lwm2m_status_t *status)
141{
142 return list_head(instances_list);
143}
144/*---------------------------------------------------------------------------*/
145static lwm2m_object_instance_t *
146get_next(lwm2m_object_instance_t *instance, lwm2m_status_t *status)
147{
148 return instance == NULL ? NULL : instance->next;
149}
150/*---------------------------------------------------------------------------*/
151static lwm2m_object_instance_t *
152get_by_id(uint16_t instance_id, lwm2m_status_t *status)
153{
154 lwm2m_object_instance_t *instance;
155 for(instance = list_head(instances_list);
156 instance != NULL;
157 instance = instance->next) {
158 if(instance->instance_id == instance_id) {
159 return instance;
160 }
161 }
162 return NULL;
163}
164/*---------------------------------------------------------------------------*/
165static lwm2m_status_t
166lwm2m_callback(lwm2m_object_instance_t *object,
167 lwm2m_context_t *ctx)
168{
169 /* NOTE: the create operation will only create an instance and should
170 avoid reading out data */
171 int32_t value;
172 int iv;
173 lwm2m_security_server_t *security;
174 security = (lwm2m_security_server_t *) object;
175
176 if(ctx->operation == LWM2M_OP_WRITE) {
177 /* Handle the writes */
178 switch(ctx->resource_id) {
179 case LWM2M_SECURITY_SERVER_URI_ID:
180 LOG_DBG("Writing security URI value: len: %"PRId16"\n", ctx->inbuf->size);
181 value = lwm2m_object_read_string(ctx, ctx->inbuf->buffer, ctx->inbuf->size, security->server_uri, LWM2M_SECURITY_URI_SIZE);
182 /* This is string... */
183 security->server_uri_len = ctx->last_value_len;
184 break;
185 case LWM2M_SECURITY_BOOTSTRAP_SERVER_ID:
186 value = lwm2m_object_read_boolean(ctx, ctx->inbuf->buffer, ctx->inbuf->size, &iv);
187 if(value > 0) {
188 LOG_DBG("Set Bootstrap: %d\n", iv);
189 security->bootstrap = (uint8_t) iv;
190 } else {
191 LOG_WARN("Failed to set bootstrap\n");
192 }
193 break;
194 case LWM2M_SECURITY_MODE_ID:
195 {
196 int32_t v2;
197 value = lwm2m_object_read_int(ctx, ctx->inbuf->buffer, ctx->inbuf->size, &v2);
198 LOG_DBG("Writing security MODE value: %"PRId32" len: %d\n", v2,
199 (int)ctx->inbuf->size);
200 security->security_mode = v2;
201 }
202 break;
203 case LWM2M_SECURITY_CLIENT_PKI_ID:
204 value = lwm2m_object_read_string(ctx, ctx->inbuf->buffer, ctx->inbuf->size, security->public_key, LWM2M_SECURITY_KEY_SIZE);
205 security->public_key_len = ctx->last_value_len;
206
207 LOG_DBG("Writing client PKI: len: %"PRIu16" '", ctx->last_value_len);
208 LOG_DBG_COAP_STRING((const char *)security->public_key,
209 ctx->last_value_len);
210 LOG_DBG_("'\n");
211 break;
212 case LWM2M_SECURITY_KEY_ID:
213 value = lwm2m_object_read_string(ctx, ctx->inbuf->buffer, ctx->inbuf->size, security->secret_key, LWM2M_SECURITY_KEY_SIZE);
214 security->secret_key_len = ctx->last_value_len;
215
216 LOG_DBG("Writing secret key: len: %"PRIu16" '", ctx->last_value_len);
217 LOG_DBG_COAP_STRING((const char *)security->secret_key,
218 ctx->last_value_len);
219 LOG_DBG_("'\n");
220
221 break;
222 }
223 } else if(ctx->operation == LWM2M_OP_READ) {
224 switch(ctx->resource_id) {
225 case LWM2M_SECURITY_SERVER_URI_ID:
226 lwm2m_object_write_string(ctx, (const char *) security->server_uri,
227 security->server_uri_len);
228 break;
229 default:
230 return LWM2M_STATUS_ERROR;
231 }
232 }
233 return LWM2M_STATUS_OK;
234}
235
236/*---------------------------------------------------------------------------*/
237lwm2m_security_server_t *
238lwm2m_security_get_first(void)
239{
240 return list_head(instances_list);
241}
242/*---------------------------------------------------------------------------*/
243lwm2m_security_server_t *
244lwm2m_security_get_next(lwm2m_security_server_t *last)
245{
246 return last == NULL ? NULL : (lwm2m_security_server_t *)last->instance.next;
247}
248/*---------------------------------------------------------------------------*/
249lwm2m_security_server_t *
250lwm2m_security_add_server(uint16_t instance_id,
251 uint16_t server_id,
252 const uint8_t *server_uri,
253 uint8_t server_uri_len)
254{
255 lwm2m_security_server_t *server;
256 int i;
257
258 if(server_uri_len > LWM2M_SECURITY_URI_SIZE) {
259 LOG_WARN("too long server URI: %u\n", server_uri_len);
260 return NULL;
261 }
262
263 for(server = lwm2m_security_get_first();
264 server != NULL;
265 server = lwm2m_security_get_next(server)) {
266 if(server->server_id == server_id) {
267 if(server->instance.instance_id != instance_id) {
268 LOG_WARN("wrong instance id\n");
269 return NULL;
270 }
271 /* Correct server id and instance id */
272 break;
273 } else if(server->instance.instance_id == instance_id) {
274 LOG_WARN("wrong server id\n");
275 return NULL;
276 }
277 }
278
279 if(server == NULL) {
280 for(i = 0; i < MAX_COUNT; i++) {
281 if(instances[i].instance.instance_id == LWM2M_OBJECT_INSTANCE_NONE) {
282 memset(&instances[i], 0, sizeof(instances[i]));
283 instances[i].instance.callback = lwm2m_callback;
284 instances[i].instance.object_id = LWM2M_OBJECT_SECURITY_ID;
285 instances[i].instance.instance_id = instance_id;
286 instances[i].instance.resource_ids = resources;
287 instances[i].instance.resource_count =
288 sizeof(resources) / sizeof(lwm2m_resource_id_t);
289 list_add(instances_list, &instances[i].instance);
290 server = &instances[i];
291 }
292 }
293 if(server == NULL) {
294 LOG_WARN("no space for more servers\n");
295 return NULL;
296 }
297 }
298
299 memcpy(server->server_uri, server_uri, server_uri_len);
300 server->server_uri_len = server_uri_len;
301
302 return server;
303}
304/*---------------------------------------------------------------------------*/
305int
306lwm2m_security_set_server_psk(lwm2m_security_server_t *server,
307 const uint8_t *identity,
308 uint8_t identity_len,
309 const uint8_t *key,
310 uint8_t key_len)
311{
312 if(server == NULL || identity == NULL || key == NULL) {
313 return 0;
314 }
315 if(identity_len > LWM2M_SECURITY_KEY_SIZE) {
316 LOG_WARN("too large identity: %u\n", identity_len);
317 return 0;
318 }
319 if(key_len > LWM2M_SECURITY_KEY_SIZE) {
320 LOG_WARN("too large identity: %u\n", key_len);
321 return 0;
322 }
323 memcpy(server->public_key, identity, identity_len);
324 server->public_key_len = identity_len;
325 memcpy(server->secret_key, key, key_len);
326 server->secret_key_len = key_len;
327
328 server->security_mode = LWM2M_SECURITY_MODE_PSK;
329
330 return 1;
331}
332/*---------------------------------------------------------------------------*/
333static const lwm2m_object_impl_t impl = {
334 .object_id = LWM2M_OBJECT_SECURITY_ID,
335 .get_first = get_first,
336 .get_next = get_next,
337 .get_by_id = get_by_id,
338 .create_instance = create_instance,
339 .delete_instance = delete_instance,
340};
341static lwm2m_object_t reg_object = {
342 .impl = &impl,
343};
344/*---------------------------------------------------------------------------*/
345#ifdef WITH_DTLS
346#if COAP_DTLS_KEYSTORE_CONF_WITH_LWM2M
347static int
348get_psk_info(const coap_endpoint_t *address_info,
350{
351 /* Find matching security object based on address */
352 lwm2m_security_server_t *e;
353 coap_endpoint_t ep;
354
355 if(info == NULL || address_info == NULL) {
356 return 0;
357 }
358
359 for(e = lwm2m_security_get_first();
360 e != NULL;
361 e = lwm2m_security_get_next(e)) {
362 if(e->server_uri_len == 0) {
363 continue;
364 }
365 if(e->security_mode != LWM2M_SECURITY_MODE_PSK) {
366 /* Only PSK supported for now */
367 continue;
368 }
369 if(!coap_endpoint_parse((char *)e->server_uri, e->server_uri_len, &ep)) {
370 /* Failed to parse URI to endpoint */
371 LOG_DBG("failed to parse server URI ");
372 LOG_DBG_COAP_STRING((char *)e->server_uri, e->server_uri_len);
373 LOG_DBG_("\n");
374 continue;
375 }
376 if(!coap_endpoint_cmp(address_info, &ep)) {
377 /* Wrong server */
378 LOG_DBG("wrong server ");
379 LOG_DBG_COAP_EP(address_info);
380 LOG_DBG_(" != ");
381 LOG_DBG_COAP_EP(&ep);
382 LOG_DBG_("\n");
383 continue;
384 }
385 if(info->identity_len > 0 && info->identity != NULL) {
386 /* Searching for a specific identity */
387 if(info->identity_len != e->public_key_len ||
388 memcmp(info->identity, e->public_key, info->identity_len)) {
389 /* Identity not matching */
390 LOG_DBG("identity not matching\n");
391 continue;
392 }
393 }
394 /* Found security information for this server */
395 LOG_DBG("found security match!\n");
396 break;
397 }
398
399 if(e == NULL) {
400 /* No matching security object found */
401 return 0;
402 }
403
404 if(info->identity == NULL || info->identity_len == 0) {
405 /* Identity requested */
406 info->identity = e->public_key;
407 info->identity_len = e->public_key_len;
408 return 1;
409 }
410
411 if(e->secret_key_len == 0) {
412 /* No secret key / password */
413 return 0;
414 }
415
416 info->key = e->secret_key;
417 info->key_len = e->secret_key_len;
418 return 1;
419}
420static const coap_keystore_t key_store = {
421 .coap_get_psk_info = get_psk_info
422};
423#endif /* COAP_DTLS_KEYSTORE_CONF_WITH_LWM2M */
424#endif /* WITH_DTLS */
425/*---------------------------------------------------------------------------*/
426void
427lwm2m_security_init(void)
428{
429 int i;
430
431 LOG_INFO("init\n");
432
433 list_init(instances_list);
434
435 for(i = 0; i < MAX_COUNT; i++) {
436 instances[i].instance.instance_id = LWM2M_OBJECT_INSTANCE_NONE;
437 }
438 if(lwm2m_engine_add_generic_object(&reg_object)) {
439
440#ifdef WITH_DTLS
441#if COAP_DTLS_KEYSTORE_CONF_WITH_LWM2M
442 /* Security object handler added - register keystore */
443 coap_set_keystore(&key_store);
444 LOG_DBG("registered keystore\n");
445#endif /* COAP_DTLS_KEYSTORE_CONF_WITH_LWM2M */
446#endif /* WITH_DTLS */
447
448 } else {
449 LOG_WARN("failed to register\n");
450 }
451}
452/*---------------------------------------------------------------------------*/
453/** @} */
API for CoAP keystore.
Log support for CoAP.
void coap_set_keystore(const coap_keystore_t *keystore)
Set the CoAP keystore to use by CoAP.
int coap_endpoint_cmp(const coap_endpoint_t *e1, const coap_endpoint_t *e2)
Compare two CoAP endpoints.
Definition: coap-uip.c:163
int coap_endpoint_parse(const char *text, size_t size, coap_endpoint_t *ep)
Parse a CoAP endpoint.
Definition: coap-uip.c:201
void list_init(list_t list)
Initialize a list.
Definition: list.c:57
#define LIST(name)
Declare a linked list.
Definition: list.h:89
void list_add(list_t list, void *item)
Add an item at the end of a list.
Definition: list.c:89
void list_remove(list_t list, const void *item)
Remove a specific element from a list.
Definition: list.c:152
void * list_pop(list_t list)
Remove the first object on a list.
Definition: list.c:140
void * list_head(const_list_t list)
Get a pointer to the first element of a list.
Definition: list.c:63
Linked list manipulation routines.
Header file for the Contiki OMA LWM2M engine.
Header file for the LWM2M object API.
The structure of a CoAP pre-shared key info.
Definition: coap-keystore.h:59
The structure of a CoAP keystore.
Definition: coap-keystore.h:75