oc_obt.c 68.3 KB
Newer Older
1
/*
2
// Copyright (c) 2017-2019 Intel Corporation
3 4 5 6 7 8 9 10 11 12 13 14 15 16
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
*/

17
#include "oc_config.h"
18 19 20 21 22
#ifdef OC_SECURITY
#ifndef OC_DYNAMIC_ALLOCATION
#error "ERROR: Please rebuild with OC_DYNAMIC_ALLOCATION"
#endif /* !OC_DYNAMIC_ALLOCATION */

23 24 25 26 27
#ifndef OC_STORAGE
#error Preprocessor macro OC_SECURITY is defined but OC_STORAGE is not defined \
check oc_config.h and make sure OC_STORAGE is defined if OC_SECURITY is defined.
#endif

28 29
#include "oc_obt.h"
#include "oc_core_res.h"
30
#include "security/oc_acl_internal.h"
31
#include "security/oc_certs.h"
32
#include "security/oc_cred_internal.h"
33
#include "security/oc_doxm.h"
34
#include "security/oc_keypair.h"
35
#include "security/oc_obt_internal.h"
36 37
#include "security/oc_pstat.h"
#include "security/oc_store.h"
38
#include "security/oc_tls.h"
39 40
#include <stdlib.h>

41 42
OC_MEMB(oc_discovery_s, oc_discovery_cb_t, 1);
OC_LIST(oc_discovery_cbs);
43

44 45
OC_MEMB(oc_otm_ctx_m, oc_otm_ctx_t, 1);
OC_LIST(oc_otm_ctx_l);
46

47 48
OC_MEMB(oc_switch_dos_ctx_m, oc_switch_dos_ctx_t, 1);
OC_LIST(oc_switch_dos_ctx_l);
49

50
OC_MEMB(oc_hard_reset_ctx_m, oc_hard_reset_ctx_t, 1);
51
OC_LIST(oc_hard_reset_ctx_l);
52

53 54
OC_MEMB(oc_credprov_ctx_m, oc_credprov_ctx_t, 1);
OC_LIST(oc_credprov_ctx_l);
55

56 57 58 59 60 61 62 63 64
OC_MEMB(oc_credret_ctx_m, oc_credret_ctx_t, 1);
OC_LIST(oc_credret_ctx_l);

OC_MEMB(oc_creddel_ctx_m, oc_creddel_ctx_t, 1);
OC_LIST(oc_creddel_ctx_l);

OC_MEMB(oc_cred_m, oc_sec_cred_t, 1);
OC_MEMB(oc_creds_m, oc_sec_creds_t, 1);

65 66
OC_MEMB(oc_acl2prov_ctx_m, oc_acl2prov_ctx_t, 1);
OC_LIST(oc_acl2prov_ctx_l);
67

68 69 70 71 72 73
OC_MEMB(oc_aclret_ctx_m, oc_aclret_ctx_t, 1);
OC_LIST(oc_aclret_ctx_l);

OC_MEMB(oc_acedel_ctx_m, oc_acedel_ctx_t, 1);
OC_LIST(oc_acedel_ctx_l);

74 75
OC_MEMB(oc_aces_m, oc_sec_ace_t, 1);
OC_MEMB(oc_res_m, oc_ace_res_t, 1);
76

77 78
OC_MEMB(oc_acl_m, oc_sec_acl_t, 1);

79 80 81 82
#ifdef OC_PKI
OC_MEMB(oc_roles, oc_role_t, 1);
#endif /* OC_PKI */

83
/* Owned/unowned device caches */
84 85 86 87
OC_MEMB(oc_devices_s, oc_device_t, 1);
OC_LIST(oc_devices);
OC_LIST(oc_cache);

88
/* Public/Private key-pair for the local domain's root of trust */
89 90
#ifdef OC_PKI
const char *root_subject = "C=US, O=OCF, CN=IoTivity-Lite OBT Root";
91
uint8_t private_key[OC_ECDSA_PRIVKEY_SIZE];
92
size_t private_key_size;
93 94 95
int root_cert_credid;
#endif /* OC_PKI */

96
/* Internal utility functions */
97 98 99
oc_endpoint_t *
oc_obt_get_unsecure_endpoint(oc_endpoint_t *endpoint)
{
100
  while (endpoint && endpoint->next != NULL && endpoint->flags & SECURED) {
101 102 103 104 105 106 107
    endpoint = endpoint->next;
  }
  return endpoint;
}

oc_endpoint_t *
oc_obt_get_secure_endpoint(oc_endpoint_t *endpoint)
108
{
109
  while (endpoint && endpoint->next != NULL && !(endpoint->flags & SECURED)) {
110 111 112 113 114
    endpoint = endpoint->next;
  }
  return endpoint;
}

115 116 117 118 119 120 121 122 123 124 125 126 127
static oc_device_t *
get_device_handle(oc_uuid_t *uuid, oc_list_t list)
{
  oc_device_t *device = (oc_device_t *)oc_list_head(list);
  while (device) {
    if (memcmp(uuid->id, device->uuid.id, 16) == 0) {
      return device;
    }
    device = device->next;
  }
  return NULL;
}

