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 
62 static lwm2m_status_t lwm2m_callback(lwm2m_object_instance_t *object,
63  lwm2m_context_t *ctx);
64 
65 static lwm2m_object_instance_t *get_by_id(uint16_t instance_id,
66  lwm2m_status_t *status);
67 
68 static 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 
75 LIST(instances_list);
76 static lwm2m_security_server_t instances[MAX_COUNT];
77 /*---------------------------------------------------------------------------*/
78 static lwm2m_object_instance_t *
79 create_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 /*---------------------------------------------------------------------------*/
116 static int
117 delete_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 /*---------------------------------------------------------------------------*/
139 static lwm2m_object_instance_t *
140 get_first(lwm2m_status_t *status)
141 {
142  return list_head(instances_list);
143 }
144 /*---------------------------------------------------------------------------*/
145 static lwm2m_object_instance_t *
146 get_next(lwm2m_object_instance_t *instance, lwm2m_status_t *status)
147 {
148  return instance == NULL ? NULL : instance->next;
149 }
150 /*---------------------------------------------------------------------------*/
151 static lwm2m_object_instance_t *
152 get_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 /*---------------------------------------------------------------------------*/
165 static lwm2m_status_t
166 lwm2m_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 /*---------------------------------------------------------------------------*/
237 lwm2m_security_server_t *
238 lwm2m_security_get_first(void)
239 {
240  return list_head(instances_list);
241 }
242 /*---------------------------------------------------------------------------*/
243 lwm2m_security_server_t *
244 lwm2m_security_get_next(lwm2m_security_server_t *last)
245 {
246  return last == NULL ? NULL : (lwm2m_security_server_t *)last->instance.next;
247 }
248 /*---------------------------------------------------------------------------*/
249 lwm2m_security_server_t *
250 lwm2m_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 /*---------------------------------------------------------------------------*/
305 int
306 lwm2m_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 /*---------------------------------------------------------------------------*/
333 static 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 };
341 static lwm2m_object_t reg_object = {
342  .impl = &impl,
343 };
344 /*---------------------------------------------------------------------------*/
345 #ifdef WITH_DTLS
346 #if COAP_DTLS_KEYSTORE_CONF_WITH_LWM2M
347 static int
348 get_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 }
420 static 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 /*---------------------------------------------------------------------------*/
426 void
427 lwm2m_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 /** @} */
Log support for CoAP
Header file for the LWM2M object API
int coap_endpoint_parse(const char *text, size_t size, coap_endpoint_t *ep)
Parse a CoAP endpoint.
Definition: coap-uip.c:201
Linked list manipulation routines.
The structure of a CoAP keystore.
Definition: coap-keystore.h:75
void coap_set_keystore(const coap_keystore_t *keystore)
Set the CoAP keystore to use by CoAP.
void * list_head(list_t list)
Get a pointer to the first element of a list.
Definition: list.c:82
The structure of a CoAP pre-shared key info.
Definition: coap-keystore.h:59
void list_add(list_t list, void *item)
Add an item at the end of a list.
Definition: list.c:142
void list_init(list_t list)
Initialize a list.
Definition: list.c:65
#define LIST(name)
Declare a linked list.
Definition: list.h:89
Header file for the Contiki OMA LWM2M engine
void * list_pop(list_t list)
Remove the first object on a list.
Definition: list.c:215
API for CoAP keystore
void list_remove(list_t list, void *item)
Remove a specific element from a list.
Definition: list.c:237
int coap_endpoint_cmp(const coap_endpoint_t *e1, const coap_endpoint_t *e2)
Compare two CoAP endpoints.
Definition: coap-uip.c:163