From ff99eee5a28ac5c2433ac004077fbf3ed23642f9 Mon Sep 17 00:00:00 2001 From: Daniel Adam Date: Fri, 16 Jun 2023 18:31:22 +0200 Subject: [PATCH] Refactor oc_cred.c and oc_roles.c --- api/oc_client_role.c | 6 +- api/oc_core_res.c | 17 +- api/oc_helpers.c | 32 +- api/oc_helpers_internal.h | 34 ++ api/oc_main.c | 4 + api/oc_ri.c | 4 +- security/oc_acl.c | 12 +- security/oc_cred.c | 590 ++++++++++++++++---------------- security/oc_cred_internal.h | 46 ++- security/oc_obt.c | 3 +- security/oc_obt_certs.c | 11 +- security/oc_obt_otm_cert.c | 9 +- security/oc_obt_otm_justworks.c | 9 +- security/oc_obt_otm_randompin.c | 14 +- security/oc_pki.c | 16 +- security/oc_roles.c | 325 +++++++++++++----- security/oc_roles_internal.h | 87 ++++- security/oc_svr.c | 14 +- security/unittest/rolestest.cpp | 241 +++++++++++++ tests/gtest/tls/DTLS.cpp | 9 +- 20 files changed, 1013 insertions(+), 470 deletions(-) create mode 100644 security/unittest/rolestest.cpp diff --git a/api/oc_client_role.c b/api/oc_client_role.c index 89bfb8003e..0e21c2a00e 100644 --- a/api/oc_client_role.c +++ b/api/oc_client_role.c @@ -27,7 +27,7 @@ oc_role_t * oc_get_all_roles(void) { - return oc_sec_get_role_creds(); + return oc_sec_role_creds_get(); } static void @@ -74,7 +74,7 @@ oc_assert_role(const char *role, const char *authority, return false; } oc_tls_select_cert_ciphersuite(); - if (!oc_init_post("/oic/sec/roles", endpoint, NULL, handler, HIGH_QOS, + if (!oc_init_post(OCF_SEC_ROLES_URI, endpoint, NULL, handler, HIGH_QOS, user_data)) { OC_ERR("cannot init POST"); } @@ -103,7 +103,7 @@ oc_assert_all_roles(const oc_endpoint_t *endpoint, if (roles == NULL) { return; } - if (!oc_init_post("/oic/sec/roles", endpoint, NULL, handler, HIGH_QOS, + if (!oc_init_post(OCF_SEC_ROLES_URI, endpoint, NULL, handler, HIGH_QOS, user_data)) { OC_ERR("cannot init POST"); } diff --git a/api/oc_core_res.c b/api/oc_core_res.c index 9da38e1712..e42c4836b7 100644 --- a/api/oc_core_res.c +++ b/api/oc_core_res.c @@ -16,10 +16,11 @@ * ****************************************************************************/ -#include "oc_core_res.h" #include "messaging/coap/oc_coap.h" #include "oc_api.h" +#include "oc_core_res.h" #include "oc_core_res_internal.h" +#include "oc_csr.h" #include "oc_discovery.h" #include "oc_endpoint.h" #include "oc_introspection_internal.h" @@ -28,6 +29,7 @@ #include "oc_ri_internal.h" #include "oc_main.h" #include "oc_server_api_internal.h" +#include "oc_swupdate_internal.h" #include "port/oc_assert.h" #include "util/oc_atomic.h" #include "util/oc_compiler.h" @@ -45,6 +47,7 @@ #ifdef OC_SECURITY #include "security/oc_doxm_internal.h" #include "security/oc_pstat.h" +#include "security/oc_roles_internal.h" #include "security/oc_sdi_internal.h" #include "security/oc_sp_internal.h" #include "security/oc_tls_internal.h" @@ -873,12 +876,12 @@ oc_core_get_resource_type_by_uri(const char *uri) return OCF_SEC_SP; } #ifdef OC_PKI - if (core_is_resource_uri(uri, uri_len, "/oic/sec/csr", - OC_CHAR_ARRAY_LEN("/oic/sec/csr"))) { + if (core_is_resource_uri(uri, uri_len, OCF_SEC_CSR_URI, + OC_CHAR_ARRAY_LEN(OCF_SEC_CSR_URI))) { return OCF_SEC_CSR; } - if (core_is_resource_uri(uri, uri_len, "/oic/sec/roles", - OC_CHAR_ARRAY_LEN("/oic/sec/roles"))) { + if (core_is_resource_uri(uri, uri_len, OCF_SEC_ROLES_URI, + OC_CHAR_ARRAY_LEN(OCF_SEC_ROLES_URI))) { return OCF_SEC_ROLES; } #endif /* OC_PKI */ @@ -888,8 +891,8 @@ oc_core_get_resource_type_by_uri(const char *uri) } #endif /* OC_SECURITY */ #ifdef OC_SOFTWARE_UPDATE - if (core_is_resource_uri(uri, uri_len, "/oc/swu", - OC_CHAR_ARRAY_LEN("/oc/swu"))) { + if (core_is_resource_uri(uri, uri_len, OCF_SW_UPDATE_URI, + OC_CHAR_ARRAY_LEN(OCF_SW_UPDATE_URI))) { return OCF_SW_UPDATE; } #endif /* OC_SOFTWARE_UPDATE */ diff --git a/api/oc_helpers.c b/api/oc_helpers.c index 0215773161..8399e4b913 100644 --- a/api/oc_helpers.c +++ b/api/oc_helpers.c @@ -137,18 +137,44 @@ oc_set_string(oc_string_t *dst, const char *str, size_t str_len) #endif /* OC_DYNAMIC_ALLOCATION */ } +oc_string_view_t +oc_string_view(const char *data, size_t length) +{ + oc_string_view_t view = { + .data = data, + .length = length, + }; + return view; +} + +oc_string_view_t +oc_string_view2(const oc_string_t *str) +{ + if (str == NULL) { + return oc_string_view(NULL, 0); + } + return oc_string_view(oc_string(*str), oc_string_len(*str)); +} + +bool +oc_string_view_is_equal(oc_string_view_t str1, oc_string_view_t str2) +{ + return str1.length == str2.length && + (str1.length == 0 || memcmp(str1.data, str2.data, str1.length) == 0); +} + bool oc_string_is_equal(const oc_string_t *str1, const oc_string_t *str2) { - return oc_string_is_cstr_equal(str1, oc_string(*str2), oc_string_len(*str2)); + return oc_string_view_is_equal(oc_string_view2(str1), oc_string_view2(str2)); } bool oc_string_is_cstr_equal(const oc_string_t *str1, const char *str2, size_t str2_len) { - return oc_string_len(*str1) == str2_len && - memcmp(oc_string(*str1), str2, str2_len) == 0; + return oc_string_view_is_equal(oc_string_view2(str1), + oc_string_view(str2, str2_len)); } void diff --git a/api/oc_helpers_internal.h b/api/oc_helpers_internal.h index d8544425a0..5b907c216f 100644 --- a/api/oc_helpers_internal.h +++ b/api/oc_helpers_internal.h @@ -29,6 +29,40 @@ extern "C" { #endif +/** + * @brief Non-mutable view into a C-string or an oc_string_t. + * + * @note It is the programmer's responsibility to ensure that oc_string_view_t + * does not outlive the pointed-to string. + * This is especially important with dynamic allocation disabled. Because then + * oc_string_t values are taken from a preallocated pool and when an oc_string_t + * is "deallocated" then data is returned to the pool and oc_string_t values + * allocated after this value are reallocated so that the pool is always + * contiguous. This means that the data pointer might become invalid after a + * call to oc_free_string. + */ +typedef struct oc_string_view_t +{ + const char *data; + size_t length; +} oc_string_view_t; + +/** Create an oc_string_view_t from a C-string. */ +oc_string_view_t oc_string_view(const char *data, size_t length); + +/** Create an oc_string_view_t from an oc_string_t. */ +oc_string_view_t oc_string_view2(const oc_string_t *str); + +/** + * @brief Compare two oc_string_view_t values. + * + * @param str1 first oc_string_view_t + * @param str2 second oc_string_view_t + * @return true strings are equal + * @return false strings are not equal + */ +bool oc_string_view_is_equal(oc_string_view_t str1, oc_string_view_t str2); + /** * @brief Compare two oc_strings. * diff --git a/api/oc_main.c b/api/oc_main.c index c8908b5f3e..d75171a16f 100644 --- a/api/oc_main.c +++ b/api/oc_main.c @@ -48,6 +48,7 @@ #include "security/oc_tls_internal.h" #ifdef OC_PKI #include "security/oc_keypair_internal.h" +#include "security/oc_roles_internal.h" #endif /* OC_PKI */ #include "security/oc_sdi_internal.h" #endif /* OC_SECURITY */ @@ -446,6 +447,9 @@ oc_main_shutdown(void) oc_sec_svr_free(); #ifdef OC_PKI +#ifdef OC_CLIENT + oc_sec_role_creds_free(); +#endif /* OC_CLIENT */ oc_sec_ecdsa_free_keypairs(); #endif /* OC_PKI */ #endif /* OC_SECURITY */ diff --git a/api/oc_ri.c b/api/oc_ri.c index 7ac882910f..a01308e0dd 100644 --- a/api/oc_ri.c +++ b/api/oc_ri.c @@ -848,9 +848,9 @@ oc_ri_audit_log(oc_method_t method, const oc_resource_t *resource, snprintf(aux[idx++], LINE_WIDTH, "device is in %s", state_str_val[state]); snprintf(aux[idx++], LINE_WIDTH, "No roles asserted"); #ifdef OC_PKI - if (peer) { + if (peer != NULL) { size_t pos = 0; - for (oc_sec_cred_t *rc = oc_sec_get_roles(peer); rc && pos < LINE_WIDTH; + for (oc_sec_cred_t *rc = oc_sec_roles_get(peer); rc && pos < LINE_WIDTH; rc = rc->next) { pos += snprintf(aux[idx - 1] + pos, LINE_WIDTH - pos - 1, "%s ", oc_string(rc->role.role)); diff --git a/security/oc_acl.c b/security/oc_acl.c index bb29dfb8e4..81841abd46 100644 --- a/security/oc_acl.c +++ b/security/oc_acl.c @@ -231,10 +231,9 @@ oc_ace_get_permission(const oc_sec_ace_t *ace, const oc_resource_t *resource, oc_ace_wildcard_t wc = 0; if (!is_DCR) { if (resource->properties & OC_DISCOVERABLE) { + wc = OC_ACE_WC_ALL_SECURED; if (is_public) { - wc = OC_ACE_WC_ALL_PUBLIC | OC_ACE_WC_ALL_SECURED; - } else { - wc = OC_ACE_WC_ALL_SECURED; + wc |= OC_ACE_WC_ALL_PUBLIC; } } else { wc = OC_ACE_WC_ALL; @@ -556,8 +555,8 @@ oc_sec_check_acl(oc_method_t method, const oc_resource_t *resource, } if ((pstat->s == OC_DOS_RFPRO || pstat->s == OC_DOS_RFNOP || pstat->s == OC_DOS_SRESET) && - oc_string_len(resource->uri) == 14 && - memcmp(oc_string(resource->uri), "/oic/sec/roles", 14) == 0) { + oc_string_is_cstr_equal(&resource->uri, OCF_SEC_ROLES_URI, + OC_CHAR_ARRAY_LEN(OCF_SEC_ROLES_URI))) { OC_DBG("oc_acl: peer has implicit access to /oic/sec/roles in RFPRO, " "RFNOP, SRESET"); return true; @@ -600,7 +599,8 @@ oc_sec_check_acl(oc_method_t method, const oc_resource_t *resource, } #ifdef OC_PKI else { - const oc_sec_cred_t *role_cred = peer ? oc_sec_get_roles(peer) : NULL; + const oc_sec_cred_t *role_cred = + peer != NULL ? oc_sec_roles_get(peer) : NULL; while (role_cred) { const oc_sec_cred_t *next = role_cred->next; uint32_t flags = 0; diff --git a/security/oc_cred.c b/security/oc_cred.c index f4aafe8bac..b7d9266c57 100644 --- a/security/oc_cred.c +++ b/security/oc_cred.c @@ -18,6 +18,7 @@ #ifdef OC_SECURITY +#include "api/oc_core_res_internal.h" #include "oc_cred_internal.h" #include "oc_api.h" #include "oc_base64.h" @@ -36,7 +37,6 @@ #include "util/oc_list.h" #include "util/oc_macros_internal.h" #include "util/oc_memb.h" -#include #ifdef OC_OSCORE #include "oc_oscore_context.h" @@ -47,6 +47,9 @@ #include #endif /* OC_PKI */ +#include +#include + OC_MEMB(g_creds, oc_sec_cred_t, OC_MAX_NUM_DEVICES *OC_MAX_NUM_SUBJECTS + 1); #define OXM_JUST_WORKS "oic.sec.doxm.jw" #define OXM_RANDOM_DEVICE_PIN "oic.sec.doxm.rdp" @@ -126,7 +129,7 @@ oc_sec_is_existing_cred(int credid, bool roles_resource, } #ifdef OC_PKI else { - cred = oc_sec_get_roles(client); + cred = oc_sec_roles_get(client); } #endif /* OC_PKI */ while (cred != NULL) { @@ -242,8 +245,8 @@ oc_sec_free_cred(oc_sec_cred_t *cred) { if (oc_string_len(cred->role.role) > 0) { #if defined(OC_PKI) && defined(OC_CLIENT) - oc_sec_remove_role_cred(oc_string(cred->role.role), - oc_string(cred->role.authority)); + oc_sec_role_cred_remove(oc_string_view2(&cred->role.role), + oc_string_view2(&cred->role.authority)); #endif /* OC_PKI && OC_CLIENT */ } oc_free_string(&cred->role.role); @@ -375,8 +378,6 @@ oc_sec_cred_t * oc_sec_allocate_cred(const oc_uuid_t *subjectuuid, oc_sec_credtype_t credtype, oc_sec_credusage_t credusage, size_t device) { - (void)credusage; - oc_sec_cred_t *cred = oc_memb_alloc(&g_creds); if (cred == NULL) { OC_WRN("insufficient memory to add new credential"); @@ -385,6 +386,8 @@ oc_sec_allocate_cred(const oc_uuid_t *subjectuuid, oc_sec_credtype_t credtype, cred->credtype = credtype; #ifdef OC_PKI cred->credusage = credusage; +#else /* OC_PKI */ + (void)credusage; #endif /* OC_PKI */ memcpy(cred->subjectuuid.id, subjectuuid->id, OC_UUID_ID_SIZE); oc_list_add(devices[device].creds, cred); @@ -470,13 +473,13 @@ oc_sec_is_equal_cred_data(oc_cred_data_t creddata, const uint8_t *data, } static bool -oc_sec_is_equal_cred_tag(const oc_string_t *credtag, const char *tag) +oc_sec_is_equal_cred_tag(const oc_string_t *credtag, oc_string_view_t tag) { size_t credtag_size = credtag->size; - size_t tag_size = tag != NULL ? strlen(tag) + 1 : 0; + size_t tag_size = tag.data != NULL ? tag.length + 1 : 0; return (credtag_size == tag_size) && - ((tag == NULL) || - (memcmp(oc_string(*credtag), tag, credtag_size) == 0)); + ((tag.data == NULL) || + (memcmp(oc_string(*credtag), tag.data, credtag_size) == 0)); } static bool @@ -504,7 +507,7 @@ oc_sec_is_duplicate_cred(const oc_sec_cred_t *cred, oc_sec_credtype_t credtype, oc_sec_credusage_t credusage, oc_uuid_t subject, size_t privatedata_size, const uint8_t *privatedata, size_t publicdata_size, const uint8_t *publicdata, - const char *tag) + oc_string_view_t tag) { if ((cred->credtype != credtype) || !oc_uuid_is_equal(cred->subjectuuid, subject) || @@ -556,28 +559,184 @@ oc_sec_get_valid_ecdsa_keypair(size_t device, size_t public_key_len, } #endif /* OC_PKI */ +static oc_sec_cred_t * +cred_allocate(size_t device, bool roles_resource, const oc_tls_peer_t *client, + oc_sec_credtype_t credtype, oc_sec_credusage_t credusage, + const oc_uuid_t *subject) +{ +#ifdef OC_PKI + if (roles_resource) { + if (credusage == OC_CREDUSAGE_ROLE_CERT) { + return oc_sec_roles_add(client, device); + } + return NULL; + } +#else /* !OC_PKI */ + (void)roles_resource; + (void)client; +#endif /* OC_PKI */ + return oc_sec_allocate_cred(subject, credtype, credusage, device); +} + +static void +cred_deallocate(oc_sec_cred_t *cred, size_t device, bool roles_resource, + const oc_tls_peer_t *client) +{ +#ifdef OC_PKI + if (roles_resource) { + oc_sec_free_role(cred, client); + return; + } +#else /* !OC_PKI */ + (void)roles_resource; + (void)client; +#endif /* OC_PKI */ + oc_sec_remove_cred(cred, device); +} + +static int +cred_set_privatedata(oc_sec_cred_t *cred, const uint8_t *data, size_t data_size, + oc_sec_encoding_t encoding) +{ + if (cred->credtype == OC_CREDTYPE_PSK) { + if (encoding == OC_ENCODING_BASE64) { + if (data_size > 64) { + return -1; + } + uint8_t key[64]; + memcpy(key, data, data_size); + int key_size = oc_base64_decode(key, data_size); + if (key_size < 0 || !check_symmetric_key_length((size_t)key_size)) { + return -1; + } + oc_new_string(&cred->privatedata.data, (const char *)key, key_size); + cred->privatedata.encoding = OC_ENCODING_RAW; + return 0; + } + if (!check_symmetric_key_length(data_size)) { + return -1; + } + } + oc_new_string(&cred->privatedata.data, (const char *)data, data_size); + cred->privatedata.encoding = encoding; + return 0; +} + +typedef struct cred_create_t +{ + const oc_uuid_t *subject; + const oc_tls_peer_t *client; + size_t device; + int credid; + oc_sec_credtype_t credtype; + oc_sec_credusage_t credusage; + bool roles_resource; + oc_sec_encoded_data_t privatedata; + oc_string_view_t role; + oc_string_view_t authority; + oc_string_view_t tag; +#ifdef OC_PKI + oc_sec_encoded_data_t publicdata; + oc_ecdsa_keypair_t *keypair; +#endif /* OC_PKI */ +} cred_create_t; + +static oc_sec_cred_t * +cred_create(cred_create_t create) +{ + oc_sec_cred_t *cred = + cred_allocate(create.device, create.roles_resource, create.client, + create.credtype, create.credusage, create.subject); + if (cred == NULL) { + return NULL; + } + +#ifdef OC_PKI + if (create.credusage == OC_CREDUSAGE_ROLE_CERT) { + if (oc_certs_parse_role_certificate(create.publicdata.data, + create.publicdata.size + 1, cred, + create.roles_resource) < 0) { + cred_deallocate(cred, create.device, create.roles_resource, + create.client); + return NULL; + } + + if (create.roles_resource && check_role_assertion(cred) < 0) { + oc_sec_free_role(cred, create.client); + return NULL; + } + } +#endif /* OC_PKI */ + + /* if a credid wasn't provided in the request, pick a suitable one */ + if (create.credid == -1) { + create.credid = + get_new_credid(create.roles_resource, create.client, create.device); + } + /* credid */ + cred->credid = create.credid; + /* credtype */ + cred->credtype = create.credtype; + /* privatedata */ + if (create.privatedata.data != NULL && create.privatedata.size > 0) { + if (cred_set_privatedata(cred, create.privatedata.data, + create.privatedata.size, + create.privatedata.encoding) != 0) { + cred_deallocate(cred, create.device, create.roles_resource, + create.client); + } + } else { +#ifdef OC_PKI + if (create.keypair != NULL) { + oc_new_string(&cred->privatedata.data, + (const char *)create.keypair->private_key, + create.keypair->private_key_size); + cred->privatedata.encoding = OC_ENCODING_RAW; + } +#endif /* OC_PKI */ + } + + /* roleid */ + if (!create.roles_resource && create.role.data != NULL) { + oc_new_string(&cred->role.role, create.role.data, create.role.length); + if (create.authority.data != NULL) { + oc_new_string(&cred->role.authority, create.authority.data, + create.authority.length); + } + } + +#ifdef OC_PKI + /* publicdata */ + if (create.publicdata.data != NULL && create.publicdata.size > 0) { + cred->publicdata.encoding = create.publicdata.encoding; + oc_new_string(&cred->publicdata.data, (const char *)create.publicdata.data, + create.publicdata.size); + } +#endif /* OC_PKI */ + + /* tag */ + if (create.tag.data != NULL) { + oc_new_string(&cred->tag, create.tag.data, create.tag.length); + } + return cred; +} + int oc_sec_add_new_cred(size_t device, bool roles_resource, const oc_tls_peer_t *client, int credid, oc_sec_credtype_t credtype, oc_sec_credusage_t credusage, - const char *subjectuuid, - oc_sec_encoding_t privatedata_encoding, - size_t privatedata_size, const uint8_t *privatedata, - oc_sec_encoding_t publicdata_encoding, - size_t publicdata_size, const uint8_t *publicdata, - const char *role, const char *authority, const char *tag, + const char *subjectuuid, oc_sec_encoded_data_t privatedata, + oc_sec_encoded_data_t publicdata, oc_string_view_t role, + oc_string_view_t authority, oc_string_view_t tag, oc_sec_add_new_cred_data_t *new_cred_data) { - (void)publicdata_encoding; - (void)publicdata; - (void)publicdata_size; #ifdef OC_PKI oc_string_t public_key; memset(&public_key, 0, sizeof(oc_string_t)); size_t public_key_len = 0; if (credtype == OC_CREDTYPE_CERT) { int pk_len = oc_certs_parse_public_key_to_oc_string( - publicdata, publicdata_size + 1, &public_key); + publicdata.data, publicdata.size + 1, &public_key); if (pk_len < 0) { goto add_new_cred_error; } @@ -587,7 +746,7 @@ oc_sec_add_new_cred(size_t device, bool roles_resource, if (roles_resource && (credtype != OC_CREDTYPE_CERT || !oc_sec_verify_role_cred(client, credusage, public_key_len, &public_key, - publicdata_size, publicdata))) { + publicdata.size, publicdata.data))) { goto add_new_cred_error; } #endif /* OC_PKI */ @@ -598,9 +757,9 @@ oc_sec_add_new_cred(size_t device, bool roles_resource, #ifdef OC_PKI oc_ecdsa_keypair_t *kp = NULL; - if (credusage == OC_CREDUSAGE_IDENTITY_CERT && privatedata_size == 0) { + if (credusage == OC_CREDUSAGE_IDENTITY_CERT && privatedata.size == 0) { kp = oc_sec_get_valid_ecdsa_keypair(device, public_key_len, &public_key, - publicdata_size, publicdata); + publicdata.size, publicdata.data); if (!kp) { goto add_new_cred_error; } @@ -613,8 +772,8 @@ oc_sec_add_new_cred(size_t device, bool roles_resource, if (!roles_resource) { /* skip duplicate cred, if one exists. */ if (oc_sec_is_duplicate_cred(existing, credtype, credusage, subject, - privatedata_size, privatedata, - publicdata_size, publicdata, tag)) { + privatedata.size, privatedata.data, + publicdata.size, publicdata.data, tag)) { #ifdef OC_PKI oc_free_string(&public_key); #endif /* OC_PKI */ @@ -635,8 +794,8 @@ oc_sec_add_new_cred(size_t device, bool roles_resource, #endif /* OC_PKI */ } - oc_sec_cred_t *cred = NULL; if (!roles_resource) { + oc_sec_cred_t *cred = NULL; do { cred = oc_sec_find_creds_for_subject(cred, &subject, device); @@ -656,9 +815,9 @@ oc_sec_add_new_cred(size_t device, bool roles_resource, #ifdef OC_PKI if (credtype == OC_CREDTYPE_CERT && cred->credusage == credusage) { /* Trying to add a duplicate certificate chain, so ignore */ - if (publicdata_size > 0 && - oc_sec_is_equal_cred_data(cred->publicdata, publicdata, - publicdata_size) && + if (publicdata.size > 0 && + oc_sec_is_equal_cred_data(cred->publicdata, publicdata.data, + publicdata.size) && oc_sec_is_equal_cred_tag(&cred->tag, tag)) { oc_free_string(&public_key); return cred->credid; @@ -672,11 +831,11 @@ oc_sec_add_new_cred(size_t device, bool roles_resource, } #ifdef OC_PKI else { - oc_sec_cred_t *roles = oc_sec_get_roles(client); + oc_sec_cred_t *roles = oc_sec_roles_get(client); while (roles) { /* Trying to add a duplicate role credential, so ignore */ - if (oc_sec_is_equal_cred_data(roles->publicdata, publicdata, - publicdata_size) && + if (oc_sec_is_equal_cred_data(roles->publicdata, publicdata.data, + publicdata.size) && oc_sec_is_equal_cred_tag(&roles->tag, tag)) { oc_free_string(&public_key); return roles->credid; @@ -686,112 +845,31 @@ oc_sec_add_new_cred(size_t device, bool roles_resource, } #endif /* OC_PKI */ - cred = NULL; + cred_create_t create = { + .subject = &subject, + .client = client, + .device = device, + .credid = credid, + .credtype = credtype, + .credusage = credusage, + .roles_resource = roles_resource, + .privatedata = privatedata, + .role = role, + .authority = authority, #ifdef OC_PKI - if (roles_resource && credusage == OC_CREDUSAGE_ROLE_CERT) { - cred = oc_sec_allocate_role(client, device); - } else if (!roles_resource) + .publicdata = publicdata, + .keypair = kp, #endif /* OC_PKI */ - { - cred = oc_sec_allocate_cred(&subject, credtype, credusage, device); - } - if (!cred) { + }; + oc_sec_cred_t *cred = cred_create(create); + if (cred == NULL) { goto add_new_cred_error; } -#ifdef OC_PKI - if (credusage == OC_CREDUSAGE_ROLE_CERT) { - if (oc_certs_parse_role_certificate(publicdata, publicdata_size + 1, cred, - roles_resource) < 0) { - if (roles_resource) { - oc_sec_free_role(cred, client); - } else { - oc_sec_remove_cred(cred, device); - } - goto add_new_cred_error; - } - - if (roles_resource && check_role_assertion(cred) < 0) { - oc_sec_free_role(cred, client); - goto add_new_cred_error; - } - } -#endif /* OC_PKI */ - - /* if a credid wasn't provided in the request, pick a suitable one */ - if (credid == -1) { - credid = get_new_credid(roles_resource, client, device); - } if (new_cred_data) { new_cred_data->created = true; } - /* credid */ - cred->credid = credid; - /* credtype */ - cred->credtype = credtype; - - /* privatedata */ - if (privatedata && privatedata_size > 0) { - if (credtype == OC_CREDTYPE_PSK && - privatedata_encoding == OC_ENCODING_BASE64) { - if (privatedata_size > 64) { - oc_sec_remove_cred(cred, device); - goto add_new_cred_error; - } - uint8_t key[64]; - memcpy(key, privatedata, privatedata_size); - int key_size = oc_base64_decode(key, privatedata_size); - if (key_size < 0 || !check_symmetric_key_length((size_t)key_size)) { - oc_sec_remove_cred(cred, device); - goto add_new_cred_error; - } - oc_new_string(&cred->privatedata.data, (const char *)key, key_size); - privatedata_encoding = OC_ENCODING_RAW; - } else { - if (credtype == OC_CREDTYPE_PSK && - !check_symmetric_key_length(privatedata_size)) { - oc_sec_remove_cred(cred, device); - goto add_new_cred_error; - } - oc_new_string(&cred->privatedata.data, (const char *)privatedata, - privatedata_size); - } - cred->privatedata.encoding = privatedata_encoding; - } -#ifdef OC_PKI - else if (kp) { - oc_new_string(&cred->privatedata.data, (const char *)kp->private_key, - kp->private_key_size); - cred->privatedata.encoding = OC_ENCODING_RAW; - } -#endif /* OC_PKI */ - - /* roleid */ - if (!roles_resource && role) { - oc_new_string(&cred->role.role, role, strlen(role)); - if (authority) { - oc_new_string(&cred->role.authority, authority, strlen(authority)); - } - } - -#ifdef OC_PKI - /* publicdata */ - if (publicdata && publicdata_size > 0) { - cred->publicdata.encoding = publicdata_encoding; - oc_new_string(&cred->publicdata.data, (const char *)publicdata, - publicdata_size); - } - - /* credusage */ - cred->credusage = credusage; -#endif /* OC_PKI */ - - /* tag */ - if (tag) { - oc_new_string(&cred->tag, tag, strlen(tag)); - } - #ifdef OC_PKI if (cred->credtype == OC_CREDTYPE_CERT) { if (cred->credusage == OC_CREDUSAGE_MFG_CERT || @@ -805,8 +883,8 @@ oc_sec_add_new_cred(size_t device, bool roles_resource, #if defined(OC_PKI) && defined(OC_CLIENT) if (!roles_resource && credusage == OC_CREDUSAGE_ROLE_CERT && oc_string_len(cred->role.role) > 0) { - oc_sec_add_role_cred(oc_string(cred->role.role), - oc_string(cred->role.authority)); + oc_sec_role_cred_add_or_get(oc_string_view2(&cred->role.role), + oc_string_view2(&cred->role.authority)); } #endif /* OC_PKI && OC_CLIENT */ } @@ -879,55 +957,6 @@ oc_cred_read_encoding(oc_sec_encoding_t encoding) return "Unknown"; } -#ifdef OC_PKI -static void -oc_sec_encode_roles(const oc_tls_peer_t *client, size_t device, - oc_interface_mask_t iface_mask) -{ - oc_sec_cred_t *cr = oc_sec_get_roles(client); - oc_rep_start_root_object(); - if (iface_mask & OC_IF_BASELINE) { - oc_process_baseline_interface( - oc_core_get_resource_by_index(OCF_SEC_ROLES, device)); - } - oc_rep_set_array(root, roles); - while (cr != NULL) { - oc_rep_object_array_start_item(roles); - /* credid */ - oc_rep_set_int(roles, credid, cr->credid); - /* credtype */ - oc_rep_set_int(roles, credtype, cr->credtype); - /* credusage */ - const char *credusage_string = oc_cred_read_credusage(cr->credusage); - if (strlen(credusage_string) > 4) { - oc_rep_set_text_string(roles, credusage, credusage_string); - } - /* publicdata */ - if (oc_string_len(cr->publicdata.data) > 0) { - oc_rep_set_object(roles, publicdata); - if (cr->publicdata.encoding == OC_ENCODING_PEM) { - oc_rep_set_text_string(publicdata, data, - oc_string(cr->publicdata.data)); - } else { - oc_rep_set_byte_string(publicdata, data, - oc_cast(cr->publicdata.data, const uint8_t), - oc_string_len(cr->publicdata.data)); - } - const char *encoding_string = - oc_cred_read_encoding(cr->publicdata.encoding); - if (strlen(encoding_string) > 7) { - oc_rep_set_text_string(publicdata, encoding, encoding_string); - } - oc_rep_close_object(roles, publicdata); - } - oc_rep_object_array_end_item(roles); - cr = cr->next; - } - oc_rep_close_array(root, roles); - oc_rep_end_root_object(); -} -#endif /* OC_PKI */ - void oc_sec_encode_cred(size_t device, oc_interface_mask_t iface_mask, bool to_storage) @@ -1325,8 +1354,8 @@ oc_sec_decode_cred(const oc_rep_t *rep, oc_sec_cred_t **owner, const oc_rep_t *cred = creds_array->value.object; int credid = -1; oc_sec_credtype_t credtype = 0; - const char *role = NULL; - const char *authority = NULL; + const oc_string_t *role = NULL; + const oc_string_t *authority = NULL; const char *subjectuuid = NULL; const char *privatedata = NULL; oc_sec_encoding_t privatedatatype = 0; @@ -1344,7 +1373,7 @@ oc_sec_decode_cred(const oc_rep_t *rep, oc_sec_cred_t **owner, uint64_t ssn = 0; #endif /* OC_OSCORE */ bool owner_cred = false; - const char *tag = NULL; + const oc_string_t *tag = NULL; bool non_empty = false; while (cred != NULL) { non_empty = true; @@ -1373,7 +1402,7 @@ oc_sec_decode_cred(const oc_rep_t *rep, oc_sec_cred_t **owner, #endif /* OC_PKI */ else if (len == 3 && memcmp(oc_string(cred->name), "tag", 3) == 0) { - tag = oc_string(cred->value.string); + tag = &cred->value.string; } break; /* publicdata, privatedata, roleid, oscore */ @@ -1442,10 +1471,10 @@ oc_sec_decode_cred(const oc_rep_t *rep, oc_sec_cred_t **owner, len = oc_string_len(data->name); if (len == 4 && memcmp(oc_string(data->name), "role", 4) == 0) { - role = oc_string(data->value.string); + role = &data->value.string; } else if (len == 9 && memcmp(oc_string(data->name), "authority", 9) == 0) { - authority = oc_string(data->value.string); + authority = &data->value.string; } data = data->next; } @@ -1528,6 +1557,16 @@ oc_sec_decode_cred(const oc_rep_t *rep, oc_sec_cred_t **owner, } #endif /* OC_OSCORE */ if (non_empty) { + oc_sec_encoded_data_t privdata = { (const uint8_t *)privatedata, + privatedata_size, + privatedatatype }; + oc_sec_encoded_data_t pubdata = { +#ifdef OC_PKI + (const uint8_t *)publicdata, publicdata_size, publicdatatype +#else /* !OC_PKI */ + /*data*/ NULL, /*size*/ 0, /*encoding*/ 0 +#endif /* OC_PKI */ + }; oc_sec_add_new_cred_data_t add_cred_data = { false, NULL }; credid = oc_sec_add_new_cred( device, roles_resource, client, credid, credtype, @@ -1536,14 +1575,8 @@ oc_sec_decode_cred(const oc_rep_t *rep, oc_sec_cred_t **owner, #else /* OC_PKI */ 0, #endif /* !OC_PKI */ - subjectuuid, privatedatatype, privatedata_size, - (const uint8_t *)privatedata, -#ifdef OC_PKI - publicdatatype, publicdata_size, (const uint8_t *)publicdata, -#else /* OC_PKI */ - 0, 0, NULL, -#endif /* !OC_PKI */ - role, authority, tag, &add_cred_data); + subjectuuid, privdata, pubdata, oc_string_view2(role), + oc_string_view2(authority), oc_string_view2(tag), &add_cred_data); if (credid == -1) { return false; @@ -1604,30 +1637,6 @@ oc_sec_decode_cred(const oc_rep_t *rep, oc_sec_cred_t **owner, return true; } -void -get_cred(oc_request_t *request, oc_interface_mask_t iface_mask, void *data) -{ - (void)data; - bool roles_resource = false; -#ifdef OC_PKI - const oc_tls_peer_t *client = NULL; - if (oc_string_len(request->resource->uri) == 14 && - memcmp(oc_string(request->resource->uri), "/oic/sec/roles", 14) == 0) { - roles_resource = true; - } -#endif /* OC_PKI */ - if (!roles_resource) { - oc_sec_encode_cred(request->resource->device, iface_mask, false); - } -#ifdef OC_PKI - else { - client = oc_tls_get_peer(request->origin); - oc_sec_encode_roles(client, request->resource->device, iface_mask); - } -#endif /* OC_PKI */ - oc_send_response_with_callback(request, OC_STATUS_OK, true); -} - bool oc_cred_remove_subject(const char *subjectuuid, size_t device) { @@ -1650,72 +1659,6 @@ oc_cred_remove_subject(const char *subjectuuid, size_t device) return false; } -void -delete_cred(oc_request_t *request, oc_interface_mask_t iface_mask, void *data) -{ - (void)iface_mask; - (void)data; - - bool roles_resource = false; - -#ifdef OC_PKI - const oc_tls_peer_t *client = NULL; - if (oc_string_len(request->resource->uri) == 14 && - memcmp(oc_string(request->resource->uri), "/oic/sec/roles", 14) == 0) { - client = oc_tls_get_peer(request->origin); - roles_resource = true; - } -#endif /* OC_PKI */ - - if (!roles_resource) { - const oc_sec_pstat_t *ps = oc_sec_get_pstat(request->resource->device); - if (ps->s == OC_DOS_RFNOP) { - OC_ERR("oc_cred: Cannot DELETE ACE in RFNOP"); - oc_send_response_with_callback(request, OC_STATUS_FORBIDDEN, true); - return; - } - } - - bool success = false; - const char *query_param = 0; - int ret = oc_get_query_value(request, "credid", &query_param); - int credid = 0; - if (ret != -1) { - credid = (int)strtoul(query_param, NULL, 10); - if (credid >= 0) { - if (!roles_resource) { - if (oc_sec_remove_cred_by_credid(credid, request->resource->device)) { - success = true; - } - } -#ifdef OC_PKI - else { - if (oc_sec_free_role_by_credid(credid, client) >= 0) { - success = true; - } - } -#endif /* OC_PKI */ - } - } else { - if (!roles_resource) { - oc_sec_cred_clear(request->resource->device, NULL, NULL); - } -#ifdef OC_PKI - else { - oc_sec_free_roles(client); - } -#endif /* OC_PKI */ - success = true; - } - - if (success) { - oc_send_response_with_callback(request, OC_STATUS_DELETED, true); - oc_sec_dump_cred(request->resource->device); - } else { - oc_send_response_with_callback(request, OC_STATUS_NOT_FOUND, true); - } -} - int oc_sec_apply_cred(const oc_rep_t *rep, const oc_resource_t *resource, const oc_endpoint_t *endpoint, @@ -1726,10 +1669,8 @@ oc_sec_apply_cred(const oc_rep_t *rep, const oc_resource_t *resource, const oc_tls_peer_t *client = NULL; #ifdef OC_PKI -#define OIC_SEC_ROLES "/oic/sec/roles" - if (oc_string_len(resource->uri) == strlen(OIC_SEC_ROLES) && - memcmp(oc_string(resource->uri), OIC_SEC_ROLES, strlen(OIC_SEC_ROLES)) == - 0) { + if (oc_string_is_cstr_equal(&resource->uri, OCF_SEC_ROLES_URI, + OC_CHAR_ARRAY_LEN(OCF_SEC_ROLES_URI))) { roles_resource = true; client = oc_tls_get_peer(endpoint); } @@ -1787,23 +1728,74 @@ oc_sec_apply_cred(const oc_rep_t *rep, const oc_resource_t *resource, return 0; } -void -post_cred(oc_request_t *request, oc_interface_mask_t iface_mask, void *data) +static void +cred_resource_get(oc_request_t *request, oc_interface_mask_t iface_mask, + void *data) { - (void)iface_mask; (void)data; + oc_sec_encode_cred(request->resource->device, iface_mask, false); + oc_send_response_with_callback(request, OC_STATUS_OK, true); +} - bool success = oc_sec_apply_cred(request->request_payload, request->resource, - request->origin, - /*on_apply_cred_cb*/ NULL, - /*on_apply_cred_data*/ NULL) == 0; +static void +cred_resource_post(oc_request_t *request, oc_interface_mask_t iface_mask, + void *data) +{ + (void)iface_mask; + (void)data; - if (!success) { + if (oc_sec_apply_cred(request->request_payload, request->resource, + request->origin, + /*on_apply_cred_cb*/ NULL, + /*on_apply_cred_data*/ NULL) != 0) { oc_send_response_with_callback(request, OC_STATUS_BAD_REQUEST, true); + return; + } + oc_send_response_with_callback(request, OC_STATUS_CHANGED, true); + oc_sec_dump_cred(request->resource->device); +} + +static void +cred_resource_delete(oc_request_t *request, oc_interface_mask_t iface_mask, + void *data) +{ + (void)iface_mask; + (void)data; + + const oc_sec_pstat_t *ps = oc_sec_get_pstat(request->resource->device); + if (ps->s == OC_DOS_RFNOP) { + OC_ERR("oc_cred: Cannot DELETE ACE in RFNOP"); + oc_send_response_with_callback(request, OC_STATUS_FORBIDDEN, true); + return; + } + + const char *query_param = NULL; + int ret = oc_get_query_value(request, "credid", &query_param); + if (ret != -1) { + errno = 0; + long credid = + strtol(query_param, NULL, 10); // NOLINT(readability-magic-numbers) + if (errno != 0 || credid > INT32_MAX || credid < 0 || + !oc_sec_remove_cred_by_credid((int)credid, request->resource->device)) { + OC_ERR("oc_cred: cannot delete credential: invalid credid(%ld)", credid); + oc_send_response_with_callback(request, OC_STATUS_NOT_FOUND, true); + return; + } } else { - oc_send_response_with_callback(request, OC_STATUS_CHANGED, true); - oc_sec_dump_cred(request->resource->device); + oc_sec_cred_clear(request->resource->device, NULL, NULL); } + + oc_send_response_with_callback(request, OC_STATUS_DELETED, true); + oc_sec_dump_cred(request->resource->device); +} + +void +oc_sec_cred_create_resource(size_t device) +{ + oc_core_populate_resource( + OCF_SEC_CRED, device, "/oic/sec/cred", OC_IF_RW | OC_IF_BASELINE, OC_IF_RW, + OC_DISCOVERABLE | OC_SECURE, cred_resource_get, /*put*/ NULL, + cred_resource_post, cred_resource_delete, 1, "oic.r.cred"); } #endif /* OC_SECURITY */ diff --git a/security/oc_cred_internal.h b/security/oc_cred_internal.h index e4e07dd757..1a93ddfbba 100644 --- a/security/oc_cred_internal.h +++ b/security/oc_cred_internal.h @@ -16,12 +16,15 @@ * ****************************************************************************/ -#ifndef OC_CRED_H -#define OC_CRED_H +#ifndef OC_CRED_INTERNAL_H +#define OC_CRED_INTERNAL_H +#include "api/oc_helpers_internal.h" #include "oc_cred.h" #include "oc_ri.h" #include "oc_uuid.h" +#include "util/oc_compiler.h" + #include #include @@ -53,14 +56,21 @@ typedef struct #endif /* OC_PKI */ -int oc_sec_add_new_cred( - size_t device, bool roles_resource, const struct oc_tls_peer_t *client, - int credid, oc_sec_credtype_t credtype, oc_sec_credusage_t credusage, - const char *subject, oc_sec_encoding_t privatedata_encoding, - size_t privatedata_size, const uint8_t *privatedata, - oc_sec_encoding_t publicdata_encoding, size_t publicdata_size, - const uint8_t *publicdata, const char *role, const char *authority, - const char *tag, oc_sec_add_new_cred_data_t *new_cred_data); +typedef struct +{ + const uint8_t *data; + size_t size; + oc_sec_encoding_t encoding; +} oc_sec_encoded_data_t; + +int oc_sec_add_new_cred(size_t device, bool roles_resource, + const struct oc_tls_peer_t *client, int credid, + oc_sec_credtype_t credtype, + oc_sec_credusage_t credusage, const char *subject, + oc_sec_encoded_data_t privatedata, + oc_sec_encoded_data_t publicdata, oc_string_view_t role, + oc_string_view_t authority, oc_string_view_t tag, + oc_sec_add_new_cred_data_t *new_cred_data); void oc_sec_cred_default(size_t device); void oc_sec_cred_init(void); @@ -168,17 +178,15 @@ oc_sec_cred_t *oc_sec_find_cred(oc_sec_cred_t *start, oc_sec_cred_t *oc_sec_find_role_cred(oc_sec_cred_t *start, const char *role, const char *authority, const char *tag); -void put_cred(oc_request_t *request, oc_interface_mask_t iface_mask, - void *data); -void post_cred(oc_request_t *request, oc_interface_mask_t iface_mask, - void *data); -void get_cred(oc_request_t *request, oc_interface_mask_t iface_mask, - void *data); -void delete_cred(oc_request_t *request, oc_interface_mask_t iface_mask, - void *data); +/** + * @brief Create roles (/oic/sec/cred) resource for given device. + * + * @param device device index + */ +void oc_sec_cred_create_resource(size_t device); #ifdef __cplusplus } #endif -#endif /* OC_CRED_H */ +#endif /* OC_CRED_INTERNAL_H */ diff --git a/security/oc_obt.c b/security/oc_obt.c index 2700d3600c..68bbed8b0e 100644 --- a/security/oc_obt.c +++ b/security/oc_obt.c @@ -40,6 +40,7 @@ check oc_config.h and make sure OC_STORAGE is defined if OC_SECURITY is defined. #include "security/oc_csr_internal.h" #include "security/oc_keypair_internal.h" #include "security/oc_obt_internal.h" +#include "security/oc_roles_internal.h" #include "security/oc_security_internal.h" #include "security/oc_sdi_internal.h" #include "security/oc_tls_internal.h" @@ -1750,7 +1751,7 @@ device_cred(oc_client_response_t *data) oc_rep_set_array(aclist2, resources); oc_rep_object_array_start_item(resources); - oc_rep_set_text_string(resources, href, "/oic/sec/roles"); + oc_rep_set_text_string(resources, href, OCF_SEC_ROLES_URI); oc_rep_object_array_end_item(resources); oc_rep_close_array(aclist2, resources); diff --git a/security/oc_obt_certs.c b/security/oc_obt_certs.c index 3bfd55de07..33181ae4d1 100644 --- a/security/oc_obt_certs.c +++ b/security/oc_obt_certs.c @@ -198,10 +198,15 @@ oc_obt_generate_self_signed_root_cert( return -1; } + oc_sec_encoded_data_t privatedata = { NULL, 0, 0 }; + oc_sec_encoded_data_t publicdata = { + cert_pem, oc_strnlen((const char *)cert_pem, sizeof(cert_pem)), + OC_ENCODING_PEM + }; int ret = oc_sec_add_new_cred( - device, false, NULL, -1, OC_CREDTYPE_CERT, OC_CREDUSAGE_TRUSTCA, "*", 0, 0, - NULL, OC_ENCODING_PEM, oc_strnlen((const char *)cert_pem, sizeof(cert_pem)), - cert_pem, NULL, NULL, NULL, NULL); + device, false, NULL, -1, OC_CREDTYPE_CERT, OC_CREDUSAGE_TRUSTCA, "*", + privatedata, publicdata, oc_string_view2(NULL), oc_string_view2(NULL), + oc_string_view2(NULL), NULL); if (ret == -1) { OC_ERR("could not write root cert into /oic/sec/cred"); diff --git a/security/oc_obt_otm_cert.c b/security/oc_obt_otm_cert.c index e2dea565b5..268a0fa0c6 100644 --- a/security/oc_obt_otm_cert.c +++ b/security/oc_obt_otm_cert.c @@ -357,9 +357,12 @@ obt_cert_9(oc_client_response_t *data) goto err_obt_cert_9; } - int credid = oc_sec_add_new_cred(0, false, NULL, -1, OC_CREDTYPE_PSK, - OC_CREDUSAGE_NULL, suuid, OC_ENCODING_RAW, - 16, key, 0, 0, NULL, NULL, NULL, NULL, NULL); + oc_sec_encoded_data_t privatedata = { key, sizeof(key), OC_ENCODING_RAW }; + oc_sec_encoded_data_t publicdata = { NULL, 0, 0 }; + int credid = + oc_sec_add_new_cred(0, false, NULL, -1, OC_CREDTYPE_PSK, OC_CREDUSAGE_NULL, + suuid, privatedata, publicdata, oc_string_view2(NULL), + oc_string_view2(NULL), oc_string_view2(NULL), NULL); if (credid == -1) { goto err_obt_cert_9; diff --git a/security/oc_obt_otm_justworks.c b/security/oc_obt_otm_justworks.c index e165979d22..ed7b7a4868 100644 --- a/security/oc_obt_otm_justworks.c +++ b/security/oc_obt_otm_justworks.c @@ -356,9 +356,12 @@ obt_jw_9(oc_client_response_t *data) goto err_obt_jw_9; } - int credid = oc_sec_add_new_cred(0, false, NULL, -1, OC_CREDTYPE_PSK, - OC_CREDUSAGE_NULL, suuid, OC_ENCODING_RAW, - 16, key, 0, 0, NULL, NULL, NULL, NULL, NULL); + oc_sec_encoded_data_t privatedata = { key, sizeof(key), OC_ENCODING_RAW }; + oc_sec_encoded_data_t publicdata = { NULL, 0, 0 }; + int credid = + oc_sec_add_new_cred(0, false, NULL, -1, OC_CREDTYPE_PSK, OC_CREDUSAGE_NULL, + suuid, privatedata, publicdata, oc_string_view2(NULL), + oc_string_view2(NULL), oc_string_view2(NULL), NULL); if (credid == -1) { goto err_obt_jw_9; diff --git a/security/oc_obt_otm_randompin.c b/security/oc_obt_otm_randompin.c index d4ec67d902..02ba33d8a1 100644 --- a/security/oc_obt_otm_randompin.c +++ b/security/oc_obt_otm_randompin.c @@ -357,9 +357,12 @@ obt_rdp_7(oc_client_response_t *data) goto err_obt_rdp_7; } - int credid = oc_sec_add_new_cred(0, false, NULL, -1, OC_CREDTYPE_PSK, - OC_CREDUSAGE_NULL, suuid, OC_ENCODING_RAW, - 16, key, 0, 0, NULL, NULL, NULL, NULL, NULL); + oc_sec_encoded_data_t privatedata = { key, sizeof(key), OC_ENCODING_RAW }; + oc_sec_encoded_data_t publicdata = { NULL, 0, 0 }; + int credid = + oc_sec_add_new_cred(0, false, NULL, -1, OC_CREDTYPE_PSK, OC_CREDUSAGE_NULL, + suuid, privatedata, publicdata, oc_string_view2(NULL), + oc_string_view2(NULL), oc_string_view2(NULL), NULL); if (credid == -1) { goto err_obt_rdp_7; @@ -645,9 +648,12 @@ oc_obt_perform_random_pin_otm(const oc_uuid_t *uuid, const unsigned char *pin, * TLS layer. */ + oc_sec_encoded_data_t privatedata = { key, sizeof(key), OC_ENCODING_RAW }; + oc_sec_encoded_data_t publicdata = { NULL, 0, 0 }; int credid = oc_sec_add_new_cred( 0, false, NULL, -1, OC_CREDTYPE_PSK, OC_CREDUSAGE_NULL, subjectuuid, - OC_ENCODING_RAW, 16, key, 0, 0, NULL, NULL, NULL, NULL, NULL); + privatedata, publicdata, oc_string_view2(NULL), oc_string_view2(NULL), + oc_string_view2(NULL), NULL); if (credid == -1) { oc_obt_free_otm_ctx(o, -1, OC_OBT_OTM_RDP); diff --git a/security/oc_pki.c b/security/oc_pki.c index 8861c5c25b..8465ff9b4d 100644 --- a/security/oc_pki.c +++ b/security/oc_pki.c @@ -236,10 +236,13 @@ pki_add_identity_cert(size_t device, const unsigned char *cert, mbedtls_x509_crt_free(&cert1); + oc_sec_encoded_data_t privatedata = { privkbuf + (200 - private_key_size), + private_key_size, OC_ENCODING_RAW }; + oc_sec_encoded_data_t publicdata = { cert, c_size - 1, OC_ENCODING_PEM }; int credid = oc_sec_add_new_cred( device, false, NULL, -1, OC_CREDTYPE_CERT, credusage, subjectuuid, - OC_ENCODING_RAW, private_key_size, privkbuf + (200 - private_key_size), - OC_ENCODING_PEM, c_size - 1, cert, NULL, NULL, NULL, NULL); + privatedata, publicdata, oc_string_view2(NULL), oc_string_view2(NULL), + oc_string_view2(NULL), NULL); if (credid != -1) { OC_DBG("added new identity cert(credid=%d) chain to /oic/sec/cred", credid); @@ -336,9 +339,12 @@ pki_add_trust_anchor(size_t device, const unsigned char *cert, size_t cert_size, OC_DBG("adding a new trust anchor entry to /oic/sec/cred"); - ret = oc_sec_add_new_cred(device, false, NULL, -1, OC_CREDTYPE_CERT, - credusage, "*", 0, 0, NULL, OC_ENCODING_PEM, - c_size - 1, cert, NULL, NULL, NULL, NULL); + oc_sec_encoded_data_t privatedata = { NULL, 0, 0 }; + oc_sec_encoded_data_t publicdata = { cert, c_size - 1, OC_ENCODING_PEM }; + ret = + oc_sec_add_new_cred(device, false, NULL, -1, OC_CREDTYPE_CERT, credusage, + "*", privatedata, publicdata, oc_string_view2(NULL), + oc_string_view2(NULL), oc_string_view2(NULL), NULL); if (ret != -1) { OC_DBG("added new trust anchor entry to /oic/sec/cred"); oc_sec_dump_cred(device); diff --git a/security/oc_roles.c b/security/oc_roles.c index 8527dfb1c1..2429172519 100644 --- a/security/oc_roles.c +++ b/security/oc_roles.c @@ -16,81 +16,107 @@ * ****************************************************************************/ -#ifdef OC_SECURITY -#ifdef OC_PKI +#include "oc_config.h" -#include "oc_roles_internal.h" +#if defined(OC_SECURITY) && defined(OC_PKI) + +#include "api/oc_core_res_internal.h" +#include "api/oc_helpers_internal.h" #include "port/oc_log_internal.h" -#include "security/oc_tls_internal.h" +#include "oc_roles_internal.h" +#include "oc_tls_internal.h" #include -#define OC_ROLES_NUM_ROLE_CREDS (2) -#define OC_ROLES_NUM_ROLES (2) +#include +#include #ifdef OC_CLIENT -OC_MEMB(role_creds_s, oc_role_t, OC_ROLES_NUM_ROLE_CREDS); -OC_LIST(role_creds); -oc_role_t * -oc_sec_get_role_creds(void) -{ - return oc_list_head(role_creds); -} +OC_MEMB(g_role_cred_s, oc_role_t, OC_ROLES_NUM_ROLE_CREDS); +OC_LIST(g_role_creds); static oc_role_t * -allocate_role_cred(const char *role, const char *authority) +role_cred_allocate(oc_string_view_t role, oc_string_view_t authority) { - oc_role_t *role_cred = (oc_role_t *)oc_memb_alloc(&role_creds_s); - if (role) { - oc_new_string(&role_cred->role, role, strlen(role)); - oc_new_string(&role_cred->authority, authority, strlen(authority)); - oc_list_add(role_creds, role_cred); + if (role.data == NULL || authority.data == NULL) { + OC_ERR("invalid input"); + return NULL; } + oc_role_t *role_cred = (oc_role_t *)oc_memb_alloc(&g_role_cred_s); + if (role_cred == NULL) { + OC_ERR("failed to allocate role cred"); + return NULL; + } + oc_new_string(&role_cred->role, role.data, role.length); + oc_new_string(&role_cred->authority, authority.data, authority.length); + oc_list_add(g_role_creds, role_cred); return role_cred; } static oc_role_t * -find_role_cred(const char *role, const char *authority) -{ - oc_role_t *role_cred = (oc_role_t *)oc_list_head(role_creds); - size_t role_len = strlen(role); - size_t authority_len = (authority ? strlen(authority) : 0); - - while (role_cred) { - if ((oc_string_len(role_cred->role) == role_len) && - (memcmp(oc_string(role_cred->role), role, role_len) == 0)) { - if (authority && (oc_string_len(role_cred->authority) == authority_len) && - (memcmp(oc_string(role_cred->authority), authority, authority_len) == - 0)) { - return role_cred; - } +role_cred_find(oc_string_view_t role, oc_string_view_t authority) +{ + oc_role_t *role_cred = (oc_role_t *)oc_list_head(g_role_creds); + while (role_cred != NULL) { + if ((role.data != NULL && + oc_string_view_is_equal(oc_string_view2(&role_cred->role), role)) && + (authority.data != NULL && + oc_string_view_is_equal(oc_string_view2(&role_cred->authority), + authority))) { + return role_cred; } role_cred = role_cred->next; } + return NULL; +} +oc_role_t * +oc_sec_role_cred_add_or_get(oc_string_view_t role, oc_string_view_t authority) +{ + oc_role_t *role_cred = role_cred_find(role, authority); + if (!role_cred) { + role_cred = role_cred_allocate(role, authority); + } return role_cred; } -void -oc_sec_remove_role_cred(const char *role, const char *authority) +static void +role_cred_free(oc_role_t *role_cred) { - oc_role_t *role_cred = find_role_cred(role, authority); - if (role_cred) { - oc_list_remove(role_creds, role_cred); - oc_memb_free(&role_creds_s, role_cred); + oc_free_string(&role_cred->authority); + oc_free_string(&role_cred->role); + oc_memb_free(&g_role_cred_s, role_cred); +} + +bool +oc_sec_role_cred_remove(oc_string_view_t role, oc_string_view_t authority) +{ + oc_role_t *role_cred = role_cred_find(role, authority); + if (role_cred != NULL) { + oc_list_remove(g_role_creds, role_cred); + role_cred_free(role_cred); + return true; } + return false; } oc_role_t * -oc_sec_add_role_cred(const char *role, const char *authority) +oc_sec_role_creds_get(void) { - oc_role_t *role_cred = find_role_cred(role, authority); - if (!role_cred) { - role_cred = allocate_role_cred(role, authority); + return oc_list_head(g_role_creds); +} + +void +oc_sec_role_creds_free(void) +{ + oc_role_t *role_cred = (oc_role_t *)oc_list_pop(g_role_creds); + while (role_cred != NULL) { + role_cred_free(role_cred); + role_cred = (oc_role_t *)oc_list_pop(g_role_creds); } - return role_cred; } + #endif /* OC_CLIENT */ typedef struct oc_sec_roles_t @@ -101,65 +127,76 @@ typedef struct oc_sec_roles_t size_t device; } oc_sec_roles_t; -OC_MEMB(x509_crt_s, mbedtls_x509_crt, OC_ROLES_NUM_ROLES); -OC_MEMB(roles_s, oc_sec_cred_t, OC_ROLES_NUM_ROLES); -OC_MEMB(clients_s, oc_sec_roles_t, OC_MAX_NUM_DEVICES); -OC_LIST(clients); +OC_MEMB(g_x509_crt_s, mbedtls_x509_crt, OCF_SEC_ROLES_MAX_NUM); +OC_MEMB(g_roles_s, oc_sec_cred_t, OCF_SEC_ROLES_MAX_NUM); +OC_MEMB(g_clients_s, oc_sec_roles_t, OC_MAX_NUM_DEVICES); +OC_LIST(g_clients); static oc_sec_roles_t * -get_roles_for_client(const oc_tls_peer_t *client) +roles_get_for_client(const oc_tls_peer_t *client) { - oc_sec_roles_t *roles = (oc_sec_roles_t *)oc_list_head(clients); - while (roles) { + oc_sec_roles_t *roles = (oc_sec_roles_t *)oc_list_head(g_clients); + while (roles != NULL) { if (roles->client == client) { return roles; } roles = roles->next; } - return roles; + return NULL; } static oc_sec_roles_t * -allocate_roles_for_client(const oc_tls_peer_t *client, size_t device) +roles_add_for_client(const oc_tls_peer_t *client, size_t device) { - oc_sec_roles_t *roles = (oc_sec_roles_t *)oc_memb_alloc(&clients_s); - if (!roles) { + oc_sec_roles_t *roles = (oc_sec_roles_t *)oc_memb_alloc(&g_clients_s); + if (roles == NULL) { + OC_ERR("insufficient memory to allocate roles"); return NULL; } roles->device = device; roles->client = client; OC_LIST_STRUCT_INIT(roles, roles); - oc_list_add(clients, roles); + oc_list_add(g_clients, roles); return roles; } +static oc_sec_cred_t * +role_add(oc_sec_roles_t *roles) +{ + oc_sec_cred_t *role = (oc_sec_cred_t *)oc_memb_alloc(&g_roles_s); + if (role == NULL) { + OC_ERR("insufficient memory to allocate role"); + return NULL; + } + role->ctx = oc_memb_alloc(&g_x509_crt_s); + if (role->ctx == NULL) { + OC_ERR("insufficient memory to allocate role context"); + oc_memb_free(&g_roles_s, role); + return NULL; + } + mbedtls_x509_crt_init(role->ctx); + oc_list_add(roles->roles, role); + return role; +} + oc_sec_cred_t * -oc_sec_allocate_role(const oc_tls_peer_t *client, size_t device) +oc_sec_roles_add(const oc_tls_peer_t *client, size_t device) { - oc_sec_roles_t *roles = get_roles_for_client(client); - if (!roles) { - roles = allocate_roles_for_client(client, device); + oc_sec_roles_t *roles = roles_get_for_client(client); + if (roles == NULL) { + roles = roles_add_for_client(client, device); } - if (roles) { - oc_sec_cred_t *role = (oc_sec_cred_t *)oc_memb_alloc(&roles_s); - if (role) { - role->ctx = oc_memb_alloc(&x509_crt_s); - if (role->ctx) { - mbedtls_x509_crt_init(role->ctx); - oc_list_add(roles->roles, role); - return role; - } - oc_sec_free_role(role, client); - } + if (roles == NULL) { + return NULL; } - return NULL; + return role_add(roles); } oc_sec_cred_t * -oc_sec_get_roles(const oc_tls_peer_t *client) +oc_sec_roles_get(const oc_tls_peer_t *client) { - oc_sec_roles_t *roles = get_roles_for_client(client); - if (roles) { + oc_sec_roles_t *roles = roles_get_for_client(client); + if (roles != NULL) { return (oc_sec_cred_t *)oc_list_head(roles->roles); } return NULL; @@ -176,16 +213,16 @@ free_cred_properties(oc_sec_cred_t *cred) void oc_sec_free_role(const oc_sec_cred_t *role, const oc_tls_peer_t *client) { - oc_sec_roles_t *roles = get_roles_for_client(client); + oc_sec_roles_t *roles = roles_get_for_client(client); if (roles) { oc_sec_cred_t *r = (oc_sec_cred_t *)oc_list_head(roles->roles); while (r) { if (role == r) { oc_list_remove(roles->roles, r); mbedtls_x509_crt_free(r->ctx); - oc_memb_free(&x509_crt_s, r->ctx); + oc_memb_free(&g_x509_crt_s, r->ctx); free_cred_properties(r); - oc_memb_free(&roles_s, r); + oc_memb_free(&g_roles_s, r); return; } r = r->next; @@ -196,7 +233,7 @@ oc_sec_free_role(const oc_sec_cred_t *role, const oc_tls_peer_t *client) void oc_sec_free_roles_for_device(size_t device) { - oc_sec_roles_t *roles = (oc_sec_roles_t *)oc_list_head(clients), *next; + oc_sec_roles_t *roles = (oc_sec_roles_t *)oc_list_head(g_clients), *next; while (roles) { next = roles->next; if (roles->device == device) { @@ -209,34 +246,34 @@ oc_sec_free_roles_for_device(size_t device) void oc_sec_free_roles(const oc_tls_peer_t *client) { - oc_sec_roles_t *roles = get_roles_for_client(client); + oc_sec_roles_t *roles = roles_get_for_client(client); if (roles) { oc_sec_cred_t *r = (oc_sec_cred_t *)oc_list_pop(roles->roles); while (r) { mbedtls_x509_crt_free(r->ctx); - oc_memb_free(&x509_crt_s, r->ctx); + oc_memb_free(&g_x509_crt_s, r->ctx); free_cred_properties(r); - oc_memb_free(&roles_s, r); + oc_memb_free(&g_roles_s, r); r = (oc_sec_cred_t *)oc_list_pop(roles->roles); } - oc_list_remove(clients, roles); - oc_memb_free(&clients_s, roles); + oc_list_remove(g_clients, roles); + oc_memb_free(&g_clients_s, roles); } } int oc_sec_free_role_by_credid(int credid, const oc_tls_peer_t *client) { - oc_sec_roles_t *roles = get_roles_for_client(client); + oc_sec_roles_t *roles = roles_get_for_client(client); if (roles) { oc_sec_cred_t *r = (oc_sec_cred_t *)oc_list_head(roles->roles); while (r) { if (r->credid == credid) { oc_list_remove(roles->roles, r); mbedtls_x509_crt_free(r->ctx); - oc_memb_free(&x509_crt_s, r->ctx); + oc_memb_free(&g_x509_crt_s, r->ctx); free_cred_properties(r); - oc_memb_free(&roles_s, r); + oc_memb_free(&g_roles_s, r); return 0; } r = r->next; @@ -245,7 +282,113 @@ oc_sec_free_role_by_credid(int credid, const oc_tls_peer_t *client) return -1; } -#else /* OC_PKI */ -typedef int dummy_declaration; -#endif /* !OC_PKI */ -#endif /* OC_SECURITY */ +static void +oc_sec_encode_roles(const oc_tls_peer_t *client, size_t device, + oc_interface_mask_t iface_mask) +{ + oc_sec_cred_t *cr = oc_sec_roles_get(client); + oc_rep_start_root_object(); + if ((iface_mask & OC_IF_BASELINE) != 0) { + oc_process_baseline_interface( + oc_core_get_resource_by_index(OCF_SEC_ROLES, device)); + } + oc_rep_set_array(root, roles); + while (cr != NULL) { + oc_rep_object_array_start_item(roles); + /* credid */ + oc_rep_set_int(roles, credid, cr->credid); + /* credtype */ + oc_rep_set_int(roles, credtype, cr->credtype); + /* credusage */ + const char *credusage_string = oc_cred_read_credusage(cr->credusage); + if (strlen(credusage_string) > 4) { + oc_rep_set_text_string(roles, credusage, credusage_string); + } + /* publicdata */ + if (oc_string_len(cr->publicdata.data) > 0) { + oc_rep_set_object(roles, publicdata); + if (cr->publicdata.encoding == OC_ENCODING_PEM) { + oc_rep_set_text_string(publicdata, data, + oc_string(cr->publicdata.data)); + } else { + oc_rep_set_byte_string(publicdata, data, + oc_cast(cr->publicdata.data, const uint8_t), + oc_string_len(cr->publicdata.data)); + } + const char *encoding_string = + oc_cred_read_encoding(cr->publicdata.encoding); + if (strlen(encoding_string) > 7) { + oc_rep_set_text_string(publicdata, encoding, encoding_string); + } + oc_rep_close_object(roles, publicdata); + } + oc_rep_object_array_end_item(roles); + cr = cr->next; + } + oc_rep_close_array(root, roles); + oc_rep_end_root_object(); +} + +static void +roles_resource_get(oc_request_t *request, oc_interface_mask_t iface_mask, + void *data) +{ + (void)data; + const oc_tls_peer_t *client = oc_tls_get_peer(request->origin); + oc_sec_encode_roles(client, request->resource->device, iface_mask); + oc_send_response_with_callback(request, OC_STATUS_OK, true); +} + +static void +roles_resource_post(oc_request_t *request, oc_interface_mask_t iface_mask, + void *data) +{ + (void)iface_mask; + (void)data; + if (oc_sec_apply_cred(request->request_payload, request->resource, + request->origin, + /*on_apply_cred_cb*/ NULL, + /*on_apply_cred_data*/ NULL) != 0) { + oc_send_response_with_callback(request, OC_STATUS_BAD_REQUEST, true); + return; + } + oc_send_response_with_callback(request, OC_STATUS_CHANGED, true); +} + +static void +roles_resource_delete(oc_request_t *request, oc_interface_mask_t iface_mask, + void *data) +{ + (void)iface_mask; + (void)data; + const oc_tls_peer_t *client = oc_tls_get_peer(request->origin); + const char *query_param = NULL; + int ret = oc_get_query_value(request, "credid", &query_param); + if (ret != -1) { + errno = 0; + long credid = + strtol(query_param, NULL, 10); // NOLINT(readability-magic-numbers) + if (errno != 0 || credid > INT32_MAX || credid < 0 || + oc_sec_free_role_by_credid((int)credid, client) < 0) { + OC_ERR("cannot delete roles credential: invalid credid(%ld)", credid); + oc_send_response_with_callback(request, OC_STATUS_NOT_FOUND, true); + return; + } + } else { + oc_sec_free_roles(client); + } + oc_send_response_with_callback(request, OC_STATUS_DELETED, true); +} + +void +oc_sec_roles_create_resource(size_t device) +{ + oc_core_populate_resource(OCF_SEC_ROLES, device, OCF_SEC_ROLES_URI, + (oc_interface_mask_t)OCF_SEC_ROLES_IF_MASK, + (oc_interface_mask_t)OCF_SEC_ROLES_DEFAULT_IF, + OC_DISCOVERABLE | OC_SECURE, roles_resource_get, + /*put*/ NULL, roles_resource_post, + roles_resource_delete, 1, OCF_SEC_ROLES_RT); +} + +#endif /* OC_SECURITY && OC_PKI */ diff --git a/security/oc_roles_internal.h b/security/oc_roles_internal.h index 5d3c4d04ea..dad981969a 100644 --- a/security/oc_roles_internal.h +++ b/security/oc_roles_internal.h @@ -19,28 +19,101 @@ #ifndef OC_ROLES_INTERNAL_H #define OC_ROLES_INTERNAL_H +#include "api/oc_helpers_internal.h" #include "oc_role.h" #include "security/oc_cred_internal.h" #include "security/oc_tls_internal.h" +#include "util/oc_compiler.h" + +#include +#include #ifdef __cplusplus extern "C" { #endif -/* Used on the server-side for handling role assertions via /oic/sec/roles */ -oc_sec_cred_t *oc_sec_allocate_role(const oc_tls_peer_t *client, size_t device); +#define OCF_SEC_ROLES_URI "/oic/sec/roles" +#define OCF_SEC_ROLES_RT "oic.r.roles" + +enum { + OCF_SEC_ROLES_IF_MASK = OC_IF_BASELINE | OC_IF_RW, + OCF_SEC_ROLES_DEFAULT_IF = OC_IF_RW, + + OCF_SEC_ROLES_MAX_NUM = 2, +}; + +/** + * \defgroup server-roles Event timers + * + * Server-side API for handling role assertions via /oic/sec/roles + * + * @{ + */ + +/** Get head of the list of roles asserted by the client. */ +oc_sec_cred_t *oc_sec_roles_get(const oc_tls_peer_t *client) OC_NONNULL(); + +/** + * @brief Add a role to the list of roles asserted by the client. + * + * @param client client asserting the role (cannot be NULL) + * @param device device index + * + * @return newly allocated role on success + * @return NULL on failure + * + */ +oc_sec_cred_t *oc_sec_roles_add(const oc_tls_peer_t *client, size_t device) + OC_NONNULL(); + +/** + * @brief Create roles (/oic/sec/roles) resource for given device. + * + * @param device device index + */ +void oc_sec_roles_create_resource(size_t device); + void oc_sec_free_role(const oc_sec_cred_t *role, const oc_tls_peer_t *client); -oc_sec_cred_t *oc_sec_get_roles(const oc_tls_peer_t *client); void oc_sec_free_roles(const oc_tls_peer_t *client); void oc_sec_free_roles_for_device(size_t device); int oc_sec_free_role_by_credid(int credid, const oc_tls_peer_t *client); -/* Used on the client-side for asserting roles that had been provisioned to +/** @} */ + +#ifdef OC_CLIENT + +#define OC_ROLES_NUM_ROLE_CREDS (2) + +/** + * \defgroup client-roles Event timers + * + * Client-side API for asserting roles that had been provisioned to * /oic/sec/cred. + * + * @{ */ -void oc_sec_remove_role_cred(const char *role, const char *authority); -oc_role_t *oc_sec_add_role_cred(const char *role, const char *authority); -oc_role_t *oc_sec_get_role_creds(void); + +/** @brief Add a role (if it doesn't exist) to the list of roles asserted by the + * client. */ +oc_role_t *oc_sec_role_cred_add_or_get(oc_string_view_t role, + oc_string_view_t authority); + +/** @brief Remove a role from the list of roles asserted by the client. */ +bool oc_sec_role_cred_remove(oc_string_view_t role, oc_string_view_t authority); + +/** @brief Get the list of roles asserted by the client. */ +oc_role_t *oc_sec_role_creds_get(void); + +/** @brief Free the list of roles asserted by the client. */ +void oc_sec_role_creds_free(void); + +// TODO: if a cred is removed, the list of roles asserted by the client should +// be refreshed and if no longer asserted, the role should be removed from the +// list of roles asserted by the client. + +/** @} */ + +#endif /* OC_CLIENT */ #ifdef __cplusplus } diff --git a/security/oc_svr.c b/security/oc_svr.c index 60789adf0c..bf2550f03c 100644 --- a/security/oc_svr.c +++ b/security/oc_svr.c @@ -18,7 +18,6 @@ #ifdef OC_SECURITY -#include "oc_svr_internal.h" #include "api/oc_core_res_internal.h" #include "oc_acl_internal.h" #include "oc_ael.h" @@ -30,8 +29,10 @@ #include "oc_doxm_internal.h" #include "oc_pstat.h" #include "oc_ri.h" +#include "oc_roles_internal.h" #include "oc_sdi_internal.h" #include "oc_sp_internal.h" +#include "oc_svr_internal.h" #include "port/oc_log_internal.h" void @@ -57,10 +58,7 @@ oc_sec_svr_create(void) OC_IF_RW | OC_IF_BASELINE, OC_IF_RW, OC_DISCOVERABLE | OC_SECURE, get_acl, 0, post_acl, delete_acl, 1, "oic.r.acl2"); - oc_core_populate_resource(OCF_SEC_CRED, i, "/oic/sec/cred", - OC_IF_RW | OC_IF_BASELINE, OC_IF_RW, - OC_DISCOVERABLE | OC_SECURE, get_cred, 0, - post_cred, delete_cred, 1, "oic.r.cred"); + oc_sec_cred_create_resource(i); oc_core_populate_resource( OCF_SEC_AEL, i, "/oic/sec/ael", OC_IF_RW | OC_IF_BASELINE, OC_IF_RW, OC_DISCOVERABLE | OC_SECURE, get_ael, 0, post_ael, 0, 1, "oic.r.ael"); @@ -69,11 +67,7 @@ oc_sec_svr_create(void) oc_sec_sdi_create_resource(i); #ifdef OC_PKI oc_sec_csr_create_resource(i); - - oc_core_populate_resource(OCF_SEC_ROLES, i, "/oic/sec/roles", - OC_IF_RW | OC_IF_BASELINE, OC_IF_RW, - OC_DISCOVERABLE | OC_SECURE, get_cred, 0, - post_cred, delete_cred, 1, "oic.r.roles"); + oc_sec_roles_create_resource(i); #endif /* OC_PKI */ } } diff --git a/security/unittest/rolestest.cpp b/security/unittest/rolestest.cpp new file mode 100644 index 0000000000..00b2b3f65c --- /dev/null +++ b/security/unittest/rolestest.cpp @@ -0,0 +1,241 @@ +/**************************************************************************** + * + * Copyright (c) 2023 plgd.dev s.r.o. + * + * 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. + * + ****************************************************************************/ + +#if defined(OC_SECURITY) && defined(OC_PKI) + +#include "oc_core_res.h" +#include "security/oc_roles_internal.h" +#include "tests/gtest/Device.h" +#include "tests/gtest/Resource.h" + +#include + +static constexpr size_t kDeviceID{ 0 }; + +class TestRolesWithServer : public testing::Test { +public: + static void SetUpTestCase() + { + ASSERT_TRUE(oc::TestDevice::StartServer()); +#ifdef OC_HAS_FEATURE_RESOURCE_ACCESS_IN_RFOTM + ASSERT_TRUE( + oc::SetAccessInRFOTM(OCF_SEC_ROLES, kDeviceID, true, + OC_PERM_RETRIEVE | OC_PERM_UPDATE | OC_PERM_DELETE)); +#endif /* OC_HAS_FEATURE_RESOURCE_ACCESS_IN_RFOTM */ + } + + static void TearDownTestCase() + { + oc::TestDevice::StopServer(); + } +}; + +TEST_F(TestRolesWithServer, GetResourceByIndex) +{ + EXPECT_NE(nullptr, oc_core_get_resource_by_index(OCF_SEC_ROLES, kDeviceID)); +} + +TEST_F(TestRolesWithServer, GetResourceByURI) +{ + EXPECT_NE(nullptr, oc_core_get_resource_by_uri(OCF_SEC_ROLES_URI, kDeviceID)); +} + +#ifdef OC_HAS_FEATURE_RESOURCE_ACCESS_IN_RFOTM + +// TODO: add API to generate certificates +// TODO: GET /oic/sec/roles -> retrieve role certificates +// TODO: POST /oic/sec/roles -> add role certificate +// TODO: DELETE /oic/sec/roles -> remove role certificate + +#else /* !OC_HAS_FEATURE_RESOURCE_ACCESS_IN_RFOTM */ + +#endif /* OC_HAS_FEATURE_RESOURCE_ACCESS_IN_RFOTM */ + +TEST_F(TestRolesWithServer, PutRequest_FailMethodNotSupported) +{ + const oc_endpoint_t *ep = oc::TestDevice::GetEndpoint(kDeviceID, 0, SECURED); + ASSERT_NE(nullptr, ep); + +#ifdef OC_HAS_FEATURE_RESOURCE_ACCESS_IN_RFOTM + oc_status_t error_code = OC_STATUS_METHOD_NOT_ALLOWED; +#else /* !OC_HAS_FEATURE_RESOURCE_ACCESS_IN_RFOTM */ + oc_status_t error_code = OC_STATUS_UNAUTHORIZED; +#endif /* OC_HAS_FEATURE_RESOURCE_ACCESS_IN_RFOTM */ + oc::testNotSupportedMethod(OC_PUT, ep, OCF_SEC_ROLES_URI, nullptr, + error_code); +} + +#ifdef OC_CLIENT + +class TestRoleCreds : public testing::Test { +public: + void TearDown() override { oc_sec_role_creds_free(); } + + static size_t countRoleCreds() + { + size_t count = 0; + const oc_role_t *role = oc_sec_role_creds_get(); + while (role != NULL) { + ++count; + role = role->next; + } + return count; + } +}; + +TEST_F(TestRoleCreds, Add) +{ + ASSERT_EQ(0, countRoleCreds()); + + std::string role = "role"; + std::string authority = "authority"; + oc_role_t *role_cred = oc_sec_role_cred_add_or_get( + { role.c_str(), role.length() }, { authority.c_str(), authority.length() }); + EXPECT_NE(nullptr, role_cred); + EXPECT_EQ(1, countRoleCreds()); + + // adding the same role-authority pair should return the same role_cred + oc_role_t *role_cred2 = oc_sec_role_cred_add_or_get( + { role.c_str(), role.length() }, { authority.c_str(), authority.length() }); + EXPECT_NE(nullptr, role_cred2); + EXPECT_EQ(1, countRoleCreds()); + EXPECT_EQ(role_cred, role_cred2); + +#ifdef OC_DYNAMIC_ALLOCATION + // different role + std::string role2 = "role2"; + oc_role_t *role_cred3 = + oc_sec_role_cred_add_or_get({ role2.c_str(), role2.length() }, + { authority.c_str(), authority.length() }); + EXPECT_NE(nullptr, role_cred3); + EXPECT_EQ(2, countRoleCreds()); + EXPECT_NE(role_cred, role_cred3); + + // different authority + std::string authority2 = "authority2"; + oc_role_t *role_cred4 = + oc_sec_role_cred_add_or_get({ role.c_str(), role.length() }, + { authority2.c_str(), authority2.length() }); + EXPECT_NE(nullptr, role_cred4); + EXPECT_EQ(3, countRoleCreds()); + EXPECT_NE(role_cred, role_cred4); +#endif /* OC_DYNAMIC_ALLOCATION */ +} + +TEST_F(TestRoleCreds, Add_Fail) +{ + ASSERT_EQ(0, countRoleCreds()); + + // missing role + std::string authority = "authority"; + EXPECT_EQ(nullptr, + oc_sec_role_cred_add_or_get( + { nullptr, 0 }, { authority.c_str(), authority.length() })); + EXPECT_EQ(0, countRoleCreds()); + + // missing authority + std::string role = "role"; + EXPECT_EQ(nullptr, oc_sec_role_cred_add_or_get( + { role.c_str(), role.length() }, { nullptr, 0 })); + EXPECT_EQ(0, countRoleCreds()); + +#ifndef OC_DYNAMIC_ALLOCATION + // without dynamic allocation, we can allocate only OC_ROLES_NUM_ROLE_CREDS + // role creds + for (int i = 0; i < OC_ROLES_NUM_ROLE_CREDS; ++i) { + std::string role = "role" + std::to_string(i); + std::string authority = "authority" + std::to_string(i); + oc_role_t *role_cred = + oc_sec_role_cred_add_or_get({ role.c_str(), role.length() }, + { authority.c_str(), authority.length() }); + EXPECT_NE(nullptr, role_cred); + EXPECT_EQ(i + 1, countRoleCreds()); + } + // adding one more should fail + EXPECT_EQ(nullptr, oc_sec_role_cred_add_or_get( + { role.c_str(), role.length() }, + { authority.c_str(), authority.length() })); + EXPECT_EQ(OC_ROLES_NUM_ROLE_CREDS, countRoleCreds()); +#endif /* !OC_DYNAMIC_ALLOCATION */ +} + +TEST_F(TestRoleCreds, Remove) +{ + for (int i = 0; i < OC_ROLES_NUM_ROLE_CREDS; ++i) { + std::string role = "role" + std::to_string(i); + std::string authority = "authority" + std::to_string(i); + oc_role_t *role_cred = + oc_sec_role_cred_add_or_get({ role.c_str(), role.length() }, + { authority.c_str(), authority.length() }); + EXPECT_NE(nullptr, role_cred); + } + EXPECT_EQ(OC_ROLES_NUM_ROLE_CREDS, countRoleCreds()); + + for (int i = 0; i < OC_ROLES_NUM_ROLE_CREDS; ++i) { + std::string role = "role" + std::to_string(i); + std::string authority = "authority" + std::to_string(i); + EXPECT_TRUE( + oc_sec_role_cred_remove({ role.c_str(), role.length() }, + { authority.c_str(), authority.length() })); + EXPECT_EQ(OC_ROLES_NUM_ROLE_CREDS - i - 1, countRoleCreds()); + } + EXPECT_EQ(0, countRoleCreds()); +} + +TEST_F(TestRoleCreds, RemoveFail) +{ + for (int i = 0; i < OC_ROLES_NUM_ROLE_CREDS; ++i) { + std::string role = "role" + std::to_string(i); + std::string authority = "authority" + std::to_string(i); + oc_role_t *role_cred = + oc_sec_role_cred_add_or_get({ role.c_str(), role.length() }, + { authority.c_str(), authority.length() }); + EXPECT_NE(nullptr, role_cred); + } + EXPECT_EQ(OC_ROLES_NUM_ROLE_CREDS, countRoleCreds()); + + // missing role + std::string authority = "authority0"; + EXPECT_FALSE(oc_sec_role_cred_remove( + { nullptr, 0 }, { authority.c_str(), authority.length() })); + + // missing authority + std::string role = "role0"; + EXPECT_FALSE( + oc_sec_role_cred_remove({ role.c_str(), role.length() }, { nullptr, 0 })); + + // non-existing role-authority pair + std::string role2 = "role2"; + std::string authority2 = "authority2"; + EXPECT_FALSE( + oc_sec_role_cred_remove({ role.c_str(), role.length() }, + { authority2.c_str(), authority2.length() })); + EXPECT_FALSE( + oc_sec_role_cred_remove({ role2.c_str(), role2.length() }, + { authority.c_str(), authority.length() })); + EXPECT_FALSE( + oc_sec_role_cred_remove({ role2.c_str(), role2.length() }, + { authority2.c_str(), authority2.length() })); +} + +// TODO: test oc_assert_role +// TODO: test oc_assert_all_roles + +#endif /* OC_CLIENT */ + +#endif /* OC_SECURITY && OC_PKI */ diff --git a/tests/gtest/tls/DTLS.cpp b/tests/gtest/tls/DTLS.cpp index 470301373e..443f7506fa 100644 --- a/tests/gtest/tls/DTLS.cpp +++ b/tests/gtest/tls/DTLS.cpp @@ -36,10 +36,11 @@ AddPresharedKey(size_t device, const PreSharedKey &psk) std::copy(std::begin(uuid->id), std::end(uuid->id), std::begin(hint)); std::array uuid_str{}; oc_uuid_to_str(uuid, uuid_str.data(), uuid_str.size()); - if (oc_sec_add_new_cred(device, false, nullptr, -1, OC_CREDTYPE_PSK, - OC_CREDUSAGE_NULL, uuid_str.data(), OC_ENCODING_RAW, - psk.size(), psk.data(), OC_ENCODING_UNSUPPORTED, 0, - nullptr, nullptr, nullptr, nullptr, nullptr) == -1) { + if (oc_sec_add_new_cred( + device, false, nullptr, -1, OC_CREDTYPE_PSK, OC_CREDUSAGE_NULL, + uuid_str.data(), { psk.data(), psk.size(), OC_ENCODING_RAW }, + { nullptr, 0, OC_ENCODING_UNSUPPORTED }, { nullptr, 0 }, { nullptr, 0 }, + { nullptr, 0 }, nullptr) == -1) { return {}; } return hint;