128 129 130 131 132 133 134 135 136 137 138 139 140 141
oc_device_t *
oc_obt_get_cached_device_handle(oc_uuid_t *uuid)
{
  return get_device_handle(uuid, oc_cache);
}

oc_device_t *
oc_obt_get_owned_device_handle(oc_uuid_t *uuid)
{
  return get_device_handle(uuid, oc_devices);
}

bool
oc_obt_is_owned_device(oc_uuid_t *uuid)
142 143 144 145 146
{
  /* Check if we already own this device by querying our creds */
  oc_sec_creds_t *creds = oc_sec_get_creds(0);
  oc_sec_cred_t *c = (oc_sec_cred_t *)oc_list_head(creds->creds);
  while (c != NULL) {
147
    if (memcmp(c->subjectuuid.id, uuid->id, 16) == 0 && c->owner_cred) {
148 149 150 151 152 153 154
      return true;
    }
    c = c->next;
  }
  return false;
}

155 156
oc_dostype_t
oc_obt_parse_dos(oc_rep_t *rep)
157 158 159 160
{
  oc_dostype_t s = 0;
  while (rep != NULL) {
    switch (rep->type) {
161
    case OC_REP_OBJECT: {
162 163 164 165 166
      if (oc_string_len(rep->name) == 3 &&
          memcmp(oc_string(rep->name), "dos", 3) == 0) {
        oc_rep_t *dos = rep->value.object;
        while (dos != NULL) {
          switch (dos->type) {
167
          case OC_REP_INT: {
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
            if (oc_string_len(dos->name) == 1 &&
                oc_string(dos->name)[0] == 's') {
              s = dos->value.integer;
            }
          } break;
          default:
            break;
          }
          dos = dos->next;
        }
      }
    } break;
    default:
      break;
    }
    rep = rep->next;
  }
  return s;
}

188
static oc_device_t *
189
cache_new_device(oc_list_t list, oc_uuid_t *uuid, oc_endpoint_t *endpoint)
190
{
191 192 193 194 195 196 197
  oc_device_t *device = (oc_device_t *)oc_list_head(list);
  while (device != NULL) {
    if (memcmp(device->uuid.id, uuid->id, sizeof(oc_uuid_t)) == 0) {
      break;
    }
    device = device->next;
  }
198

199
  if (!device) {
Andreas Zisowsky's avatar
Andreas Zisowsky committed
200
    device = oc_memb_alloc(&oc_devices_s);
201 202 203 204 205 206
    if (!device) {
      return NULL;
    }
    memcpy(device->uuid.id, uuid->id, sizeof(oc_uuid_t));
    oc_list_add(list, device);
  }
207

208 209 210
  if (device->endpoint) {
    oc_free_server_endpoints(device->endpoint);
  }
211 212 213 214 215 216 217 218 219 220

  oc_endpoint_t *ep = oc_new_endpoint();
  if (!ep) {
    oc_list_remove(list, device);
    oc_memb_free(&oc_devices_s, device);
    return NULL;
  }

  memcpy(ep, endpoint, sizeof(oc_endpoint_t));
  device->endpoint = ep;
221
  ep->next = NULL;
222
  return device;
223 224 225 226 227 228 229
}

static oc_event_callback_retval_t
free_device(void *data)
{
  oc_device_t *device = (oc_device_t *)data;
  oc_free_server_endpoints(device->endpoint);
230 231
  oc_list_remove(oc_cache, device);
  oc_list_remove(oc_devices, device);
232
  oc_memb_free(&oc_devices_s, device);
233
  return OC_EVENT_DONE;
234 235
}

236
#ifdef OC_PKI
237 238 239 240 241 242 243 244 245
static void
oc_obt_dump_state(void)
{
  uint8_t *buf = malloc(OC_MAX_APP_DATA_SIZE);
  if (!buf)
    return;

  oc_rep_new(buf, OC_MAX_APP_DATA_SIZE);
  oc_rep_start_root_object();
246 247
  oc_rep_set_byte_string(root, private_key, private_key, private_key_size);
  oc_rep_set_int(root, credid, root_cert_credid);
248 249
  oc_rep_end_root_object();

250
  int size = oc_rep_get_encoded_payload_size();
251
  if (size > 0) {
252
    OC_DBG("oc_obt: dumped current state: size %d", size);
253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271
    oc_storage_write("obt_state", buf, size);
  }

  free(buf);
}

static void
oc_obt_load_state(void)
{
  long ret = 0;
  oc_rep_t *rep, *head;

  uint8_t *buf = malloc(OC_MAX_APP_DATA_SIZE);
  if (!buf) {
    return;
  }

  ret = oc_storage_read("obt_state", buf, OC_MAX_APP_DATA_SIZE);
  if (ret > 0) {
272
    struct oc_memb rep_objects = { sizeof(oc_rep_t), 0, 0, 0, 0 };
273
    oc_rep_set_pool(&rep_objects);
Andreas Zisowsky's avatar
Andreas Zisowsky committed
274
    int err = oc_parse_rep(buf, ret, &rep);
275 276 277 278
    head = rep;
    if (err == 0) {
      while (rep != NULL) {
        switch (rep->type) {
279
        case OC_REP_INT:
280 281
          if (oc_string_len(rep->name) == 6 &&
              memcmp(oc_string(rep->name), "credid", 6) == 0) {
282 283
            root_cert_credid = (int)rep->value.integer;
          }
284
          break;
285 286 287 288 289 290 291
        case OC_REP_BYTE_STRING:
          if (oc_string_len(rep->name) == 11 &&
              memcmp(oc_string(rep->name), "private_key", 11) == 0) {
            private_key_size = oc_string_len(rep->value.string);
            memcpy(private_key, oc_string(rep->value.string), private_key_size);
          }
          break;
292 293 294 295 296 297 298 299 300 301
        default:
          break;
        }
        rep = rep->next;
      }
    }
    oc_free_rep(head);
  }
  free(buf);
}
302
#endif /* OC_PKI */
303

304 305 306 307 308
struct list
{
  struct list *next;
};

309
bool
310 311 312 313 314 315 316 317 318 319 320 321
is_item_in_list(oc_list_t list, void *item)
{
  struct list *h = oc_list_head(list);
  while (h != NULL) {
    if (h == item) {
      return true;
    }
    h = h->next;
  }
  return false;
}

322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337
bool
oc_obt_is_otm_ctx_valid(oc_otm_ctx_t *ctx)
{
  return is_item_in_list(oc_otm_ctx_l, ctx);
}

oc_otm_ctx_t *
oc_obt_alloc_otm_ctx(void)
{
  oc_otm_ctx_t *o = (oc_otm_ctx_t *)oc_memb_alloc(&oc_otm_ctx_m);
  if (o) {
    oc_list_add(oc_otm_ctx_l, o);
  }
  return o;
}

338
/* End of utility functions */
339

340
/* Ownership Transfer */
341
static void
342
free_otm_state(oc_otm_ctx_t *o, int status, oc_obt_otm_t otm)
343
{
344 345 346 347
  if (!is_item_in_list(oc_otm_ctx_l, o)) {
    return;
  }
  oc_list_remove(oc_otm_ctx_l, o);
348
  oc_endpoint_t *ep = oc_obt_get_secure_endpoint(o->device->endpoint);
349
  oc_tls_close_connection(ep);
350
  if (status == -1) {
Jaehong Jo's avatar
Jaehong Jo committed
351 352
    char suuid[OC_UUID_LEN];
    oc_uuid_to_str(&o->device->uuid, suuid, OC_UUID_LEN);
353
    oc_cred_remove_subject(suuid, 0);
354 355 356
    o->cb.cb(&o->device->uuid, status, o->cb.data);
    free_device(o->device);
  } else {
357 358 359 360
    if (otm != OC_OBT_RDP) {
      oc_list_remove(oc_cache, o->device);
      oc_list_add(oc_devices, o->device);
    }
361
    o->cb.cb(&o->device->uuid, status, o->cb.data);
362
  }
363
  oc_memb_free(&oc_otm_ctx_m, o);
364 365
}

366 367
void
oc_obt_free_otm_ctx(oc_otm_ctx_t *ctx, int status, oc_obt_otm_t otm)
368
{
369
  free_otm_state(ctx, status, otm);
370 371 372
}

/* Device discovery */
373 374 375 376 377
/* Owned/Unowned discovery timeout */
static oc_event_callback_retval_t
free_discovery_cb(void *data)
{
  oc_discovery_cb_t *c = (oc_discovery_cb_t *)data;
378 379 380 381
  if (is_item_in_list(oc_discovery_cbs, c)) {
    oc_list_remove(oc_discovery_cbs, c);
    oc_memb_free(&oc_discovery_s, c);
  }
382 383 384
  return OC_EVENT_DONE;
}

385
static void
386
get_endpoints(oc_client_response_t *data)
387
{
388 389 390
  if (data->code >= OC_STATUS_BAD_REQUEST) {
    return;
  }
391 392
  oc_rep_t *links = data->payload;

393
  oc_uuid_t di;
394
  oc_rep_t *link = (links) ? links->value.object : NULL;
395 396 397 398 399 400 401 402 403 404 405 406 407 408
  while (link != NULL) {
    switch (link->type) {
    case OC_REP_STRING: {
      if (oc_string_len(link->name) == 6 &&
          memcmp(oc_string(link->name), "anchor", 6) == 0) {
        oc_str_to_uuid(oc_string(link->value.string) + 6, &di);
        break;
      }
    } break;
    default:
      break;
    }
    link = link->next;
  }
409

410 411 412 413 414 415 416 417 418 419
  oc_uuid_t *my_uuid = oc_core_get_device_id(0);
  if (memcmp(my_uuid->id, di.id, 16) == 0) {
    return;
  }

  oc_discovery_cb_t *cb = NULL;
  oc_device_t *device = NULL;
  oc_client_cb_t *ccb = (oc_client_cb_t *)data->client_cb;
  if (ccb->multicast) {
    cb = (oc_discovery_cb_t *)data->user_data;
420
    if (links && oc_obt_is_owned_device(&di)) {
421 422 423 424 425 426 427 428 429 430 431 432 433 434
      device = cache_new_device(oc_devices, &di, data->endpoint);
    }
  } else {
    device = (oc_device_t *)data->user_data;
    cb = (oc_discovery_cb_t *)device->ctx;
  }

  if (!device) {
    return;
  }

  oc_free_server_endpoints(device->endpoint);
  device->endpoint = NULL;

435 436
  oc_endpoint_t *eps_cur = NULL;
  link = links->value.object;
437
  oc_endpoint_t temp_ep;
438 439 440 441 442 443 444 445 446 447 448 449 450
  while (link != NULL) {
    switch (link->type) {
    case OC_REP_OBJECT_ARRAY: {
      oc_rep_t *eps = link->value.object_array;
      while (eps != NULL) {
        oc_rep_t *ep = eps->value.object;
        while (ep != NULL) {
          switch (ep->type) {
          case OC_REP_STRING: {
            if (oc_string_len(ep->name) == 2 &&
                memcmp(oc_string(ep->name), "ep", 2) == 0) {
              if (oc_string_to_endpoint(&ep->value.string, &temp_ep, NULL) ==
                  0) {
451 452 453 454 455 456
                if (((data->endpoint->flags & IPV4) &&
                     (temp_ep.flags & IPV6)) ||
                    ((data->endpoint->flags & IPV6) &&
                     (temp_ep.flags & IPV4))) {
                  goto next_ep;
                }
457 458 459 460 461 462
                if (eps_cur) {
                  eps_cur->next = oc_new_endpoint();
                  eps_cur = eps_cur->next;
                } else {
                  eps_cur = device->endpoint = oc_new_endpoint();
                }
463

464 465
                if (eps_cur) {
                  memcpy(eps_cur, &temp_ep, sizeof(oc_endpoint_t));
466 467
                  eps_cur->next = NULL;
                  eps_cur->device = data->endpoint->device;
468 469
                  memcpy(eps_cur->di.id, di.id, 16);
                  eps_cur->interface_index = data->endpoint->interface_index;
470 471
                  oc_endpoint_set_local_address(
                    eps_cur, data->endpoint->interface_index);
472 473 474
                  if (oc_ipv6_endpoint_is_link_local(eps_cur) == 0 &&
                      oc_ipv6_endpoint_is_link_local(data->endpoint) == 0) {
                    eps_cur->addr.ipv6.scope = data->endpoint->addr.ipv6.scope;
475 476 477 478
                  }
                }
              }
            }
479 480 481
          } break;
          default:
            break;
482
          }
483
          ep = ep->next;
484
        }
485
      next_ep:
486
        eps = eps->next;
487
      }
488 489 490
    } break;
    default:
      break;
491
    }
492
    link = link->next;
493
  }
494 495 496 497 498

  if (!is_item_in_list(oc_discovery_cbs, cb) || !device->endpoint) {
    return;
  }
  cb->cb(&device->uuid, device->endpoint, cb->data);
499
}
500

501 502 503
static void
obt_check_owned(oc_client_response_t *data)
{
504 505 506 507
  if (data->code >= OC_STATUS_BAD_REQUEST) {
    return;
  }

508
  oc_uuid_t uuid;
509
  int owned = -1;
510
  oc_rep_t *rep = data->payload;
511

512 513
  while (rep != NULL) {
    switch (rep->type) {
514 515 516 517 518 519
    case OC_REP_STRING:
      if (oc_string_len(rep->name) == 10 &&
          memcmp(oc_string(rep->name), "deviceuuid", 10) == 0) {
        oc_str_to_uuid(oc_string(rep->value.string), &uuid);
      }
      break;
520
    case OC_REP_BOOL:
521 522
      if (oc_string_len(rep->name) == 5 &&
          memcmp(oc_string(rep->name), "owned", 5) == 0) {
523
        owned = (int)rep->value.boolean;
524 525 526 527 528 529 530 531
      }
      break;
    default:
      break;
    }
    rep = rep->next;
  }

532 533 534 535
  if (owned == -1) {
    return;
  }

536 537 538 539 540
  oc_uuid_t *my_uuid = oc_core_get_device_id(0);
  if (memcmp(my_uuid->id, uuid.id, 16) == 0) {
    return;
  }

541
  oc_device_t *device = NULL;
542

543
  if (owned == 0) {
544
    device = cache_new_device(oc_cache, &uuid, data->endpoint);
545 546
  }

547 548 549 550
  if (device) {
    device->ctx = data->user_data;
    oc_do_get("/oic/res", device->endpoint, "rt=oic.r.doxm", &get_endpoints,
              HIGH_QOS, device);
551 552 553 554
  }
}

/* Unowned device discovery */
555 556
static int
discover_unowned_devices(uint8_t scope, oc_obt_discovery_cb_t cb, void *data)
557
{
558
  oc_discovery_cb_t *c = (oc_discovery_cb_t *)oc_memb_alloc(&oc_discovery_s);
559 560 561 562 563
  if (!c) {
    return -1;
  }
  c->cb = cb;
  c->data = data;
564

565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585
  if (scope == 0x02) {
    if (oc_do_ip_multicast("/oic/sec/doxm", "owned=FALSE", &obt_check_owned,
                           c)) {
      oc_list_add(oc_discovery_cbs, c);
      oc_set_delayed_callback(c, free_discovery_cb, DISCOVERY_CB_PERIOD);
      return 0;
    }
  } else if (scope == 0x03) {
    if (oc_do_realm_local_ipv6_multicast("/oic/sec/doxm", "owned=FALSE",
                                         &obt_check_owned, c)) {
      oc_list_add(oc_discovery_cbs, c);
      oc_set_delayed_callback(c, free_discovery_cb, DISCOVERY_CB_PERIOD);
      return 0;
    }
  } else if (scope == 0x05) {
    if (oc_do_site_local_ipv6_multicast("/oic/sec/doxm", "owned=FALSE",
                                        &obt_check_owned, c)) {
      oc_list_add(oc_discovery_cbs, c);
      oc_set_delayed_callback(c, free_discovery_cb, DISCOVERY_CB_PERIOD);
      return 0;
    }
586
  }
587

588
  oc_memb_free(&oc_discovery_s, c);
589 590 591 592
  return -1;
}

int
593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614
oc_obt_discover_unowned_devices_realm_local_ipv6(oc_obt_discovery_cb_t cb,
                                                 void *data)
{
  return discover_unowned_devices(0x03, cb, data);
}

int
oc_obt_discover_unowned_devices_site_local_ipv6(oc_obt_discovery_cb_t cb,
                                                void *data)
{
  return discover_unowned_devices(0x05, cb, data);
}

int
oc_obt_discover_unowned_devices(oc_obt_discovery_cb_t cb, void *data)
{
  return discover_unowned_devices(0x02, cb, data);
}

/* Owned device disvoery */
static int
discover_owned_devices(uint8_t scope, oc_obt_discovery_cb_t cb, void *data)
615
{
616
  oc_discovery_cb_t *c = (oc_discovery_cb_t *)oc_memb_alloc(&oc_discovery_s);
617 618 619 620 621 622
  if (!c) {
    return -1;
  }
  c->cb = cb;
  c->data = data;

623
  if (scope == 0x02) {
624
    if (oc_do_ip_multicast("/oic/res", "rt=oic.r.doxm", &get_endpoints, c)) {
625 626 627 628 629
      oc_list_add(oc_discovery_cbs, c);
      oc_set_delayed_callback(c, free_discovery_cb, DISCOVERY_CB_PERIOD);
      return 0;
    }
  } else if (scope == 0x03) {
630
    if (oc_do_realm_local_ipv6_multicast("/oic/res", "rt=oic.r.doxm",
631
                                         &get_endpoints, c)) {
632 633 634 635 636
      oc_list_add(oc_discovery_cbs, c);
      oc_set_delayed_callback(c, free_discovery_cb, DISCOVERY_CB_PERIOD);
      return 0;
    }
  } else if (scope == 0x05) {
637
    if (oc_do_site_local_ipv6_multicast("/oic/res", "rt=oic.r.doxm",
638
                                        &get_endpoints, c)) {
639 640 641 642
      oc_list_add(oc_discovery_cbs, c);
      oc_set_delayed_callback(c, free_discovery_cb, DISCOVERY_CB_PERIOD);
      return 0;
    }
643
  }
644

645
  oc_memb_free(&oc_discovery_s, c);
646 647
  return -1;
}
648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667

int
oc_obt_discover_owned_devices_realm_local_ipv6(oc_obt_discovery_cb_t cb,
                                               void *data)
{
  return discover_owned_devices(0x03, cb, data);
}

int
oc_obt_discover_owned_devices_site_local_ipv6(oc_obt_discovery_cb_t cb,
                                              void *data)
{
  return discover_owned_devices(0x05, cb, data);
}

int
oc_obt_discover_owned_devices(oc_obt_discovery_cb_t cb, void *data)
{
  return discover_owned_devices(0x02, cb, data);
}
668
/* End of device discovery */
669

670 671 672
/* Resource discovery */

int
673 674
oc_obt_discover_all_resources(oc_uuid_t *uuid,
                              oc_discovery_all_handler_t handler, void *data)
675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691
{
  oc_endpoint_t *ep = NULL;
  oc_device_t *device = get_device_handle(uuid, oc_devices);

  if (device) {
    ep = oc_obt_get_secure_endpoint(device->endpoint);
  } else {
    device = get_device_handle(uuid, oc_cache);
    if (device) {
      ep = oc_obt_get_unsecure_endpoint(device->endpoint);
    }
  }

  if (!device || !ep) {
    return -1;
  }

692
  if (oc_do_ip_discovery_all_at_endpoint(handler, ep, data)) {
693 694 695 696 697 698 699 700
    return 0;
  }

  return -1;
}

/* End of resource discovery */

701
/* Helper sequence to switch between pstat device states */
702 703 704
static void
free_switch_dos_state(oc_switch_dos_ctx_t *d)
{
705 706 707
  if (!is_item_in_list(oc_switch_dos_ctx_l, d)) {
    return;
  }
708 709 710 711
  oc_list_remove(oc_switch_dos_ctx_l, d);
  oc_memb_free(&oc_switch_dos_ctx_m, d);
}

712 713 714 715
static void
free_switch_dos_ctx(oc_switch_dos_ctx_t *d, int status)
{
  oc_status_cb_t cb = d->cb;
716
  free_switch_dos_state(d);
717 718 719 720 721 722
  cb.cb(status, cb.data);
}

static void
pstat_POST_dos1_to_dos2(oc_client_response_t *data)
{
723 724 725 726
  if (!is_item_in_list(oc_switch_dos_ctx_l, data->user_data)) {
    return;
  }

727
  oc_switch_dos_ctx_t *d = (oc_switch_dos_ctx_t *)data->user_data;
728

729 730
  if (data->code >= OC_STATUS_BAD_REQUEST &&
      data->code != OC_STATUS_SERVICE_UNAVAILABLE) {
731 732 733
    free_switch_dos_ctx(d, -1);
    return;
  }
734

735
  free_switch_dos_ctx(d, 0);
736 737
}

738
static oc_switch_dos_ctx_t *
739 740 741
switch_dos(oc_device_t *device, oc_dostype_t dos, oc_obt_status_cb_t cb,
           void *data)
{
742
  oc_endpoint_t *ep = oc_obt_get_secure_endpoint(device->endpoint);
743
  if (!ep) {
744
    return NULL;
745
  }
746

747
  oc_switch_dos_ctx_t *d =
748
    (oc_switch_dos_ctx_t *)oc_memb_alloc(&oc_switch_dos_ctx_m);
749
  if (!d) {
750
    return NULL;
751
  }
752

753 754 755 756 757 758
  /* oc_switch_dos_ctx_t */
  d->device = device;
  d->dos = dos;
  /* oc_status_cb_t */
  d->cb.cb = cb;
  d->cb.data = data;
759

760
  if (oc_init_post("/oic/sec/pstat", ep, NULL, &pstat_POST_dos1_to_dos2,
761
                   HIGH_QOS, d)) {
762 763 764 765 766 767
    oc_rep_start_root_object();
    oc_rep_set_object(root, dos);
    oc_rep_set_int(dos, s, dos);
    oc_rep_close_object(root, dos);
    oc_rep_end_root_object();
    if (oc_do_post()) {
768 769
      oc_list_add(oc_switch_dos_ctx_l, d);
      return d;
770 771
    }
  }
772 773 774

  oc_memb_free(&oc_switch_dos_ctx_m, d);
  return NULL;
775
}
776
/* End of switch dos sequence */
777

778
/* Hard RESET sequence */
779 780 781
static void
free_hard_reset_ctx(oc_hard_reset_ctx_t *ctx, int status)
{
782 783 784 785
  if (!is_item_in_list(oc_hard_reset_ctx_l, ctx)) {
    return;
  }
  oc_list_remove(oc_hard_reset_ctx_l, ctx);
786
  oc_device_status_cb_t cb = ctx->cb;
787
  oc_endpoint_t *ep = oc_obt_get_secure_endpoint(ctx->device->endpoint);
788
  oc_tls_close_connection(ep);
789 790
  if (status == 0) {
    /* Remove device's credential from OBT's credential store */
791 792
    char subjectuuid[OC_UUID_LEN];
    oc_uuid_to_str(&ctx->device->uuid, subjectuuid, OC_UUID_LEN);
793 794 795 796 797
    oc_cred_remove_subject(subjectuuid, 0);
    cb.cb(&ctx->device->uuid, 0, cb.data);
  } else {
    cb.cb(&ctx->device->uuid, -1, cb.data);
  }
798
  free_device(ctx->device);
799 800 801 802
  if (ctx->switch_dos) {
    free_switch_dos_state(ctx->switch_dos);
  }
  oc_memb_free(&oc_hard_reset_ctx_m, ctx);
803 804 805 806 807
}

static void
hard_reset_cb(int status, void *data)
{
808
  oc_hard_reset_ctx_t *d = (oc_hard_reset_ctx_t *)data;
809 810 811
  if (!is_item_in_list(oc_hard_reset_ctx_l, d)) {
    return;
  }
812
  d->switch_dos = NULL;
813 814 815 816
  free_hard_reset_ctx(data, status);
}

int
817 818
oc_obt_device_hard_reset(oc_uuid_t *uuid, oc_obt_device_status_cb_t cb,
                         void *data)
819 820
{
  oc_hard_reset_ctx_t *d =
821
    (oc_hard_reset_ctx_t *)oc_memb_alloc(&oc_hard_reset_ctx_m);
822 823 824
  if (!d) {
    return -1;
  }
825

826
  if (!oc_obt_is_owned_device(uuid)) {
827 828 829
    return -1;
  }

830
  oc_device_t *device = oc_obt_get_owned_device_handle(uuid);
831 832 833 834
  if (!device) {
    return -1;
  }

835 836 837 838
  d->cb.cb = cb;
  d->cb.data = data;
  d->device = device;

839 840 841 842
  d->switch_dos = switch_dos(device, OC_DOS_RESET, hard_reset_cb, d);
  if (!d->switch_dos) {
    oc_memb_free(&oc_hard_reset_ctx_m, d);
    return -1;
843
  }
844

845
  oc_list_add(oc_hard_reset_ctx_l, d);
846 847

  return 0;
848
}
849
/* End of hard RESET sequence */
850

851
/* Provision pairwise credentials sequence */
852
static void
853
free_credprov_state(oc_credprov_ctx_t *p, int status)
854
{
855 856 857 858
  if (!is_item_in_list(oc_credprov_ctx_l, p)) {
    return;
  }
  oc_list_remove(oc_credprov_ctx_l, p);
859
  oc_endpoint_t *ep = oc_obt_get_secure_endpoint(p->device1->endpoint);
860
  oc_tls_close_connection(ep);
861 862 863 864
  if (p->device2) {
    ep = oc_obt_get_secure_endpoint(p->device2->endpoint);
    oc_tls_close_connection(ep);
  }
865
  p->cb.cb(status, p->cb.data);
866 867 868 869 870 871
#ifdef OC_PKI
  if (p->roles) {
    oc_obt_free_roleid(p->roles);
    p->roles = NULL;
  }
#endif /* OC_PKI */
872 873
  if (p->switch_dos) {
    free_switch_dos_state(p->switch_dos);
874
    p->switch_dos = NULL;
875 876
  }
  oc_memb_free(&oc_credprov_ctx_m, p);
877 878
}

879 880 881 882 883 884
static void
free_credprov_ctx(oc_credprov_ctx_t *ctx, int status)
{
  free_credprov_state(ctx, status);
}

885 886 887
static void
device2_RFNOP(int status, void *data)
{
888 889 890 891
  if (!is_item_in_list(oc_credprov_ctx_l, data)) {
    return;
  }

892
  oc_credprov_ctx_t *p = (oc_credprov_ctx_t *)data;
893 894
  p->switch_dos = NULL;

895
  if (status >= 0) {
896
    free_credprov_ctx(p, 0);
897
  } else {
898
    free_credprov_ctx(p, -1);
899 900 901 902 903 904
  }
}

static void
device1_RFNOP(int status, void *data)
{
905 906 907 908
  if (!is_item_in_list(oc_credprov_ctx_l, data)) {
    return;
  }

909
  oc_credprov_ctx_t *p = (oc_credprov_ctx_t *)data;
910 911
  p->switch_dos = NULL;

912
  if (status >= 0) {
913 914 915 916
    p->switch_dos = switch_dos(p->device2, OC_DOS_RFNOP, device2_RFNOP, p);
    if (p->switch_dos) {
      return;
    }
917
  }
918 919

  free_credprov_ctx(p, -1);
920 921 922 923 924
}

static void
device2_cred(oc_client_response_t *data)
{
925 926 927 928
  if (!is_item_in_list(oc_credprov_ctx_l, data->user_data)) {
    return;
  }

929 930 931
  oc_credprov_ctx_t *p = (oc_credprov_ctx_t *)data->user_data;

  if (data->code >= OC_STATUS_BAD_REQUEST) {
932
    free_credprov_ctx(p, -1);
933 934 935
    return;
  }

936 937 938 939
  p->switch_dos = switch_dos(p->device1, OC_DOS_RFNOP, device1_RFNOP, p);
  if (!p->switch_dos) {
    free_credprov_ctx(p, -1);
  }
940 941 942 943 944
}

static void
device1_cred(oc_client_response_t *data)
{
945 946 947 948
  if (!is_item_in_list(oc_credprov_ctx_l, data->user_data)) {
    return;
  }

949 950 951
  oc_credprov_ctx_t *p = (oc_credprov_ctx_t *)data->user_data;

  if (data->code >= OC_STATUS_BAD_REQUEST) {
952
    free_credprov_ctx(p, -1);
953 954 955
    return;
  }

Jaehong Jo's avatar
Jaehong Jo committed
956 957
  char d1uuid[OC_UUID_LEN];
  oc_uuid_to_str(&p->device1->uuid, d1uuid, OC_UUID_LEN);
958

959
  oc_endpoint_t *ep = oc_obt_get_secure_endpoint(p->device2->endpoint);
960

961
  if (oc_init_post("/oic/sec/cred", ep, NULL, &device2_cred, HIGH_QOS, p)) {
962 963 964 965 966 967 968 969 970 971 972 973 974 975 976
    oc_rep_start_root_object();
    oc_rep_set_array(root, creds);
    oc_rep_object_array_start_item(creds);

    oc_rep_set_int(creds, credtype, 1);
    oc_rep_set_text_string(creds, subjectuuid, d1uuid);

    oc_rep_set_object(creds, privatedata);
    oc_rep_set_byte_string(privatedata, data, p->key, 16);
    oc_rep_set_text_string(privatedata, encoding, "oic.sec.encoding.raw");
    oc_rep_close_object(creds, privatedata);

    oc_rep_object_array_end_item(creds);
    oc_rep_close_array(root, creds);
    oc_rep_end_root_object();
977 978 979
    if (oc_do_post()) {
      return;
    }
980
  }
981 982

  free_credprov_ctx(p, -1);
983 984 985 986 987
}

static void
device2_RFPRO(int status, void *data)
{
988 989 990 991
  if (!is_item_in_list(oc_credprov_ctx_l, data)) {
    return;
  }

992
  oc_credprov_ctx_t *p = (oc_credprov_ctx_t *)data;
993 994
  p->switch_dos = NULL;

995 996 997 998 999 1000 1001 1002
  if (status >= 0) {
    int i;
    for (i = 0; i < 4; i++) {
      unsigned int r = oc_random_value();
      memcpy(&p->key[i * 4], &r, sizeof(r));
      i += 4;
    }

Jaehong Jo's avatar
Jaehong Jo committed
1003 1004
    char d2uuid[OC_UUID_LEN];
    oc_uuid_to_str(&p->device2->uuid, d2uuid, OC_UUID_LEN);
1005

1006
    oc_endpoint_t *ep = oc_obt_get_secure_endpoint(p->device1->endpoint);
1007

1008
    if (oc_init_post("/oic/sec/cred", ep, NULL, &device1_cred, HIGH_QOS, p)) {
1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023
      oc_rep_start_root_object();
      oc_rep_set_array(root, creds);
      oc_rep_object_array_start_item(creds);

      oc_rep_set_int(creds, credtype, 1);
      oc_rep_set_text_string(creds, subjectuuid, d2uuid);

      oc_rep_set_object(creds, privatedata);
      oc_rep_set_byte_string(privatedata, data, p->key, 16);
      oc_rep_set_text_string(privatedata, encoding, "oic.sec.encoding.raw");
      oc_rep_close_object(creds, privatedata);

      oc_rep_object_array_end_item(creds);
      oc_rep_close_array(root, creds);
      oc_rep_end_root_object();
1024 1025 1026
      if (oc_do_post()) {
        return;
      }
1027 1028
    }
  }
1029 1030

  free_credprov_state(p, -1);
1031 1032 1033 1034 1035
}

static void
device1_RFPRO(int status, void *data)
{
1036 1037 1038 1039
  if (!is_item_in_list(oc_credprov_ctx_l, data)) {
    return;
  }

1040
  oc_credprov_ctx_t *p = (oc_credprov_ctx_t *)data;
1041 1042

  p->switch_dos = NULL;
1043
  if (status >= 0) {
1044 1045 1046 1047
    p->switch_dos = switch_dos(p->device2, OC_DOS_RFPRO, device2_RFPRO, p);
    if (!p->switch_dos) {
      free_credprov_ctx(p, -1);
    }
1048 1049 1050
  }
}

1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096
int
oc_obt_provision_pairwise_credentials(oc_uuid_t *uuid1, oc_uuid_t *uuid2,
                                      oc_obt_status_cb_t cb, void *data)
{
  oc_credprov_ctx_t *p = oc_memb_alloc(&oc_credprov_ctx_m);
  if (!p) {
    return -1;
  }

  if (!oc_obt_is_owned_device(uuid1)) {
    return -1;
  }

  if (!oc_obt_is_owned_device(uuid2)) {
    return -1;
  }

  oc_device_t *device1 = oc_obt_get_owned_device_handle(uuid1);
  if (!device1) {
    return -1;
  }

  oc_device_t *device2 = oc_obt_get_owned_device_handle(uuid2);
  if (!device2) {
    return -1;
  }

  p->cb.cb = cb;
  p->cb.data = data;
  p->device1 = device1;
  p->device2 = device2;

  oc_tls_select_psk_ciphersuite();

  p->switch_dos = switch_dos(device1, OC_DOS_RFPRO, device1_RFPRO, p);
  if (!p->switch_dos) {
    oc_memb_free(&oc_credprov_ctx_m, p);
    return -1;
  }

  oc_list_add(oc_credprov_ctx_l, p);

  return 0;
}
/* End of provision pair-wise credentials sequence */

1097
#ifdef OC_PKI
1098
/* Construct list of role ids to encode into a role certificate */
1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110