diff --git a/api/cloud/oc_cloud_rd.c b/api/cloud/oc_cloud_rd.c index 6df1bf638..b5484d257 100644 --- a/api/cloud/oc_cloud_rd.c +++ b/api/cloud/oc_cloud_rd.c @@ -160,8 +160,8 @@ static void cloud_publish_resources(oc_cloud_context_t *ctx) { #ifdef OC_SECURITY - if (!oc_sec_pstat_is_in_dos_state(ctx->device, - OC_PSTAT_DOS_ID_FLAG(OC_DOS_RFNOP))) { + if (!oc_device_is_in_dos_state(ctx->device, + OC_PSTAT_DOS_ID_FLAG(OC_DOS_RFNOP))) { OC_CLOUD_DBG("cannot publish resource links when not in RFNOP"); return; } @@ -253,8 +253,8 @@ cloud_delete_resources(oc_cloud_context_t *ctx) { assert(ctx->rd_delete_resources != NULL); #ifdef OC_SECURITY - if (!oc_sec_pstat_is_in_dos_state(ctx->device, - OC_PSTAT_DOS_ID_FLAG(OC_DOS_RFNOP))) { + if (!oc_device_is_in_dos_state(ctx->device, + OC_PSTAT_DOS_ID_FLAG(OC_DOS_RFNOP))) { OC_CLOUD_DBG("cannot unpublish resource links when not in RFNOP"); return; } diff --git a/api/oc_collection.c b/api/oc_collection.c index 6d78c1646..6f0ef8628 100644 --- a/api/oc_collection.c +++ b/api/oc_collection.c @@ -40,6 +40,7 @@ #ifdef OC_SECURITY #include "security/oc_acl_internal.h" +#include "security/oc_acl_util_internal.h" #endif /* OC_SECURITY */ #ifdef OC_HAS_FEATURE_ETAG diff --git a/api/oc_core_res.c b/api/oc_core_res.c index d59d8ee24..ab4751fc8 100644 --- a/api/oc_core_res.c +++ b/api/oc_core_res.c @@ -48,6 +48,7 @@ #endif /* OC_MNT */ #ifdef OC_SECURITY +#include "security/oc_acl_internal.h" #include "security/oc_doxm_internal.h" #include "security/oc_pstat_internal.h" #include "security/oc_roles_internal.h" @@ -86,6 +87,12 @@ static oc_device_info_t g_oc_device_info[OC_MAX_NUM_DEVICES] = { 0 }; static int g_res_latency = 0; static OC_ATOMIC_UINT32_T g_device_count = 0; +bool +oc_is_device_resource_uri(oc_string_view_t uri) +{ + return oc_resource_match_uri(OC_STRING_VIEW(OCF_D_URI), uri); +} + void oc_core_init(void) { @@ -687,8 +694,7 @@ oc_core_get_resource_type_by_uri(const char *uri, size_t uri_len) if (oc_is_platform_resource_uri(oc_string_view(uri, uri_len))) { return OCF_P; } - if (core_is_resource_uri(uri, uri_len, OCF_D_URI, - OC_CHAR_ARRAY_LEN(OCF_D_URI))) { + if (oc_is_device_resource_uri(oc_string_view(uri, uri_len))) { return OCF_D; } if (oc_is_discovery_resource_uri(oc_string_view(uri, uri_len))) { @@ -710,14 +716,12 @@ oc_core_get_resource_type_by_uri(const char *uri, size_t uri_len) } #endif /* OC_INTROSPECTION */ #ifdef OC_HAS_FEATURE_PLGD_TIME - if (core_is_resource_uri(uri, uri_len, PLGD_TIME_URI, - OC_CHAR_ARRAY_LEN(PLGD_TIME_URI))) { + if (plgd_is_time_resource_uri(oc_string_view(uri, uri_len))) { return PLGD_TIME; } #endif /* OC_HAS_FEATURE_PLGD_TIME */ #ifdef OC_WKCORE - if (core_is_resource_uri(uri, uri_len, OC_WELLKNOWNCORE_URI, - OC_CHAR_ARRAY_LEN(OC_WELLKNOWNCORE_URI))) { + if (oc_is_wkcore_resource_uri(oc_string_view(uri, uri_len))) { return WELLKNOWNCORE; } #endif /* OC_WKCORE */ @@ -733,19 +737,16 @@ oc_core_get_resource_type_by_uri(const char *uri, size_t uri_len) } #endif /* OC_CLIENT && OC_SERVER && OC_CLOUD */ #ifdef OC_SECURITY - if (core_is_resource_uri(uri, uri_len, "/oic/sec/pstat", - OC_CHAR_ARRAY_LEN("/oic/sec/pstat"))) { + if (oc_sec_is_pstat_resource_uri(oc_string_view(uri, uri_len))) { return OCF_SEC_PSTAT; } if (oc_sec_is_doxm_resource_uri(oc_string_view(uri, uri_len))) { return OCF_SEC_DOXM; } - if (core_is_resource_uri(uri, uri_len, "/oic/sec/acl2", - OC_CHAR_ARRAY_LEN("/oic/sec/acl2"))) { + if (oc_sec_is_acl_resource_uri(oc_string_view(uri, uri_len))) { return OCF_SEC_ACL; } - if (core_is_resource_uri(uri, uri_len, "/oic/sec/cred", - OC_CHAR_ARRAY_LEN("/oic/sec/cred"))) { + if (oc_sec_is_cred_resource_uri(oc_string_view(uri, uri_len))) { return OCF_SEC_CRED; } if (core_is_resource_uri(uri, uri_len, "/oic/sec/ael", @@ -761,8 +762,7 @@ oc_core_get_resource_type_by_uri(const char *uri, size_t uri_len) OC_CHAR_ARRAY_LEN(OCF_SEC_CSR_URI))) { return OCF_SEC_CSR; } - if (core_is_resource_uri(uri, uri_len, OCF_SEC_ROLES_URI, - OC_CHAR_ARRAY_LEN(OCF_SEC_ROLES_URI))) { + if (oc_sec_is_roles_resource_uri(oc_string_view(uri, uri_len))) { return OCF_SEC_ROLES; } #endif /* OC_PKI */ diff --git a/api/oc_core_res_internal.h b/api/oc_core_res_internal.h index 84aad6bf7..3e9c21289 100644 --- a/api/oc_core_res_internal.h +++ b/api/oc_core_res_internal.h @@ -19,6 +19,7 @@ #ifndef OC_CORE_RES_INTERNAL_H #define OC_CORE_RES_INTERNAL_H +#include "api/oc_helpers_internal.h" #include "oc_api.h" #include "oc_core_res.h" #include "oc_helpers.h" @@ -37,6 +38,11 @@ extern "C" { #define OCF_D_URI "/oic/d" #define OCF_D_RT "oic.wk.d" +/** @brief Check if the URI matches the device resource URI (with or without + * the leading slash) + */ +bool oc_is_device_resource_uri(oc_string_view_t uri); + /** * @brief initialize the core functionality */ diff --git a/api/oc_discovery.c b/api/oc_discovery.c index 2aacf53e2..430f81acb 100644 --- a/api/oc_discovery.c +++ b/api/oc_discovery.c @@ -48,6 +48,7 @@ #include "security/oc_tls_internal.h" #ifdef OC_RES_BATCH_SUPPORT #include "security/oc_acl_internal.h" +#include "security/oc_acl_util_internal.h" #endif /* OC_RES_BATCH_SUPPORT*/ #endif /* OC_SECURITY */ @@ -1266,6 +1267,12 @@ oc_create_wkcore_resource(size_t device) OC_WELLKNOWNCORE_RT); } +bool +oc_is_wkcore_resource_uri(oc_string_view_t uri) +{ + return oc_resource_match_uri(OC_STRING_VIEW(OC_WELLKNOWNCORE_URI), uri); +} + #endif /* OC_WKCORE */ void diff --git a/api/oc_discovery_internal.h b/api/oc_discovery_internal.h index 39dd68710..4e5a4d56c 100644 --- a/api/oc_discovery_internal.h +++ b/api/oc_discovery_internal.h @@ -135,8 +135,14 @@ uint64_t oc_discovery_get_batch_etag(const oc_endpoint_t *endpoint, #define OC_WELLKNOWNCORE_URI "/.well-known/core" #define OC_WELLKNOWNCORE_RT "wk" +/** @brief Create /.well-known/core resource */ void oc_create_wkcore_resource(size_t device); +/** @brief Check if the URI matches the wkcore resource URI (with or without + * the leading slash) + */ +bool oc_is_wkcore_resource_uri(oc_string_view_t uri); + #endif /* OC_WKCORE */ #ifdef __cplusplus diff --git a/api/oc_enums_internal.h b/api/oc_enums_internal.h index 19115f697..7b75818b0 100644 --- a/api/oc_enums_internal.h +++ b/api/oc_enums_internal.h @@ -27,6 +27,10 @@ extern "C" { #endif +#define OC_PERM_ALL \ + (OC_PERM_CREATE | OC_PERM_RETRIEVE | OC_PERM_UPDATE | OC_PERM_DELETE | \ + OC_PERM_NOTIFY) + /** @brief Convert enum value to string view */ oc_string_view_t oc_enum_to_string_view(oc_enum_t val); diff --git a/api/oc_etag.c b/api/oc_etag.c index 78d94cd11..e41bbce43 100644 --- a/api/oc_etag.c +++ b/api/oc_etag.c @@ -212,8 +212,7 @@ bool oc_etag_dump_ignore_resource(const char *uri, size_t uri_len) { #ifdef OC_WKCORE - if (uri_len == OC_CHAR_ARRAY_LEN(OC_WELLKNOWNCORE_URI) && - memcmp(uri, OC_WELLKNOWNCORE_URI, uri_len) == 0) { + if (oc_is_wkcore_resource_uri(oc_string_view(uri, uri_len))) { return true; } #endif /* OC_WKCORE */ @@ -441,8 +440,7 @@ static bool etag_can_update_device(size_t device) { #ifdef OC_SECURITY - return oc_sec_pstat_is_in_dos_state(device, - OC_PSTAT_DOS_ID_FLAG(OC_DOS_RFNOP)); + return oc_device_is_in_dos_state(device, OC_PSTAT_DOS_ID_FLAG(OC_DOS_RFNOP)); #else /* OC_SECURITY */ (void)device; return true; diff --git a/api/oc_ri.c b/api/oc_ri.c index 016babd22..698ca62fa 100644 --- a/api/oc_ri.c +++ b/api/oc_ri.c @@ -83,6 +83,7 @@ #ifdef OC_SECURITY #include "security/oc_acl_internal.h" +#include "security/oc_acl_util_internal.h" #include "security/oc_audit_internal.h" #include "security/oc_pstat_internal.h" #include "security/oc_roles_internal.h" diff --git a/api/plgd/plgd_time.c b/api/plgd/plgd_time.c index acfebd26b..bf26bb801 100644 --- a/api/plgd/plgd_time.c +++ b/api/plgd/plgd_time.c @@ -610,6 +610,12 @@ plgd_time_create_resource(void) /*delete*/ NULL, 1, PLGD_TIME_RT); } +bool +plgd_is_time_resource_uri(oc_string_view_t uri) +{ + return oc_resource_match_uri(OC_STRING_VIEW(PLGD_TIME_URI), uri); +} + void plgd_time_configure(bool use_in_mbedtls, plgd_set_system_time_fn_t set_system_time, diff --git a/api/plgd/plgd_time_internal.h b/api/plgd/plgd_time_internal.h index 0b62098ce..2c02f93c1 100644 --- a/api/plgd/plgd_time_internal.h +++ b/api/plgd/plgd_time_internal.h @@ -23,6 +23,7 @@ #ifdef OC_HAS_FEATURE_PLGD_TIME +#include "api/oc_helpers_internal.h" #include "oc_rep.h" #include "oc_ri.h" #include "plgd/plgd_time.h" @@ -56,6 +57,11 @@ extern "C" { */ void plgd_time_create_resource(void); +/** @brief Check if the URI matches the plgd time resource URI (with or without + * the leading slash) + */ +bool plgd_is_time_resource_uri(oc_string_view_t uri); + typedef struct plgd_time_store_t { oc_clock_time_t last_synced_time; diff --git a/api/unittest/etagtest.cpp b/api/unittest/etagtest.cpp index 1ca058c54..8c31989ed 100644 --- a/api/unittest/etagtest.cpp +++ b/api/unittest/etagtest.cpp @@ -594,13 +594,9 @@ isETagStorageEmpty(size_t device, bool platform = false) std::string store = platform ? OC_ETAG_PLATFORM_STORE_NAME : OC_ETAG_STORE_NAME; - long ret = oc_storage_data_load( - store.c_str(), device, [](const oc_rep_t *, size_t, void *) { return 0; }, - nullptr); - if (ret > 0) { - return false; - } - return true; + return oc_storage_data_load( + store.c_str(), device, + [](const oc_rep_t *, size_t, void *) { return 0; }, nullptr) <= 0; } TEST_F(TestETagWithServer, DumpAndLoad) @@ -688,7 +684,7 @@ TEST_F(TestETagWithServer, DumpAndLoad) #ifdef OC_SECURITY static bool -isPlatformResourceURI(const std::string &uri) +isPlatformResourceURI(std::string_view uri) { return uri == "/oic/p" #ifdef OC_HAS_FEATURE_PLGD_TIME diff --git a/docker/apps/Dockerfile.cloud-server b/docker/apps/Dockerfile.cloud-server index 619bc8a5f..43c6f713a 100644 --- a/docker/apps/Dockerfile.cloud-server +++ b/docker/apps/Dockerfile.cloud-server @@ -1,21 +1,23 @@ -FROM alpine:latest AS build +FROM alpine:3.19 AS build ARG BUILD_TYPE=Release ARG BUILD_ARGS RUN apk add --no-cache cmake curl git build-base gcc linux-headers patch perl python3 COPY ./ /iotivity-lite/ -RUN cd /iotivity-lite/ && git submodule update --recursive -RUN mkdir /iotivity-lite/build && \ - cd /iotivity-lite/build && \ - cmake -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DCMAKE_VERBOSE_MAKEFILE=ON -DBUILD_TESTING=OFF -DOC_CLOUD_ENABLED=ON ${BUILD_ARGS} .. && \ +WORKDIR /iotivity-lite +RUN git submodule update --recursive +RUN mkdir /iotivity-lite/build +WORKDIR /iotivity-lite/build +RUN cmake -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DCMAKE_VERBOSE_MAKEFILE=ON -DBUILD_TESTING=OFF -DOC_CLOUD_ENABLED=ON ${BUILD_ARGS} .. && \ cmake --build . --target cloud_server # install libfaketime +WORKDIR / # enable struct stat64 by adding -D__USE_LARGEFILE64 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE RUN git clone https://github.com/wolfcw/libfaketime.git && \ cd /libfaketime/src && \ make install FAKETIME_COMPILE_CFLAGS="-DFAKE_SETTIME -DFAKE_STATELESS -D__USE_LARGEFILE64 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE" -FROM alpine:latest AS service +FROM alpine:3.19 AS service RUN apk add --no-cache bash COPY --from=build /iotivity-lite/build/apps/cloud_server /iotivity-lite/port/linux/service COPY --from=build /usr/local/lib/faketime/libfaketimeMT.so.1 /usr/local/lib/faketime/libfaketimeMT.so.1 diff --git a/docker/apps/Dockerfile.cloud-server-debug b/docker/apps/Dockerfile.cloud-server-debug index 4f00bf331..1690fcecd 100644 --- a/docker/apps/Dockerfile.cloud-server-debug +++ b/docker/apps/Dockerfile.cloud-server-debug @@ -2,16 +2,19 @@ FROM ubuntu:22.04 AS service ARG BUILD_TYPE=Release ARG BUILD_ARGS RUN apt-get update -y && \ - DEBIAN_FRONTEND="noninteractive" apt-get install -y bash ca-certificates cmake curl gdb git-core gcovr g++ make patch python3 --no-install-recommends + DEBIAN_FRONTEND="noninteractive" apt-get install --no-install-recommends -y bash ca-certificates cmake curl gdb git-core gcovr g++ make patch python3 && \ + apt-get clean COPY ./ /iotivity-lite/ -RUN cd /iotivity-lite/ && git submodule update --recursive -RUN mkdir /iotivity-lite/build && \ - cd /iotivity-lite/build && \ - cmake -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DCMAKE_VERBOSE_MAKEFILE=ON -DBUILD_TESTING=OFF -DOC_CLOUD_ENABLED=ON ${BUILD_ARGS} .. && \ +WORKDIR /iotivity-lite +RUN git submodule update --recursive +RUN mkdir /iotivity-lite/build +WORKDIR /iotivity-lite/build +RUN cmake -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DCMAKE_VERBOSE_MAKEFILE=ON -DBUILD_TESTING=OFF -DOC_CLOUD_ENABLED=ON ${BUILD_ARGS} .. && \ cmake --build . --target cloud_server RUN cp /iotivity-lite/build/apps/cloud_server /iotivity-lite/port/linux/service # install libfaketime +WORKDIR / RUN git clone https://github.com/wolfcw/libfaketime.git && \ cd /libfaketime/src && \ make install FAKETIME_COMPILE_CFLAGS="-DFAKE_SETTIME -DFAKE_STATELESS" diff --git a/docker/apps/Dockerfile.cloud-server-debug-clang b/docker/apps/Dockerfile.cloud-server-debug-clang index 9ddadb326..c74c17482 100644 --- a/docker/apps/Dockerfile.cloud-server-debug-clang +++ b/docker/apps/Dockerfile.cloud-server-debug-clang @@ -3,22 +3,27 @@ FROM ubuntu:22.04 AS service ARG BUILD_TYPE=Release ARG BUILD_ARGS RUN apt-get update -y && \ - DEBIAN_FRONTEND="noninteractive" apt-get install -y bash ca-certificates cmake curl gdb git-core gcovr g++ make patch python3 --no-install-recommends -RUN DEBIAN_FRONTEND="noninteractive" apt-get install -y clang-15 -RUN update-alternatives --install /usr/bin/clang clang /usr/bin/clang-15 100 && \ + DEBIAN_FRONTEND="noninteractive" apt-get install --no-install-recommends -y bash ca-certificates cmake gdb git-core make patch python3 && \ + apt-get clean +# install clang +RUN DEBIAN_FRONTEND="noninteractive" apt-get install --no-install-recommends -y clang-15 && \ + apt-get clean && \ + update-alternatives --install /usr/bin/clang clang /usr/bin/clang-15 100 && \ update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-15 100 COPY ./ /iotivity-lite/ -RUN cd /iotivity-lite/ && git submodule update --recursive -RUN mkdir /iotivity-lite/build && \ - cd /iotivity-lite/build && \ - cmake -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_COMPILER=clang -DBUILD_TESTING=OFF -DOC_CLOUD_ENABLED=ON ${BUILD_ARGS} .. && \ +WORKDIR /iotivity-lite +RUN git submodule update --recursive +RUN mkdir /iotivity-lite/build +WORKDIR /iotivity-lite/build +RUN cmake -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_COMPILER=clang -DBUILD_TESTING=OFF -DOC_CLOUD_ENABLED=ON ${BUILD_ARGS} .. && \ cmake --build . --target cloud_server RUN cp /iotivity-lite/build/apps/cloud_server /iotivity-lite/port/linux/service # install libfaketime +WORKDIR / RUN git clone https://github.com/wolfcw/libfaketime.git && \ cd /libfaketime/src && \ - make install FAKETIME_COMPILE_CFLAGS="-DFAKE_SETTIME -DFAKE_STATELESS" + make CC=clang install FAKETIME_COMPILE_CFLAGS="-DFAKE_SETTIME -DFAKE_STATELESS" COPY /docker/logbt /usr/local/bin/logbt RUN logbt --version diff --git a/include/oc_rep.h b/include/oc_rep.h index cb32fe09d..11794caa2 100644 --- a/include/oc_rep.h +++ b/include/oc_rep.h @@ -1189,20 +1189,22 @@ typedef enum { OC_REP_OBJECT_ARRAY = 0x0E } oc_rep_value_type_t; +typedef union oc_rep_value { + int64_t integer; + bool boolean; + double double_p; + oc_string_t string; + oc_array_t array; + struct oc_rep_s *object; + struct oc_rep_s *object_array; +} oc_rep_value_t; + typedef struct oc_rep_s { oc_rep_value_type_t type; struct oc_rep_s *next; oc_string_t name; - union oc_rep_value { - int64_t integer; - bool boolean; - double double_p; - oc_string_t string; - oc_array_t array; - struct oc_rep_s *object; - struct oc_rep_s *object_array; - } value; + oc_rep_value_t value; } oc_rep_t; /** diff --git a/messaging/coap/observe.c b/messaging/coap/observe.c index 85b99f428..5a83f93f3 100644 --- a/messaging/coap/observe.c +++ b/messaging/coap/observe.c @@ -76,6 +76,7 @@ #ifdef OC_SECURITY #include "security/oc_acl_internal.h" +#include "security/oc_acl_util_internal.h" #include "security/oc_pstat_internal.h" #endif /* OC_SECURITY */ diff --git a/port/android/Makefile b/port/android/Makefile index 8a0087f48..0c494300a 100644 --- a/port/android/Makefile +++ b/port/android/Makefile @@ -269,7 +269,7 @@ ifeq ($(PLGD_DEV_TIME),1) endif ifneq ($(SECURE),0) - SRC += $(addprefix ../../security/, oc_acl.c oc_ael.c oc_audit.c oc_certs.c oc_certs_generate.c oc_certs_validate.c \ + SRC += $(addprefix ../../security/, oc_acl.c oc_acl_util.c oc_ael.c oc_audit.c oc_certs.c oc_certs_generate.c oc_certs_validate.c \ oc_cred.c oc_cred_util.c oc_csr.c oc_doxm.c oc_entropy.c oc_keypair.c oc_pki.c oc_pstat.c oc_roles.c oc_sdi.c \ oc_security.c oc_sp.c oc_store.c oc_svr.c oc_tls.c) SRC_COMMON += $(addprefix $(MBEDTLS_DIR)/library/,${DTLS}) diff --git a/port/arduino/adapter/Makefile b/port/arduino/adapter/Makefile index c8bbc7644..fdb4a71fe 100644 --- a/port/arduino/adapter/Makefile +++ b/port/arduino/adapter/Makefile @@ -39,7 +39,7 @@ ifeq ($(DYNAMIC),1) endif ifeq ($(SECURE),1) - SEC_SRC += $(addprefix $(ROOT_DIR)/security/, oc_acl.c oc_cred.c oc_cred_util.c oc_certs.c oc_certs_generate.c oc_certs_validate.c \ + SEC_SRC += $(addprefix $(ROOT_DIR)/security/, oc_acl.c oc_acl_util.c oc_cred.c oc_cred_util.c oc_certs.c oc_certs_generate.c oc_certs_validate.c \ oc_csr.c oc_doxm.c oc_entropy.c oc_keypair.c oc_pki.c oc_pstat.c oc_roles.c oc_security.c oc_sp.c oc_store.c oc_svr.c \ oc_tls.c) SRC += $(SEC_SRC) diff --git a/port/esp32/main/CMakeLists.txt b/port/esp32/main/CMakeLists.txt index 69adee8db..21f4a6a9c 100644 --- a/port/esp32/main/CMakeLists.txt +++ b/port/esp32/main/CMakeLists.txt @@ -187,6 +187,7 @@ if(CONFIG_SECURE) list(APPEND OC_DEFINITIONS -DOC_SECURITY -DOC_PKI) list(APPEND sources ${CMAKE_CURRENT_SOURCE_DIR}/../../../security/oc_acl.c + ${CMAKE_CURRENT_SOURCE_DIR}/../../../security/oc_acl_util.c ${CMAKE_CURRENT_SOURCE_DIR}/../../../security/oc_ael.c ${CMAKE_CURRENT_SOURCE_DIR}/../../../security/oc_audit.c ${CMAKE_CURRENT_SOURCE_DIR}/../../../security/oc_certs.c diff --git a/port/linux/Makefile b/port/linux/Makefile index 42fba4c0b..3c8391c48 100644 --- a/port/linux/Makefile +++ b/port/linux/Makefile @@ -291,7 +291,7 @@ endif endif ifneq ($(SECURE),0) - SRC += $(addprefix ../../security/, oc_acl.c oc_ael.c oc_audit.c oc_certs.c oc_certs_generate.c oc_certs_validate.c \ + SRC += $(addprefix ../../security/, oc_acl.c oc_acl_util.c oc_ael.c oc_audit.c oc_certs.c oc_certs_generate.c oc_certs_validate.c \ oc_cred.c oc_cred_util.c oc_csr.c oc_doxm.c oc_entropy.c oc_keypair.c oc_oscore_engine.c oc_oscore_crypto.c \ oc_oscore_context.c oc_pki.c oc_pstat.c oc_roles.c oc_sdi.c oc_security.c oc_sp.c oc_store.c oc_svr.c oc_tls.c) SRC_COMMON += $(addprefix $(MBEDTLS_DIR)/library/,${DTLS}) diff --git a/port/openthread/Makefile b/port/openthread/Makefile index 35e341853..c82a8de28 100644 --- a/port/openthread/Makefile +++ b/port/openthread/Makefile @@ -57,7 +57,7 @@ ifeq ($(MEMORY_TRACE), 1) endif ifeq ($(SECURE),1) - SRC_COMMON += oc_acl.c oc_cred.c oc_cred_util.c oc_doxm.c oc_pstat.c oc_dtls.c oc_svr.c oc_store.c oc_sdi.c + SRC_COMMON += oc_acl.c oc_acl_util.c oc_cred.c oc_cred_util.c oc_doxm.c oc_pstat.c oc_dtls.c oc_svr.c oc_store.c oc_sdi.c SRC_COMMON += memory_buffer_alloc.c CFLAGS += -DOC_SECURITY endif diff --git a/port/unittest/clocktest.cpp b/port/unittest/clocktest.cpp index 47ce9a241..bc4679a89 100644 --- a/port/unittest/clocktest.cpp +++ b/port/unittest/clocktest.cpp @@ -69,7 +69,7 @@ TEST_F(TestClock, oc_clock_wait) oc_clock_wait(wait_time); oc_clock_time_t cur_stamp = oc_clock_time(); - int seconds = (cur_stamp - prev_stamp) / OC_CLOCK_SECOND; + auto seconds = static_cast((cur_stamp - prev_stamp) / OC_CLOCK_SECOND); EXPECT_EQ(1, seconds); } diff --git a/port/windows/vs2015/IoTivity-lite.vcxproj b/port/windows/vs2015/IoTivity-lite.vcxproj index 679ee6a71..fc6e7f02a 100644 --- a/port/windows/vs2015/IoTivity-lite.vcxproj +++ b/port/windows/vs2015/IoTivity-lite.vcxproj @@ -280,6 +280,7 @@ + @@ -460,10 +461,8 @@ - - CompileAsC - CompileAsC - + + diff --git a/port/windows/vs2015/IoTivity-lite.vcxproj.filters b/port/windows/vs2015/IoTivity-lite.vcxproj.filters index e1498a000..c610f6318 100644 --- a/port/windows/vs2015/IoTivity-lite.vcxproj.filters +++ b/port/windows/vs2015/IoTivity-lite.vcxproj.filters @@ -359,6 +359,9 @@ Security + + Security + Security @@ -730,6 +733,9 @@ Security + + Security + Security diff --git a/security/oc_acl.c b/security/oc_acl.c index 2109a2625..d2dab612d 100644 --- a/security/oc_acl.c +++ b/security/oc_acl.c @@ -22,20 +22,15 @@ #include "api/oc_discovery_internal.h" #include "api/oc_helpers_internal.h" #include "api/oc_platform_internal.h" +#include "api/oc_resource_internal.h" #include "api/oc_ri_internal.h" -#include "oc_acl_internal.h" #include "oc_api.h" -#include "oc_certs_validate_internal.h" -#include "oc_config.h" #include "oc_core_res.h" -#include "oc_cred_internal.h" -#include "oc_doxm_internal.h" -#include "oc_pstat_internal.h" -#include "oc_rep.h" -#include "oc_roles_internal.h" #include "oc_store.h" -#include "oc_tls_internal.h" #include "port/oc_assert.h" +#include "port/oc_random.h" +#include "security/oc_acl_internal.h" +#include "security/oc_pstat_internal.h" #include "util/oc_features.h" #include "util/oc_macros_internal.h" @@ -106,7 +101,7 @@ get_new_aceid(size_t device) return aceid; } -static oc_ace_res_t * +oc_ace_res_t * oc_sec_ace_find_resource(oc_ace_res_t *start, const oc_sec_ace_t *ace, const char *href, oc_ace_wildcard_t wildcard) { @@ -220,456 +215,6 @@ oc_sec_acl_find_subject(oc_sec_ace_t *start, oc_ace_subject_type_t type, return ace; } -static uint16_t -oc_ace_get_permission(const oc_sec_ace_t *ace, const oc_resource_t *resource, - bool is_DCR, bool is_public) -{ - /* If the resource is discoverable and exposes >=1 unsecured endpoints - * then match with ACEs bearing any of the 3 wildcard resources. - * If the resource is discoverable and does not expose any unsecured - * endpoint, then match with ACEs bearing either OC_ACE_WC_ALL_SECURED or - * OC_ACE_WC_ALL. If the resource is not discoverable, then match only with - * ACEs bearing OC_ACE_WC_ALL. - */ - 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; - } - } else { - wc = OC_ACE_WC_ALL; - } - } - - uint16_t permission = 0; - oc_ace_res_t *res = - oc_sec_ace_find_resource(NULL, ace, oc_string(resource->uri), wc); - while (res != NULL) { - permission |= ace->permission; - - res = oc_sec_ace_find_resource(res, ace, oc_string(resource->uri), wc); - } - - return permission; -} - -#if OC_DBG_IS_ENABLED -static void -print_acls(size_t device) -{ - const oc_sec_acl_t *a = &g_aclist[device]; - const oc_sec_ace_t *ace = oc_list_head(a->subjects); - OC_DBG("\nAccess Control List\n---------"); - while (ace != NULL) { - OC_DBG("\n---------\nAce: %d\n---------", ace->aceid); - switch (ace->subject_type) { - case OC_SUBJECT_UUID: { - char u[OC_UUID_LEN]; - oc_uuid_to_str(&ace->subject.uuid, u, OC_UUID_LEN); - OC_DBG("UUID: %s", u); - } break; - case OC_SUBJECT_CONN: { - switch (ace->subject.conn) { - case OC_CONN_AUTH_CRYPT: - OC_DBG("CONN: auth-crypt"); - break; - case OC_CONN_ANON_CLEAR: - OC_DBG("CONN: anon-clear"); - break; - } - } break; - case OC_SUBJECT_ROLE: { - OC_DBG("Role_RoleId: %s", oc_string(ace->subject.role.role)); - if (oc_string_len(ace->subject.role.authority) > 0) { - OC_DBG("Role_Authority: %s", oc_string(ace->subject.role.authority)); - } - } break; - } - - oc_ace_res_t *r = oc_list_head(ace->resources); - OC_DBG("\nResources:"); - while (r != NULL) { - if (oc_string_len(r->href) > 0) { - OC_DBG("href: %s", oc_string(r->href)); - } - switch (r->wildcard) { - case OC_ACE_NO_WC: - OC_DBG("No wildcard"); - break; - case OC_ACE_WC_ALL: - OC_DBG("Wildcard: *"); - break; - case OC_ACE_WC_ALL_SECURED: - OC_DBG("Wildcard: +"); - break; - case OC_ACE_WC_ALL_PUBLIC: - OC_DBG("Wildcard: -"); - break; - } - OC_DBG("Permission: %d", ace->permission); - r = r->next; - } - ace = ace->next; - } -} -#endif /* OC_DBG_IS_ENABLED */ - -static uint16_t -get_role_permissions(const oc_sec_cred_t *role_cred, - const oc_resource_t *resource, size_t device, bool is_DCR, - bool is_public) -{ - uint16_t permission = 0; - oc_sec_ace_t *match = NULL; - do { - match = oc_sec_acl_find_subject(match, OC_SUBJECT_ROLE, - (const oc_ace_subject_t *)&role_cred->role, - /*aceid*/ -1, /*permission*/ 0, - /*tag*/ NULL, /*match_tag*/ false, device); - - if (match) { - permission |= oc_ace_get_permission(match, resource, is_DCR, is_public); - OC_DBG("oc_check_acl: Found ACE with permission %d for matching role", - permission); - } - } while (match); - return permission; -} - -static bool -eval_access(oc_method_t method, uint16_t permission) -{ - if (permission != 0) { - switch (method) { - case OC_GET: - if ((permission & OC_PERM_RETRIEVE) || (permission & OC_PERM_NOTIFY)) { - return true; - } - break; - case OC_PUT: - case OC_POST: - if ((permission & OC_PERM_CREATE) || (permission & OC_PERM_UPDATE)) { - return true; - } - break; - case OC_DELETE: - if (permission & OC_PERM_DELETE) { - return true; - } - break; - default: - break; - } - } - return false; -} - -static bool -oc_sec_check_acl_on_get(const oc_resource_t *resource, bool is_otm) -{ - oc_string_view_t uri = oc_string_view2(&resource->uri); - - /* Retrieve requests to "/oic/res", "/oic/d" and "/oic/p" shall be granted. - */ - if (is_otm && - ((uri.length == OC_CHAR_ARRAY_LEN(OCF_RES_URI) && - memcmp(uri.data, OCF_RES_URI, OC_CHAR_ARRAY_LEN(OCF_RES_URI)) == 0) || - (uri.length == OC_CHAR_ARRAY_LEN(OCF_D_URI) && - memcmp(uri.data, OCF_D_URI, OC_CHAR_ARRAY_LEN(OCF_D_URI)) == 0) || - oc_is_platform_resource_uri(uri))) { - return true; - } - -#ifdef OC_HAS_FEATURE_PLGD_TIME - if (uri.length == OC_CHAR_ARRAY_LEN(PLGD_TIME_URI) && - memcmp(uri.data, PLGD_TIME_URI, OC_CHAR_ARRAY_LEN(PLGD_TIME_URI)) == 0) { - return true; - } -#endif /* OC_HAS_FEATURE_PLGD_TIME */ - -#ifdef OC_WKCORE - /* if enabled also the .well-known/core will be granted access, since this - * also a discovery resource. */ - if (uri.length == OC_CHAR_ARRAY_LEN(OC_WELLKNOWNCORE_URI) && - memcmp(uri.data, OC_WELLKNOWNCORE_URI, - OC_CHAR_ARRAY_LEN(OC_WELLKNOWNCORE_URI)) == 0) { - return true; - } -#endif /* OC_WKCORE */ - -/* GET requests to /oic/sec/doxm are always granted. - * This is to ensure that multicast discovery using UUID filtered requests - * to /oic/sec/doxm is not blocked. - * - * The security implications of allowing universal read access to - * /oic/sec/doxm have not been thoroughly discussed. Enabling the following - * define is FOR DEVELOPMENT USE ONLY. - */ -#ifdef OC_DOXM_UUID_FILTER - if (method == OC_GET && oc_sec_is_doxm_resource_uri(uri)) { - OC_DBG("oc_sec_check_acl: R access granted to /doxm"); - return true; - } -#endif /* OC_DOXM_UUID_FILTER */ - return false; -} - -static bool -oc_sec_check_acl_by_uuid(const oc_uuid_t *uuid, size_t device, - const oc_resource_t *resource) -{ - const char *uri = oc_string(resource->uri); - size_t uri_len = oc_string_len(resource->uri); - if (memcmp(uuid->id, g_aclist[device].rowneruuid.id, sizeof(uuid->id)) == 0 && - uri_len == 13 && memcmp(uri, "/oic/sec/acl2", 13) == 0) { - OC_DBG("oc_acl: peer's UUID matches acl2's rowneruuid"); - return true; - } - const oc_sec_doxm_t *doxm = oc_sec_get_doxm(device); - if (memcmp(uuid->id, doxm->rowneruuid.id, sizeof(uuid->id)) == 0 && - oc_sec_is_doxm_resource_uri(oc_string_view(uri, uri_len))) { - OC_DBG("oc_acl: peer's UUID matches doxm's rowneruuid"); - return true; - } - const oc_sec_pstat_t *pstat = oc_sec_get_pstat(device); - if (memcmp(uuid->id, pstat->rowneruuid.id, sizeof(uuid->id)) == 0 && - uri_len == 14 && memcmp(uri, "/oic/sec/pstat", 14) == 0) { - OC_DBG("oc_acl: peer's UUID matches pstat's rowneruuid"); - return true; - } - const oc_sec_creds_t *creds = oc_sec_get_creds(device); - if (memcmp(uuid->id, creds->rowneruuid.id, sizeof(uuid->id)) == 0 && - uri_len == 13 && memcmp(uri, "/oic/sec/cred", 13) == 0) { - OC_DBG("oc_acl: peer's UUID matches cred's rowneruuid"); - return true; - } - return false; -} - -bool -oc_sec_check_acl(oc_method_t method, const oc_resource_t *resource, - const oc_endpoint_t *endpoint) -{ -#if OC_DBG_IS_ENABLED - print_acls(endpoint->device); -#endif /* OC_DBG_IS_ENABLED */ - - bool is_DCR = oc_core_is_DCR(resource, resource->device); - bool is_SVR = oc_core_is_SVR(resource, resource->device); - bool is_public = ((resource->properties & OC_SECURE) == 0); - bool is_vertical = false; - if (!is_DCR) { - is_vertical = oc_core_is_vertical_resource(resource, resource->device); - } - - const oc_sec_pstat_t *pstat = oc_sec_get_pstat(endpoint->device); - /* All unicast requests which are not received over the open Device DOC - * shall be rejected with an appropriate error message (e.g. forbidden), - * regardless of the configuration of the ACEs in the "/oic/sec/acl2" - * Resource. - */ - if (pstat->s == OC_DOS_RFOTM && !(endpoint->flags & SECURED) && - oc_tls_num_peers(endpoint->device) == 1) { - OC_DBG("oc_sec_check_acl: unencrypted request received while DOC is open - " - "access forbidden"); - return false; - } - -#ifdef OC_HAS_FEATURE_RESOURCE_ACCESS_IN_RFOTM - /* Allow access to resources in RFOTM mode if the feature is enabled and - * permission match the method. */ - if (pstat->s == OC_DOS_RFOTM && !(endpoint->flags & SECURED) && - (resource->properties & OC_ACCESS_IN_RFOTM) == OC_ACCESS_IN_RFOTM && - eval_access(method, resource->anon_permission_in_rfotm)) { - OC_DBG("oc_sec_check_acl: access granted to %s via anon permission in " - "RFOTM state", - oc_string(resource->uri)); - return true; - } -#endif /* OC_HAS_FEATURE_RESOURCE_ACCESS_IN_RFOTM */ - - /* NCRs are accessible only in RFNOP */ - if (!is_DCR && pstat->s != OC_DOS_RFNOP) { - OC_DBG("oc_sec_check_acl: resource is NCR and dos is not RFNOP"); - return false; - } - /* anon-clear access to vertical resources is prohibited */ - if (is_vertical && !(endpoint->flags & SECURED)) { - OC_DBG("oc_sec_check_acl: anon-clear access to vertical resources is " - "prohibited"); - return false; - } - /* All requests received over the DOC which target DCRs shall be granted, - * regardless of the configuration of the ACEs in the "/oic/sec/acl2" - * Resource. - */ - const oc_tls_peer_t *peer = oc_tls_get_peer(endpoint); - if (peer && peer->doc && is_DCR) { - OC_DBG("oc_sec_check_acl: connection is DOC and request directed to DCR - " - "access granted"); - return true; - } - - if (method == OC_GET && - oc_sec_check_acl_on_get(resource, pstat->s == OC_DOS_RFOTM)) { - return true; - } - - /* Requests over unsecured channel prior to DOC */ - if (pstat->s == OC_DOS_RFOTM && oc_tls_num_peers(endpoint->device) == 0) { - /* Anonymous Retrieve and Updates requests to “/oic/sec/doxm” shall be - granted. - */ - if (oc_sec_is_doxm_resource_uri(oc_string_view2(&resource->uri))) { - OC_DBG("oc_sec_check_acl: RW access granted to /doxm prior to DOC"); - return true; - } - /* All Retrieve requests to the “/oic/sec/pstat” Resource shall be - granted. - */ - if (oc_string_len(resource->uri) == 14 && - memcmp(oc_string(resource->uri), "/oic/sec/pstat", 14) == 0 && - method == OC_GET) { - OC_DBG("oc_sec_check_acl: R access granted to pstat prior to DOC"); - return true; - } - /* Reject all other requests */ - return false; - } - - if ((pstat->s == OC_DOS_RFPRO || pstat->s == OC_DOS_RFNOP || - pstat->s == OC_DOS_SRESET) && - !(endpoint->flags & SECURED)) { - /* anon-clear requests to SVRs while the - * dos is RFPRO, RFNOP or SRESET should not be authorized - * regardless of the ACL configuration. - */ - if (is_SVR) { - OC_DBG("oc_sec_check_acl: anon-clear access to SVRs in RFPRO, RFNOP and " - "SRESET is prohibited"); - return false; - } - } - - const oc_uuid_t *uuid = &endpoint->di; - if (uuid != NULL) { - if (oc_sec_check_acl_by_uuid(uuid, endpoint->device, resource)) { - return true; - } - if ((pstat->s == OC_DOS_RFPRO || pstat->s == OC_DOS_RFNOP || - pstat->s == OC_DOS_SRESET) && - 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; - } - } - - uint16_t permission = 0; - oc_sec_ace_t *match = NULL; - if (uuid != NULL) { - do { - oc_ace_subject_t subject; - memset(&subject, 0, sizeof(oc_ace_subject_t)); - memcpy(&subject.uuid, uuid, sizeof(*uuid)); - match = oc_sec_acl_find_subject(match, OC_SUBJECT_UUID, &subject, - /*aceid*/ -1, - /*permission*/ 0, /*tag*/ NULL, - /*match_tag*/ false, endpoint->device); - - if (match) { - permission |= oc_ace_get_permission(match, resource, is_DCR, is_public); - OC_DBG("oc_check_acl: Found ACE with permission %d for subject UUID", - permission); - } - } while (match); - - if (peer && oc_tls_uses_psk_cred(peer)) { - oc_sec_cred_t *role_cred = NULL; - do { - role_cred = oc_sec_find_cred(role_cred, uuid, OC_CREDTYPE_PSK, - OC_CREDUSAGE_NULL, endpoint->device); - if (role_cred == NULL) { - break; - } - if (oc_string_len(role_cred->role.role) > 0) { - permission |= get_role_permissions( - role_cred, resource, endpoint->device, is_DCR, is_public); - } - role_cred = role_cred->next; - } while (role_cred != NULL); - } -#ifdef OC_PKI - else { - 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; - if (oc_certs_validate_role_cert(role_cred->ctx, &flags) < 0 || - flags != 0) { - oc_sec_free_role(role_cred, peer); - role_cred = next; - continue; - } - if (oc_string_len(role_cred->role.role) == strlen("oic.role.owner") && - memcmp(oc_string(role_cred->role.role), "oic.role.owner", - oc_string_len(role_cred->role.role)) == 0) { - OC_DBG("oc_acl: peer's role matches \"oic.role.owner\""); - return true; - } - permission |= get_role_permissions(role_cred, resource, - endpoint->device, is_DCR, is_public); - role_cred = role_cred->next; - } - } -#endif /* OC_PKI */ - } - - if (!is_SVR) { - /* Access to SVRs via auth-crypt ACEs is prohibited */ - if (endpoint->flags & SECURED) { - oc_ace_subject_t _auth_crypt; - memset(&_auth_crypt, 0, sizeof(oc_ace_subject_t)); - _auth_crypt.conn = OC_CONN_AUTH_CRYPT; - do { - match = oc_sec_acl_find_subject(match, OC_SUBJECT_CONN, &_auth_crypt, - /*aceid*/ -1, /*permission*/ 0, - /*tag*/ NULL, /*match_tag*/ false, - endpoint->device); - if (match) { - permission |= - oc_ace_get_permission(match, resource, is_DCR, is_public); - OC_DBG("oc_check_acl: Found ACE with permission %d for auth-crypt " - "connection", - permission); - } - } while (match); - } - - /* Access to SVRs via anon-clear ACEs is prohibited */ - oc_ace_subject_t _anon_clear; - memset(&_anon_clear, 0, sizeof(oc_ace_subject_t)); - _anon_clear.conn = OC_CONN_ANON_CLEAR; - do { - match = oc_sec_acl_find_subject(match, OC_SUBJECT_CONN, &_anon_clear, - /*aceid*/ -1, /*permission*/ 0, - /*tag*/ NULL, /*match_tag*/ false, - endpoint->device); - if (match) { - permission |= oc_ace_get_permission(match, resource, is_DCR, is_public); - OC_DBG("oc_check_acl: Found ACE with permission %d for anon-clear " - "connection", - permission); - } - } while (match); - } - return eval_access(method, permission); -} - bool oc_sec_encode_acl(size_t device, oc_interface_mask_t iface_mask, bool to_storage) @@ -1020,6 +565,7 @@ oc_sec_acl_free(void) #ifdef OC_DYNAMIC_ALLOCATION if (g_aclist != NULL) { free(g_aclist); + g_aclist = NULL; } #endif /* OC_DYNAMIC_ALLOCATION */ } @@ -1343,8 +889,9 @@ oc_sec_apply_acl(const oc_rep_t *rep, size_t device, return -1; } -void -post_acl(oc_request_t *request, oc_interface_mask_t iface_mask, void *data) +static void +acl_resource_post(oc_request_t *request, oc_interface_mask_t iface_mask, + void *data) { (void)iface_mask; (void)data; @@ -1357,8 +904,9 @@ post_acl(oc_request_t *request, oc_interface_mask_t iface_mask, void *data) } } -void -delete_acl(oc_request_t *request, oc_interface_mask_t iface_mask, void *data) +static void +acl_resource_delete(oc_request_t *request, oc_interface_mask_t iface_mask, + void *data) { (void)iface_mask; (void)data; @@ -1395,8 +943,9 @@ delete_acl(oc_request_t *request, oc_interface_mask_t iface_mask, void *data) } } -void -get_acl(oc_request_t *request, oc_interface_mask_t iface_mask, void *data) +static void +acl_resource_get(oc_request_t *request, oc_interface_mask_t iface_mask, + void *data) { (void)data; if (oc_sec_encode_acl(request->resource->device, iface_mask, false)) { @@ -1407,6 +956,28 @@ get_acl(oc_request_t *request, oc_interface_mask_t iface_mask, void *data) } } +void +oc_sec_acl_create_resource(size_t device) +{ + oc_core_populate_resource( + OCF_SEC_ACL, device, OCF_SEC_ACL_URI, OC_IF_RW | OC_IF_BASELINE, OC_IF_RW, + OC_DISCOVERABLE | OC_SECURE, acl_resource_get, /*put*/ NULL, + acl_resource_post, acl_resource_delete, 1, OCF_SEC_ACL_RT); +} + +bool +oc_sec_is_acl_resource_uri(oc_string_view_t uri) +{ + return oc_resource_match_uri(OC_STRING_VIEW(OCF_SEC_ACL_URI), uri); +} + +bool +oc_sec_acl_is_owned_by(size_t device, oc_uuid_t uuid) +{ + const oc_sec_acl_t *acl = oc_sec_get_acl(device); + return oc_uuid_is_equal(acl->rowneruuid, uuid); +} + #ifdef OC_HAS_FEATURE_RESOURCE_ACCESS_IN_RFOTM void oc_resource_set_access_in_RFOTM(oc_resource_t *resource, bool state, diff --git a/security/oc_acl_internal.h b/security/oc_acl_internal.h index 392fbfba3..f403b6cd7 100644 --- a/security/oc_acl_internal.h +++ b/security/oc_acl_internal.h @@ -19,18 +19,24 @@ #ifndef OC_ACL_INTERNAL_H #define OC_ACL_INTERNAL_H +#include "api/oc_helpers_internal.h" #include "oc_acl.h" #include "oc_ri.h" #include "oc_uuid.h" #include "port/oc_log_internal.h" #include "util/oc_list.h" #include "util/oc_memb.h" + #include +#include #ifdef __cplusplus extern "C" { #endif +#define OCF_SEC_ACL_URI "/oic/sec/acl2" +#define OCF_SEC_ACL_RT "oic.r.acl2" + #define OC_ACE_WC_ALL_STR "*" #define OC_ACE_WC_ALL_SECURED_STR "+" #define OC_ACE_WC_ALL_PUBLIC_STR "-" @@ -43,13 +49,6 @@ bool oc_sec_encode_acl(size_t device, oc_interface_mask_t iface_mask, bool oc_sec_decode_acl(const oc_rep_t *rep, bool from_storage, size_t device, oc_sec_on_apply_acl_cb_t on_apply_ace_cb, void *on_apply_ace_data); -void post_acl(oc_request_t *request, oc_interface_mask_t iface_mask, - void *data); -void get_acl(oc_request_t *request, oc_interface_mask_t iface_mask, void *data); -void delete_acl(oc_request_t *request, oc_interface_mask_t iface_mask, - void *data); -bool oc_sec_check_acl(oc_method_t method, const oc_resource_t *resource, - const oc_endpoint_t *endpoint); bool oc_sec_acl_add_created_resource_ace(const char *href, const oc_endpoint_t *client, size_t device, bool collection); @@ -73,6 +72,25 @@ oc_sec_ace_t *oc_sec_acl_find_subject(oc_sec_ace_t *start, const char *tag, bool match_tag, size_t device); +oc_ace_res_t *oc_sec_ace_find_resource(oc_ace_res_t *start, + const oc_sec_ace_t *ace, + const char *href, + oc_ace_wildcard_t wildcard); + +/** + * @brief Create ACL (/oic/sec/acl2) resource for given device. + * + * @param device device index + */ +void oc_sec_acl_create_resource(size_t device); + +/** @brief Check if the URI matches the ACL resource URI (with or without the + * leading slash */ +bool oc_sec_is_acl_resource_uri(oc_string_view_t uri); + +/** @brief Check if the ACL resource is owned by given UUID */ +bool oc_sec_acl_is_owned_by(size_t device, oc_uuid_t uuid); + #ifdef __cplusplus } #endif diff --git a/security/oc_acl_util.c b/security/oc_acl_util.c new file mode 100644 index 000000000..9e0086154 --- /dev/null +++ b/security/oc_acl_util.c @@ -0,0 +1,526 @@ +/**************************************************************************** + * + * Copyright (c) 2016-2019 Intel Corporation + * Copyright (c) 2024 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. + * + ****************************************************************************/ + +#ifdef OC_SECURITY + +#include "api/oc_core_res_internal.h" +#include "api/oc_discovery_internal.h" +#include "api/oc_enums_internal.h" +#include "api/oc_platform_internal.h" +#include "oc_helpers.h" +#include "oc_uuid.h" +#include "port/oc_log_internal.h" +#include "security/oc_acl_internal.h" +#include "security/oc_acl_util_internal.h" +#include "security/oc_certs_validate_internal.h" +#include "security/oc_cred_internal.h" +#include "security/oc_doxm_internal.h" +#include "security/oc_pstat_internal.h" +#include "security/oc_roles_internal.h" +#include "security/oc_tls_internal.h" + +#ifdef OC_HAS_FEATURE_PLGD_TIME +#include "api/plgd/plgd_time_internal.h" +#endif /* OC_HAS_FEATURE_PLGD_TIME */ + +#include +#include +#include + +#if OC_DBG_IS_ENABLED + +static void +print_acls(size_t device) +{ + // GCOVR_EXCL_START + const oc_sec_acl_t *a = oc_sec_get_acl(device); + const oc_sec_ace_t *ace = oc_list_head(a->subjects); + OC_DBG("\nAccess Control List\n---------"); + while (ace != NULL) { + OC_DBG("\n---------\nAce: %d\n---------", ace->aceid); + switch (ace->subject_type) { + case OC_SUBJECT_UUID: { + char u[OC_UUID_LEN]; + oc_uuid_to_str(&ace->subject.uuid, u, OC_UUID_LEN); + OC_DBG("UUID: %s", u); + } break; + case OC_SUBJECT_CONN: { + switch (ace->subject.conn) { + case OC_CONN_AUTH_CRYPT: + OC_DBG("CONN: auth-crypt"); + break; + case OC_CONN_ANON_CLEAR: + OC_DBG("CONN: anon-clear"); + break; + } + } break; + case OC_SUBJECT_ROLE: { + OC_DBG("Role_RoleId: %s", oc_string(ace->subject.role.role)); + if (oc_string_len(ace->subject.role.authority) > 0) { + OC_DBG("Role_Authority: %s", oc_string(ace->subject.role.authority)); + } + } break; + } + + oc_ace_res_t *r = oc_list_head(ace->resources); + OC_DBG("\nResources:"); + while (r != NULL) { + if (oc_string_len(r->href) > 0) { + OC_DBG("href: %s", oc_string(r->href)); + } + switch (r->wildcard) { + case OC_ACE_NO_WC: + OC_DBG("No wildcard"); + break; + case OC_ACE_WC_ALL: + OC_DBG("Wildcard: *"); + break; + case OC_ACE_WC_ALL_SECURED: + OC_DBG("Wildcard: +"); + break; + case OC_ACE_WC_ALL_PUBLIC: + OC_DBG("Wildcard: -"); + break; + } + OC_DBG("Permission: %d", ace->permission); + r = r->next; + } + ace = ace->next; + } + // GCOVR_EXCL_STOP +} + +#endif /* OC_DBG_IS_ENABLED */ + +static uint16_t +oc_ace_get_permission(const oc_sec_ace_t *ace, const oc_resource_t *resource, + bool is_DCR, bool is_public) +{ + /* If the resource is discoverable and exposes >=1 unsecured endpoints + * then match with ACEs bearing any of the 3 wildcard resources. + * If the resource is discoverable and does not expose any unsecured + * endpoint, then match with ACEs bearing either OC_ACE_WC_ALL_SECURED or + * OC_ACE_WC_ALL. If the resource is not discoverable, then match only with + * ACEs bearing OC_ACE_WC_ALL. + */ + 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; + } + } else { + wc = OC_ACE_WC_ALL; + } + } + + uint16_t permission = 0; + oc_ace_res_t *res = + oc_sec_ace_find_resource(NULL, ace, oc_string(resource->uri), wc); + while (res != NULL) { + permission |= ace->permission; + + res = oc_sec_ace_find_resource(res, ace, oc_string(resource->uri), wc); + } + + return permission; +} + +static uint16_t +get_role_permissions(const oc_sec_cred_t *role_cred, + const oc_resource_t *resource, size_t device, bool is_DCR, + bool is_public) +{ + uint16_t permission = 0; + oc_sec_ace_t *match = NULL; + do { + match = oc_sec_acl_find_subject(match, OC_SUBJECT_ROLE, + (const oc_ace_subject_t *)&role_cred->role, + /*aceid*/ -1, /*permission*/ 0, + /*tag*/ NULL, /*match_tag*/ false, device); + + if (match != NULL) { + permission |= oc_ace_get_permission(match, resource, is_DCR, is_public); + OC_DBG("oc_check_acl: Found ACE with permission %d for matching role", + permission); + } + } while (match != NULL); + return permission; +} + +static bool +eval_access(oc_method_t method, uint16_t permission) +{ + OC_DBG("oc_check_acl: Evaluating access for method %d with permission %d", + method, permission); + if (permission == 0) { + return false; + } + if (method == OC_GET) { + return (permission & OC_PERM_RETRIEVE) != 0 || + (permission & OC_PERM_NOTIFY) != 0; + } + + if (method == OC_POST || method == OC_PUT) { + return (permission & OC_PERM_CREATE) != 0 || + (permission & OC_PERM_UPDATE) != 0; + } + return (method == OC_DELETE) && (permission & OC_PERM_DELETE) != 0; +} + +static bool +oc_sec_check_acl_on_get(const oc_resource_t *resource, bool is_otm) +{ + oc_string_view_t uriv = oc_string_view2(&resource->uri); + + /* Retrieve requests to "/oic/res", "/oic/d" and "/oic/p" shall be granted. + */ + if (is_otm && + (oc_is_discovery_resource_uri(uriv) || oc_is_device_resource_uri(uriv) || + oc_is_platform_resource_uri(uriv))) { + return true; + } + +#ifdef OC_HAS_FEATURE_PLGD_TIME + if (plgd_is_time_resource_uri(uriv)) { + return true; + } +#endif /* OC_HAS_FEATURE_PLGD_TIME */ + +#ifdef OC_WKCORE + /* if enabled also the .well-known/core will be granted access, since this + * also a discovery resource. */ + if (oc_is_wkcore_resource_uri(uriv)) { + return true; + } +#endif /* OC_WKCORE */ + +#ifdef OC_DOXM_UUID_FILTER + /* GET requests to /oic/sec/doxm are always granted. + * This is to ensure that multicast discovery using UUID filtered requests + * to /oic/sec/doxm is not blocked. + * + * The security implications of allowing universal read access to + * /oic/sec/doxm have not been thoroughly discussed. Enabling the following + * define is FOR DEVELOPMENT USE ONLY. + */ + if (method == OC_GET && oc_sec_is_doxm_resource_uri(uriv)) { + OC_DBG("oc_sec_check_acl: R access granted to /doxm"); + return true; + } +#endif /* OC_DOXM_UUID_FILTER */ + return false; +} + +static bool +oc_sec_check_acl_by_uuid(const oc_uuid_t *uuid, size_t device, + const oc_resource_t *resource) +{ + oc_string_view_t uriv = oc_string_view2(&resource->uri); + if (oc_sec_is_acl_resource_uri(uriv) && + oc_sec_acl_is_owned_by(device, *uuid)) { + OC_DBG("oc_acl: peer's UUID matches acl2's rowneruuid"); + return true; + } + if (oc_sec_is_doxm_resource_uri(uriv) && + oc_sec_doxm_is_owned_by(device, *uuid)) { + OC_DBG("oc_acl: peer's UUID matches doxm's rowneruuid"); + return true; + } + if (oc_sec_is_pstat_resource_uri(uriv) && + oc_sec_pstat_is_owned_by(device, *uuid)) { + OC_DBG("oc_acl: peer's UUID matches pstat's rowneruuid"); + return true; + } + if (oc_sec_is_cred_resource_uri(uriv) && + oc_sec_cred_is_owned_by(device, *uuid)) { + OC_DBG("oc_acl: peer's UUID matches cred's rowneruuid"); + return true; + } + return false; +} + +static bool +oc_sec_check_acl_in_rfotm_prior_to_doc(oc_method_t method, + const oc_resource_t *resource) +{ + /* Anonymous Retrieve and Updates requests to “/oic/sec/doxm” shall be + granted. + */ + if (oc_sec_is_doxm_resource_uri(oc_string_view2(&resource->uri))) { + OC_DBG("oc_sec_check_acl: RW access granted to doxm prior to DOC"); + return true; + } + /* All Retrieve requests to the “/oic/sec/pstat” Resource shall be + granted. */ + if (method == OC_GET && + oc_sec_is_pstat_resource_uri(oc_string_view2(&resource->uri))) { + OC_DBG("oc_sec_check_acl: R access granted to pstat prior to DOC"); + return true; + } + /* Reject all other requests */ + OC_DBG("oc_sec_check_acl: access denied to %s prior to DOC", + oc_string(resource->uri)); + return false; +} + +static uint16_t +get_peer_permissions(const oc_resource_t *resource, bool is_DCR, bool is_public, + const oc_endpoint_t *endpoint, const oc_tls_peer_t *peer) +{ + uint16_t permission = 0; + if (oc_tls_uses_psk_cred(peer)) { + const oc_uuid_t *uuid = &endpoint->di; + oc_sec_cred_t *role_cred = NULL; + do { + role_cred = oc_sec_find_cred(role_cred, uuid, OC_CREDTYPE_PSK, + OC_CREDUSAGE_NULL, endpoint->device); + if (role_cred == NULL) { + break; + } + if (!oc_string_is_empty(&role_cred->role.role)) { + permission |= get_role_permissions(role_cred, resource, + endpoint->device, is_DCR, is_public); + } + role_cred = role_cred->next; + } while (role_cred != NULL); + } +#ifdef OC_PKI + else { + const oc_sec_cred_t *role_cred = oc_sec_roles_get(peer); + while (role_cred != NULL) { + const oc_sec_cred_t *next = role_cred->next; + uint32_t flags = 0; + if (oc_certs_validate_role_cert(role_cred->ctx, &flags) < 0 || + flags != 0) { + oc_sec_free_role(role_cred, peer); + role_cred = next; + continue; + } + oc_string_view_t ownerv = OC_STRING_VIEW(OCF_SEC_ROLE_OWNER); + if (oc_string_view_is_equal(oc_string_view2(&role_cred->role.role), + ownerv)) { + OC_DBG("oc_acl: peer's role matches \"%s\"", OCF_SEC_ROLE_OWNER); + return OC_PERM_ALL; + } + permission |= get_role_permissions(role_cred, resource, endpoint->device, + is_DCR, is_public); + role_cred = role_cred->next; + } + } +#endif /* OC_PKI */ + return permission; +} + +static uint16_t +get_conn_permissions(const oc_resource_t *resource, bool is_DCR, bool is_public, + const oc_endpoint_t *endpoint) +{ + uint16_t permission = 0; + oc_sec_ace_t *match = NULL; + if ((endpoint->flags & SECURED) != 0) { + oc_ace_subject_t _auth_crypt; + memset(&_auth_crypt, 0, sizeof(oc_ace_subject_t)); + _auth_crypt.conn = OC_CONN_AUTH_CRYPT; + do { + match = oc_sec_acl_find_subject(match, OC_SUBJECT_CONN, &_auth_crypt, + /*aceid*/ -1, /*permission*/ 0, + /*tag*/ NULL, /*match_tag*/ false, + endpoint->device); + if (match == NULL) { + continue; + } + permission |= oc_ace_get_permission(match, resource, is_DCR, is_public); + OC_DBG("oc_check_acl: Found ACE with permission %d for auth-crypt " + "connection", + permission); + } while (match != NULL); + } + + oc_ace_subject_t _anon_clear; + memset(&_anon_clear, 0, sizeof(oc_ace_subject_t)); + _anon_clear.conn = OC_CONN_ANON_CLEAR; + do { + match = oc_sec_acl_find_subject(match, OC_SUBJECT_CONN, &_anon_clear, + /*aceid*/ -1, /*permission*/ 0, + /*tag*/ NULL, /*match_tag*/ false, + endpoint->device); + if (match == NULL) { + continue; + } + permission |= oc_ace_get_permission(match, resource, is_DCR, is_public); + OC_DBG("oc_check_acl: Found ACE with permission %d for anon-clear " + "connection", + permission); + } while (match != NULL); + + return permission; +} + +static bool +oc_sec_check_acl_by_permissions(oc_method_t method, + const oc_resource_t *resource, bool is_DCR, + bool is_SVR, const oc_endpoint_t *endpoint, + const oc_tls_peer_t *peer) +{ + const oc_uuid_t *uuid = &endpoint->di; + const bool is_public = ((resource->properties & OC_SECURE) == 0); + uint16_t permission = 0; + oc_sec_ace_t *match = NULL; + do { + oc_ace_subject_t subject; + memset(&subject, 0, sizeof(oc_ace_subject_t)); + memcpy(&subject.uuid, uuid, sizeof(*uuid)); + match = oc_sec_acl_find_subject(match, OC_SUBJECT_UUID, &subject, + /*aceid*/ -1, + /*permission*/ 0, /*tag*/ NULL, + /*match_tag*/ false, endpoint->device); + + if (match == NULL) { + continue; + } + permission |= oc_ace_get_permission(match, resource, is_DCR, is_public); + OC_DBG("oc_check_acl: Found ACE with permission %d for subject UUID", + permission); + } while (match != NULL); + + if (peer != NULL) { + permission |= + get_peer_permissions(resource, is_DCR, is_public, endpoint, peer); + } + + /* Access to SVRs via auth-crypt or anon-clear ACEs is prohibited */ + if (!is_SVR) { + permission |= get_conn_permissions(resource, is_DCR, is_public, endpoint); + } + + return eval_access(method, permission); +} + +bool +oc_sec_check_acl(oc_method_t method, const oc_resource_t *resource, + const oc_endpoint_t *endpoint) +{ +#if OC_DBG_IS_ENABLED + print_acls(endpoint->device); +#endif /* OC_DBG_IS_ENABLED */ + + const oc_sec_pstat_t *ps = oc_sec_get_pstat(endpoint->device); + oc_dostype_t dos = ps->s; + if (dos == OC_DOS_RFOTM && (endpoint->flags & SECURED) == 0) { + /* All unicast requests which are not received over the open Device DOC + * shall be rejected with an appropriate error message (e.g. forbidden), + * regardless of the configuration of the ACEs in the "/oic/sec/acl2" + * Resource. + */ + if (oc_tls_num_peers(endpoint->device) == 1) { + OC_DBG( + "oc_sec_check_acl: unencrypted request received while DOC is open - " + "access forbidden"); + return false; + } + +#ifdef OC_HAS_FEATURE_RESOURCE_ACCESS_IN_RFOTM + /* Allow access to resources in RFOTM mode if the feature is enabled and + * permission match the method. */ + if ((resource->properties & OC_ACCESS_IN_RFOTM) == OC_ACCESS_IN_RFOTM && + eval_access(method, (uint16_t)resource->anon_permission_in_rfotm)) { + OC_DBG("oc_sec_check_acl: access granted to %s via anon permission in " + "RFOTM state", + oc_string(resource->uri)); + return true; + } +#endif /* OC_HAS_FEATURE_RESOURCE_ACCESS_IN_RFOTM */ + } + + bool is_DCR = oc_core_is_DCR(resource, resource->device); + /* NCRs are accessible only in RFNOP */ + if (!is_DCR && dos != OC_DOS_RFNOP) { + OC_DBG("oc_sec_check_acl: resource is NCR and dos is not RFNOP"); + return false; + } + + const bool is_vertical = + !is_DCR && oc_core_is_vertical_resource(resource, resource->device); + /* anon-clear access to vertical resources is prohibited */ + if (is_vertical && (endpoint->flags & SECURED) == 0) { + OC_DBG("oc_sec_check_acl: anon-clear access to vertical resources is " + "prohibited"); + return false; + } + + /* All requests received over the DOC which target DCRs shall be granted, + * regardless of the configuration of the ACEs in the "/oic/sec/acl2" + * Resource. + */ + const oc_tls_peer_t *peer = oc_tls_get_peer(endpoint); + if (is_DCR && peer != NULL && peer->doc) { + OC_DBG("oc_sec_check_acl: connection is DOC and request directed to DCR - " + "access granted"); + return true; + } + + if (method == OC_GET && + oc_sec_check_acl_on_get(resource, dos == OC_DOS_RFOTM)) { + OC_DBG("oc_sec_check_acl: access granted to %s via special GET rule", + oc_string(resource->uri)); + return true; + } + + /* Requests over unsecured channel prior to DOC */ + if (dos == OC_DOS_RFOTM && oc_tls_num_peers(endpoint->device) == 0) { + return oc_sec_check_acl_in_rfotm_prior_to_doc(method, resource); + } + + bool is_SVR = oc_core_is_SVR(resource, resource->device); + /* anon-clear requests to SVRs while the dos is RFPRO, RFNOP or SRESET + * should not be authorized regardless of the ACL configuration */ + if (is_SVR && (endpoint->flags & SECURED) == 0 && + oc_sec_pstat_is_in_dos_state(ps, OC_PSTAT_DOS_ID_FLAG(OC_DOS_RFPRO) | + OC_PSTAT_DOS_ID_FLAG(OC_DOS_RFNOP) | + OC_PSTAT_DOS_ID_FLAG(OC_DOS_SRESET))) { + OC_DBG("oc_sec_check_acl: anon-clear access to SVRs in RFPRO, RFNOP and " + "SRESET is prohibited"); + return false; + } + + const oc_uuid_t *uuid = &endpoint->di; + // access to "/oic/sec/acl2", "/oic/sec/doxm", "/oic/sec/pstat" and + // "/oic/sec/cred" is granted to the owner of the device + if (oc_sec_check_acl_by_uuid(uuid, endpoint->device, resource)) { + return true; + } + +#ifdef OC_PKI + if (oc_sec_is_roles_resource_uri(oc_string_view2(&resource->uri)) && + oc_sec_pstat_is_in_dos_state(ps, OC_PSTAT_DOS_ID_FLAG(OC_DOS_RFPRO) | + OC_PSTAT_DOS_ID_FLAG(OC_DOS_RFNOP) | + OC_PSTAT_DOS_ID_FLAG(OC_DOS_SRESET))) { + OC_DBG("oc_acl: peer has implicit access to /oic/sec/roles in RFPRO, " + "RFNOP, SRESET"); + return true; + } +#endif /* OC_PKI */ + + return oc_sec_check_acl_by_permissions(method, resource, is_DCR, is_SVR, + endpoint, peer); +} + +#endif /* OC_SECURITY */ diff --git a/security/oc_acl_util_internal.h b/security/oc_acl_util_internal.h new file mode 100644 index 000000000..9627f8894 --- /dev/null +++ b/security/oc_acl_util_internal.h @@ -0,0 +1,41 @@ +/**************************************************************************** + * + * Copyright (c) 2016-2019 Intel Corporation + * Copyright (c) 2024 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. + * + ****************************************************************************/ + +#ifndef OC_ACL_UTIL_INTERNAL_H +#define OC_ACL_UTIL_INTERNAL_H + +#include "oc_endpoint.h" +#include "oc_ri.h" +#include "util/oc_compiler.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** Check method access to given resource */ +bool oc_sec_check_acl(oc_method_t method, const oc_resource_t *resource, + const oc_endpoint_t *endpoint) OC_NONNULL(); + +#ifdef __cplusplus +} +#endif + +#endif /* OC_ACL_UTIL_INTERNAL_H */ diff --git a/security/oc_cred.c b/security/oc_cred.c index d8eb1bfe5..3186171ce 100644 --- a/security/oc_cred.c +++ b/security/oc_cred.c @@ -20,6 +20,7 @@ #include "api/oc_helpers_internal.h" #include "api/oc_core_res_internal.h" +#include "api/oc_resource_internal.h" #include "oc_api.h" #include "oc_config.h" #include "oc_core_res.h" @@ -62,8 +63,8 @@ static oc_sec_creds_t g_devices[OC_MAX_NUM_DEVICES]; #ifdef OC_PKI static oc_string_view_t g_allowed_roles[] = { { - .data = "oic.role.owner", - .length = OC_CHAR_ARRAY_LEN("oic.role.owner"), + .data = OCF_SEC_ROLE_OWNER, + .length = OC_CHAR_ARRAY_LEN(OCF_SEC_ROLE_OWNER), } }; #endif /* OC_PKI */ @@ -1588,9 +1589,22 @@ 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, + OCF_SEC_CRED, device, OCF_SEC_CRED_URI, 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"); + cred_resource_post, cred_resource_delete, 1, OCF_SEC_CRED_RT); +} + +bool +oc_sec_is_cred_resource_uri(oc_string_view_t uri) +{ + return oc_resource_match_uri(OC_STRING_VIEW(OCF_SEC_CRED_URI), uri); +} + +bool +oc_sec_cred_is_owned_by(size_t device, oc_uuid_t uuid) +{ + const oc_sec_creds_t *creds = oc_sec_get_creds(device); + return oc_uuid_is_equal(creds->rowneruuid, uuid); } #endif /* OC_SECURITY */ diff --git a/security/oc_cred_internal.h b/security/oc_cred_internal.h index 1b5172ba0..28cb0905d 100644 --- a/security/oc_cred_internal.h +++ b/security/oc_cred_internal.h @@ -32,6 +32,9 @@ extern "C" { #endif +#define OCF_SEC_CRED_URI "/oic/sec/cred" +#define OCF_SEC_CRED_RT "oic.r.cred" + struct oc_tls_peer_t; typedef struct @@ -178,12 +181,19 @@ oc_sec_cred_t *oc_sec_find_role_cred(oc_sec_cred_t *start, oc_string_view_t tag); /** - * @brief Create roles (/oic/sec/cred) resource for given device. + * @brief Create cred (/oic/sec/cred) resource for given device. * * @param device device index */ void oc_sec_cred_create_resource(size_t device); +/** @brief Check if the URI matches the cred resource URI (with or without + * the leading slash */ +bool oc_sec_is_cred_resource_uri(oc_string_view_t uri); + +/** @brief Check if the cred resource is owned by given UUID */ +bool oc_sec_cred_is_owned_by(size_t device, oc_uuid_t uuid); + #ifdef __cplusplus } #endif diff --git a/security/oc_doxm.c b/security/oc_doxm.c index ca6b9d2d2..6e785fb96 100644 --- a/security/oc_doxm.c +++ b/security/oc_doxm.c @@ -738,9 +738,8 @@ doxm_resource_post(oc_request_t *request, oc_interface_mask_t iface_mask, { (void)iface_mask; (void)data; - const oc_tls_peer_t *p = oc_tls_get_peer(request->origin); if (!oc_sec_decode_doxm(request->request_payload, false, - p != NULL ? p->doc : false, + oc_tls_peer_is_doc(request->origin), request->resource->device)) { oc_send_response_with_callback(request, OC_STATUS_BAD_REQUEST, true); return; @@ -766,6 +765,13 @@ oc_sec_is_doxm_resource_uri(oc_string_view_t uri) return oc_resource_match_uri(OC_STRING_VIEW(OCF_SEC_DOXM_URI), uri); } +bool +oc_sec_doxm_is_owned_by(size_t device, oc_uuid_t uuid) +{ + const oc_sec_doxm_t *doxm = oc_sec_get_doxm(device); + return oc_uuid_is_equal(doxm->rowneruuid, uuid); +} + void oc_set_select_oxms_cb(oc_select_oxms_cb_t callback, void *user_data) { diff --git a/security/oc_doxm_internal.h b/security/oc_doxm_internal.h index 55c01e112..1ef6a3791 100644 --- a/security/oc_doxm_internal.h +++ b/security/oc_doxm_internal.h @@ -93,7 +93,7 @@ CborError oc_sec_encode_doxm(size_t device, oc_interface_mask_t iface_mask, bool to_storage); /** - * @brief Create roles (/oic/sec/doxm) resource for given device. + * @brief Create doxm (/oic/sec/doxm) resource for given device. * * @param device device index */ @@ -103,6 +103,9 @@ void oc_sec_doxm_create_resource(size_t device); * the leading slash */ bool oc_sec_is_doxm_resource_uri(oc_string_view_t uri); +/** @brief Check if the doxm resource is owned by given UUID */ +bool oc_sec_doxm_is_owned_by(size_t device, oc_uuid_t uuid); + #ifdef OC_TEST /** @brief Configure the delay of sending separate response */ diff --git a/security/oc_obt.c b/security/oc_obt.c index ad45f6343..e296ef821 100644 --- a/security/oc_obt.c +++ b/security/oc_obt.c @@ -871,7 +871,7 @@ switch_dos(oc_device_t *device, oc_dostype_t dos, oc_obt_status_cb_t cb, d->cb.cb = cb; d->cb.data = data; - if (!oc_init_post("/oic/sec/pstat", ep, NULL, &pstat_POST_dos1_to_dos2, + if (!oc_init_post(OCF_SEC_PSTAT_URI, ep, NULL, &pstat_POST_dos1_to_dos2, HIGH_QOS, d)) { OC_ERR("Could not init POST request to /oic/sec/pstat"); goto err_switch_dos; @@ -1069,7 +1069,7 @@ device1oscore_cred(oc_client_response_t *data) oc_uuid_to_str(&p->device1->uuid, d1uuid, OC_UUID_LEN); const oc_endpoint_t *ep = oc_obt_get_secure_endpoint(p->device2->endpoint); - if (!oc_init_post("/oic/sec/cred", ep, NULL, &device2oscore_cred, HIGH_QOS, + if (!oc_init_post(OCF_SEC_CRED_URI, ep, NULL, &device2oscore_cred, HIGH_QOS, p)) { OC_ERR("Could not init POST request to /oic/sec/cred"); goto err_device1oscore_cred; @@ -1140,7 +1140,7 @@ device2oscore_RFPRO(int status, void *data) char d2uuid[OC_UUID_LEN]; oc_uuid_to_str(&p->device2->uuid, d2uuid, OC_UUID_LEN); const oc_endpoint_t *ep = oc_obt_get_secure_endpoint(p->device1->endpoint); - if (!oc_init_post("/oic/sec/cred", ep, NULL, &device1oscore_cred, HIGH_QOS, + if (!oc_init_post(OCF_SEC_CRED_URI, ep, NULL, &device1oscore_cred, HIGH_QOS, p)) { OC_ERR("Could not init POST request to /oic/sec/cred"); goto err_device2oscore_RFPRO; @@ -1325,7 +1325,7 @@ deviceoscoregroup_RFPRO(int status, void *data) char groupsub[OC_UUID_LEN]; oc_uuid_to_str(&p->subjectuuid, groupsub, OC_UUID_LEN); const oc_endpoint_t *ep = oc_obt_get_secure_endpoint(p->device->endpoint); - if (!oc_init_post("/oic/sec/cred", ep, NULL, &deviceoscoregroup_cred, + if (!oc_init_post(OCF_SEC_CRED_URI, ep, NULL, &deviceoscoregroup_cred, HIGH_QOS, p)) { OC_ERR("Could not init POST request to /oic/sec/cred"); goto err_deviceoscoregroup_RFPRO; @@ -1555,7 +1555,7 @@ device1_cred(oc_client_response_t *data) const oc_endpoint_t *ep = oc_obt_get_secure_endpoint(p->device2->endpoint); - if (!oc_init_post("/oic/sec/cred", ep, NULL, &device2_cred, HIGH_QOS, p)) { + if (!oc_init_post(OCF_SEC_CRED_URI, ep, NULL, &device2_cred, HIGH_QOS, p)) { OC_ERR("Could not init POST request to /oic/sec/cred"); goto err_device1_cred; } @@ -1607,7 +1607,7 @@ device2_RFPRO(int status, void *data) const oc_endpoint_t *ep = oc_obt_get_secure_endpoint(p->device1->endpoint); - if (!oc_init_post("/oic/sec/cred", ep, NULL, &device1_cred, HIGH_QOS, p)) { + if (!oc_init_post(OCF_SEC_CRED_URI, ep, NULL, &device1_cred, HIGH_QOS, p)) { OC_ERR("Could not init POST request to /oic/sec/cred"); goto err_device2_RFPRO; } @@ -1810,7 +1810,7 @@ device_cred(oc_client_response_t *data) /** 6) post acl2 with auth-crypt RW ACE for /oic/sec/roles */ const oc_endpoint_t *ep = oc_obt_get_secure_endpoint(p->device1->endpoint); - if (!oc_init_post("/oic/sec/acl2", ep, NULL, &device_authcrypt_roles, + if (!oc_init_post(OCF_SEC_ACL_URI, ep, NULL, &device_authcrypt_roles, HIGH_QOS, p)) { OC_ERR("Could not init POST request to /oic/sec/acl2"); goto err_device_cred; @@ -1966,7 +1966,7 @@ device_CSR(oc_client_response_t *data) */ const oc_endpoint_t *ep = oc_obt_get_secure_endpoint(p->device1->endpoint); - if (!oc_init_post("/oic/sec/cred", ep, NULL, &device_cred, HIGH_QOS, p)) { + if (!oc_init_post(OCF_SEC_CRED_URI, ep, NULL, &device_cred, HIGH_QOS, p)) { OC_ERR("Could not init POST request to /oic/sec/cred"); goto err_device_CSR; } @@ -2054,7 +2054,7 @@ device_RFPRO(int status, void *data) /** 3) post cred with trustca */ const oc_endpoint_t *ep = oc_obt_get_secure_endpoint(p->device1->endpoint); - if (!oc_init_post("/oic/sec/cred", ep, NULL, &device_root, HIGH_QOS, p)) { + if (!oc_init_post(OCF_SEC_CRED_URI, ep, NULL, &device_root, HIGH_QOS, p)) { OC_ERR("Could not init POST request to /oic/sec/cred"); goto err_device_RFPRO; } @@ -2277,7 +2277,7 @@ trustanchor_device_RFPRO(int status, void *response_data) /** 3) post cred with trustca */ const oc_endpoint_t *ep = oc_obt_get_secure_endpoint(p->device1->endpoint); - if (!oc_init_post("/oic/sec/cred", ep, NULL, &trustanchor_device_RFNOP, + if (!oc_init_post(OCF_SEC_CRED_URI, ep, NULL, &trustanchor_device_RFNOP, HIGH_QOS, p)) { OC_ERR("Could not init POST request to /oic/sec/cred"); goto err_trustanchor_device_RFPRO; @@ -2648,7 +2648,7 @@ provision_ace(int status, void *data) oc_sec_ace_t *ace = r->ace; const oc_endpoint_t *ep = oc_obt_get_secure_endpoint(device->endpoint); - if (!oc_init_post("/oic/sec/acl2", ep, NULL, &acl2_response, HIGH_QOS, r)) { + if (!oc_init_post(OCF_SEC_ACL_URI, ep, NULL, &acl2_response, HIGH_QOS, r)) { OC_ERR("Could not init POST request to /oic/sec/acl2"); goto err_provision_ace; } @@ -3010,7 +3010,7 @@ oc_obt_retrieve_creds(const oc_uuid_t *uuid, oc_obt_creds_cb_t cb, void *data) oc_tls_select_psk_ciphersuite(); const oc_endpoint_t *ep = oc_obt_get_secure_endpoint(device->endpoint); - if (!oc_do_get("/oic/sec/cred", ep, NULL, &cred_rsrc, HIGH_QOS, r)) { + if (!oc_do_get(OCF_SEC_CRED_URI, ep, NULL, &cred_rsrc, HIGH_QOS, r)) { OC_ERR("could not issue GET request to /oic/sec/cred"); oc_memb_free(&oc_credret_ctx_m, r); return -1; @@ -3099,7 +3099,7 @@ creddel_RFPRO(int status, void *data) char query[64]; snprintf(query, 64, "credid=%d", p->credid); const oc_endpoint_t *ep = oc_obt_get_secure_endpoint(p->device->endpoint); - if (!oc_do_delete("/oic/sec/cred", ep, query, &cred_del, HIGH_QOS, p)) { + if (!oc_do_delete(OCF_SEC_CRED_URI, ep, query, &cred_del, HIGH_QOS, p)) { OC_ERR("could not issue DELETE request to /oic/sec/cred"); goto err_creddel_RFPRO; } @@ -3343,7 +3343,7 @@ oc_obt_retrieve_acl(const oc_uuid_t *uuid, oc_obt_acl_cb_t cb, void *data) oc_tls_select_psk_ciphersuite(); const oc_endpoint_t *ep = oc_obt_get_secure_endpoint(device->endpoint); - if (!oc_do_get("/oic/sec/acl2", ep, NULL, &acl2_rsrc, HIGH_QOS, r)) { + if (!oc_do_get(OCF_SEC_ACL_URI, ep, NULL, &acl2_rsrc, HIGH_QOS, r)) { OC_ERR("could not issue GET request to /oic/sec/acl2"); oc_memb_free(&oc_aclret_ctx_m, r); return -1; @@ -3431,7 +3431,7 @@ acedel_RFPRO(int status, void *data) char query[64]; snprintf(query, 64, "aceid=%d", p->aceid); const oc_endpoint_t *ep = oc_obt_get_secure_endpoint(p->device->endpoint); - if (!oc_do_delete("/oic/sec/acl2", ep, query, &ace_del, HIGH_QOS, p)) { + if (!oc_do_delete(OCF_SEC_ACL_URI, ep, query, &ace_del, HIGH_QOS, p)) { OC_ERR("could not issue DELETE request to /oic/sec/acl2"); goto err_acedel_RFPRO; } diff --git a/security/oc_obt_otm_cert.c b/security/oc_obt_otm_cert.c index 5dea45828..0610cccaa 100644 --- a/security/oc_obt_otm_cert.c +++ b/security/oc_obt_otm_cert.c @@ -76,7 +76,7 @@ obt_cert_15(oc_client_response_t *data) */ const oc_device_t *device = o->device; const oc_endpoint_t *ep = oc_obt_get_secure_endpoint(device->endpoint); - if (oc_init_post("/oic/sec/pstat", ep, NULL, &obt_cert_16, HIGH_QOS, o)) { + if (oc_init_post(OCF_SEC_PSTAT_URI, ep, NULL, &obt_cert_16, HIGH_QOS, o)) { oc_rep_start_root_object(); oc_rep_set_object(root, dos); oc_rep_set_int(dos, s, OC_DOS_RFNOP); @@ -108,7 +108,7 @@ obt_cert_14(oc_client_response_t *data) */ const oc_device_t *device = o->device; const oc_endpoint_t *ep = oc_obt_get_secure_endpoint(device->endpoint); - if (oc_init_post("/oic/sec/acl2", ep, NULL, &obt_cert_15, HIGH_QOS, o)) { + if (oc_init_post(OCF_SEC_ACL_URI, ep, NULL, &obt_cert_15, HIGH_QOS, o)) { char uuid[OC_UUID_LEN]; const oc_uuid_t *my_uuid = oc_core_get_device_id(0); oc_uuid_to_str(my_uuid, uuid, OC_UUID_LEN); @@ -219,7 +219,7 @@ obt_cert_13(oc_client_response_t *data) */ const oc_device_t *device = o->device; const oc_endpoint_t *ep = oc_obt_get_secure_endpoint(device->endpoint); - if (oc_do_delete("/oic/sec/acl2", ep, NULL, &obt_cert_14, HIGH_QOS, o)) { + if (oc_do_delete(OCF_SEC_ACL_URI, ep, NULL, &obt_cert_14, HIGH_QOS, o)) { return; } @@ -244,7 +244,7 @@ obt_cert_12(oc_client_response_t *data) */ const oc_device_t *device = o->device; const oc_endpoint_t *ep = oc_obt_get_secure_endpoint(device->endpoint); - if (oc_init_post("/oic/sec/pstat", ep, NULL, &obt_cert_13, HIGH_QOS, o)) { + if (oc_init_post(OCF_SEC_PSTAT_URI, ep, NULL, &obt_cert_13, HIGH_QOS, o)) { oc_rep_start_root_object(); oc_rep_set_object(root, dos); oc_rep_set_int(dos, s, OC_DOS_RFPRO); @@ -379,7 +379,7 @@ obt_cert_9(oc_client_response_t *data) /** 9) post cred rowneruuid, cred */ - if (oc_init_post("/oic/sec/cred", ep, NULL, &obt_cert_10, HIGH_QOS, o)) { + if (oc_init_post(OCF_SEC_CRED_URI, ep, NULL, &obt_cert_10, HIGH_QOS, o)) { oc_rep_start_root_object(); oc_rep_set_array(root, creds); oc_rep_object_array_start_item(creds); @@ -422,7 +422,7 @@ obt_cert_8(oc_client_response_t *data) */ const oc_device_t *device = o->device; const oc_endpoint_t *ep = oc_obt_get_secure_endpoint(device->endpoint); - if (oc_init_post("/oic/sec/pstat", ep, NULL, &obt_cert_9, HIGH_QOS, o)) { + if (oc_init_post(OCF_SEC_PSTAT_URI, ep, NULL, &obt_cert_9, HIGH_QOS, o)) { const oc_uuid_t *my_uuid = oc_core_get_device_id(0); char uuid[OC_UUID_LEN]; oc_uuid_to_str(my_uuid, uuid, OC_UUID_LEN); @@ -457,7 +457,7 @@ obt_cert_7(oc_client_response_t *data) const oc_device_t *device = o->device; const oc_endpoint_t *ep = oc_obt_get_secure_endpoint(device->endpoint); - if (oc_init_post("/oic/sec/acl2", ep, NULL, &obt_cert_8, HIGH_QOS, o)) { + if (oc_init_post(OCF_SEC_ACL_URI, ep, NULL, &obt_cert_8, HIGH_QOS, o)) { const oc_uuid_t *my_uuid = oc_core_get_device_id(0); char uuid[OC_UUID_LEN]; oc_uuid_to_str(my_uuid, uuid, OC_UUID_LEN); @@ -609,7 +609,7 @@ obt_cert_3(oc_client_response_t *data) const oc_endpoint_t *ep = oc_obt_get_secure_endpoint(device->endpoint); oc_tls_close_connection(ep); oc_tls_select_cert_ciphersuite(); - if (oc_init_post("/oic/sec/pstat", ep, NULL, &obt_cert_4, HIGH_QOS, o)) { + if (oc_init_post(OCF_SEC_PSTAT_URI, ep, NULL, &obt_cert_4, HIGH_QOS, o)) { oc_rep_start_root_object(); oc_rep_set_int(root, om, 4); oc_rep_end_root_object(); diff --git a/security/oc_obt_otm_justworks.c b/security/oc_obt_otm_justworks.c index 270f99019..854bac484 100644 --- a/security/oc_obt_otm_justworks.c +++ b/security/oc_obt_otm_justworks.c @@ -75,7 +75,7 @@ obt_jw_15(oc_client_response_t *data) */ const oc_device_t *device = o->device; const oc_endpoint_t *ep = oc_obt_get_secure_endpoint(device->endpoint); - if (oc_init_post("/oic/sec/pstat", ep, NULL, &obt_jw_16, HIGH_QOS, o)) { + if (oc_init_post(OCF_SEC_PSTAT_URI, ep, NULL, &obt_jw_16, HIGH_QOS, o)) { oc_rep_start_root_object(); oc_rep_set_object(root, dos); oc_rep_set_int(dos, s, OC_DOS_RFNOP); @@ -107,7 +107,7 @@ obt_jw_14(oc_client_response_t *data) */ const oc_device_t *device = o->device; const oc_endpoint_t *ep = oc_obt_get_secure_endpoint(device->endpoint); - if (oc_init_post("/oic/sec/acl2", ep, NULL, &obt_jw_15, HIGH_QOS, o)) { + if (oc_init_post(OCF_SEC_ACL_URI, ep, NULL, &obt_jw_15, HIGH_QOS, o)) { char uuid[OC_UUID_LEN]; const oc_uuid_t *my_uuid = oc_core_get_device_id(0); oc_uuid_to_str(my_uuid, uuid, OC_UUID_LEN); @@ -218,7 +218,7 @@ obt_jw_13(oc_client_response_t *data) */ const oc_device_t *device = o->device; const oc_endpoint_t *ep = oc_obt_get_secure_endpoint(device->endpoint); - if (oc_do_delete("/oic/sec/acl2", ep, NULL, &obt_jw_14, HIGH_QOS, o)) { + if (oc_do_delete(OCF_SEC_ACL_URI, ep, NULL, &obt_jw_14, HIGH_QOS, o)) { return; } @@ -243,7 +243,7 @@ obt_jw_12(oc_client_response_t *data) */ const oc_device_t *device = o->device; const oc_endpoint_t *ep = oc_obt_get_secure_endpoint(device->endpoint); - if (oc_init_post("/oic/sec/pstat", ep, NULL, &obt_jw_13, HIGH_QOS, o)) { + if (oc_init_post(OCF_SEC_PSTAT_URI, ep, NULL, &obt_jw_13, HIGH_QOS, o)) { oc_rep_start_root_object(); oc_rep_set_object(root, dos); oc_rep_set_int(dos, s, OC_DOS_RFPRO); @@ -378,7 +378,7 @@ obt_jw_9(oc_client_response_t *data) /** 9) post cred rowneruuid, cred */ - if (oc_init_post("/oic/sec/cred", ep, NULL, &obt_jw_10, HIGH_QOS, o)) { + if (oc_init_post(OCF_SEC_CRED_URI, ep, NULL, &obt_jw_10, HIGH_QOS, o)) { oc_rep_start_root_object(); oc_rep_set_array(root, creds); oc_rep_object_array_start_item(creds); @@ -421,7 +421,7 @@ obt_jw_8(oc_client_response_t *data) */ const oc_device_t *device = o->device; const oc_endpoint_t *ep = oc_obt_get_secure_endpoint(device->endpoint); - if (oc_init_post("/oic/sec/pstat", ep, NULL, &obt_jw_9, HIGH_QOS, o)) { + if (oc_init_post(OCF_SEC_PSTAT_URI, ep, NULL, &obt_jw_9, HIGH_QOS, o)) { const oc_uuid_t *my_uuid = oc_core_get_device_id(0); char uuid[OC_UUID_LEN]; oc_uuid_to_str(my_uuid, uuid, OC_UUID_LEN); @@ -456,7 +456,7 @@ obt_jw_7(oc_client_response_t *data) const oc_device_t *device = o->device; const oc_endpoint_t *ep = oc_obt_get_secure_endpoint(device->endpoint); - if (oc_init_post("/oic/sec/acl2", ep, NULL, &obt_jw_8, HIGH_QOS, o)) { + if (oc_init_post(OCF_SEC_ACL_URI, ep, NULL, &obt_jw_8, HIGH_QOS, o)) { const oc_uuid_t *my_uuid = oc_core_get_device_id(0); char uuid[OC_UUID_LEN]; oc_uuid_to_str(my_uuid, uuid, OC_UUID_LEN); @@ -608,7 +608,7 @@ obt_jw_3(oc_client_response_t *data) const oc_endpoint_t *ep = oc_obt_get_secure_endpoint(device->endpoint); oc_tls_close_connection(ep); oc_tls_select_anon_ciphersuite(); - if (oc_init_post("/oic/sec/pstat", ep, NULL, &obt_jw_4, HIGH_QOS, o)) { + if (oc_init_post(OCF_SEC_PSTAT_URI, ep, NULL, &obt_jw_4, HIGH_QOS, o)) { oc_rep_start_root_object(); oc_rep_set_int(root, om, 4); oc_rep_end_root_object(); diff --git a/security/oc_obt_otm_randompin.c b/security/oc_obt_otm_randompin.c index f5d191a49..f1fee879b 100644 --- a/security/oc_obt_otm_randompin.c +++ b/security/oc_obt_otm_randompin.c @@ -76,7 +76,7 @@ obt_rdp_13(oc_client_response_t *data) */ const oc_device_t *device = o->device; const oc_endpoint_t *ep = oc_obt_get_secure_endpoint(device->endpoint); - if (oc_init_post("/oic/sec/pstat", ep, NULL, &obt_rdp_14, HIGH_QOS, o)) { + if (oc_init_post(OCF_SEC_PSTAT_URI, ep, NULL, &obt_rdp_14, HIGH_QOS, o)) { oc_rep_start_root_object(); oc_rep_set_object(root, dos); oc_rep_set_int(dos, s, OC_DOS_RFNOP); @@ -108,7 +108,7 @@ obt_rdp_12(oc_client_response_t *data) */ const oc_device_t *device = o->device; const oc_endpoint_t *ep = oc_obt_get_secure_endpoint(device->endpoint); - if (oc_init_post("/oic/sec/acl2", ep, NULL, &obt_rdp_13, HIGH_QOS, o)) { + if (oc_init_post(OCF_SEC_ACL_URI, ep, NULL, &obt_rdp_13, HIGH_QOS, o)) { char uuid[OC_UUID_LEN]; const oc_uuid_t *my_uuid = oc_core_get_device_id(0); oc_uuid_to_str(my_uuid, uuid, OC_UUID_LEN); @@ -219,7 +219,7 @@ obt_rdp_11(oc_client_response_t *data) */ const oc_device_t *device = o->device; const oc_endpoint_t *ep = oc_obt_get_secure_endpoint(device->endpoint); - if (oc_do_delete("/oic/sec/acl2", ep, NULL, &obt_rdp_12, HIGH_QOS, o)) { + if (oc_do_delete(OCF_SEC_ACL_URI, ep, NULL, &obt_rdp_12, HIGH_QOS, o)) { return; } @@ -244,7 +244,7 @@ obt_rdp_10(oc_client_response_t *data) */ const oc_device_t *device = o->device; const oc_endpoint_t *ep = oc_obt_get_secure_endpoint(device->endpoint); - if (oc_init_post("/oic/sec/pstat", ep, NULL, &obt_rdp_11, HIGH_QOS, o)) { + if (oc_init_post(OCF_SEC_PSTAT_URI, ep, NULL, &obt_rdp_11, HIGH_QOS, o)) { oc_rep_start_root_object(); oc_rep_set_object(root, dos); oc_rep_set_int(dos, s, OC_DOS_RFPRO); @@ -379,7 +379,7 @@ obt_rdp_7(oc_client_response_t *data) /** 7) post cred rowneruuid, cred */ - if (oc_init_post("/oic/sec/cred", ep, NULL, &obt_rdp_8, HIGH_QOS, o)) { + if (oc_init_post(OCF_SEC_CRED_URI, ep, NULL, &obt_rdp_8, HIGH_QOS, o)) { oc_rep_start_root_object(); oc_rep_set_array(root, creds); oc_rep_object_array_start_item(creds); @@ -422,7 +422,7 @@ obt_rdp_6(oc_client_response_t *data) */ const oc_device_t *device = o->device; const oc_endpoint_t *ep = oc_obt_get_secure_endpoint(device->endpoint); - if (oc_init_post("/oic/sec/pstat", ep, NULL, &obt_rdp_7, HIGH_QOS, o)) { + if (oc_init_post(OCF_SEC_PSTAT_URI, ep, NULL, &obt_rdp_7, HIGH_QOS, o)) { const oc_uuid_t *my_uuid = oc_core_get_device_id(0); char uuid[OC_UUID_LEN]; oc_uuid_to_str(my_uuid, uuid, OC_UUID_LEN); @@ -457,7 +457,7 @@ obt_rdp_5(oc_client_response_t *data) const oc_device_t *device = o->device; const oc_endpoint_t *ep = oc_obt_get_secure_endpoint(device->endpoint); - if (oc_init_post("/oic/sec/acl2", ep, NULL, &obt_rdp_6, HIGH_QOS, o)) { + if (oc_init_post(OCF_SEC_ACL_URI, ep, NULL, &obt_rdp_6, HIGH_QOS, o)) { const oc_uuid_t *my_uuid = oc_core_get_device_id(0); char uuid[OC_UUID_LEN]; oc_uuid_to_str(my_uuid, uuid, OC_UUID_LEN); @@ -671,7 +671,7 @@ oc_obt_perform_random_pin_otm(const oc_uuid_t *uuid, const unsigned char *pin, oc_tls_close_connection(ep); oc_tls_select_psk_ciphersuite(); oc_tls_use_pin_obt_psk_identity(); - if (oc_init_post("/oic/sec/pstat", ep, NULL, &obt_rdp_2, HIGH_QOS, o)) { + if (oc_init_post(OCF_SEC_PSTAT_URI, ep, NULL, &obt_rdp_2, HIGH_QOS, o)) { oc_rep_start_root_object(); oc_rep_set_int(root, om, 4); oc_rep_end_root_object(); diff --git a/security/oc_pstat.c b/security/oc_pstat.c index 82f93b2a2..988345c46 100644 --- a/security/oc_pstat.c +++ b/security/oc_pstat.c @@ -19,8 +19,10 @@ #ifdef OC_SECURITY #include "api/oc_core_res_internal.h" +#include "api/oc_helpers_internal.h" #include "api/oc_main_internal.h" #include "api/oc_message_buffer_internal.h" +#include "api/oc_resource_internal.h" #include "messaging/coap/coap_internal.h" #include "messaging/coap/observe_internal.h" #include "oc_acl_internal.h" @@ -446,9 +448,15 @@ oc_sec_is_operational(size_t device) } bool -oc_sec_pstat_is_in_dos_state(size_t device, unsigned dos_mask) +oc_sec_pstat_is_in_dos_state(const oc_sec_pstat_t *ps, unsigned dos_mask) { - return (OC_PSTAT_DOS_ID_FLAG(g_pstat[device].s) & dos_mask) != 0; + return (OC_PSTAT_DOS_ID_FLAG(ps->s) & dos_mask) != 0; +} + +bool +oc_device_is_in_dos_state(size_t device, unsigned dos_mask) +{ + return oc_sec_pstat_is_in_dos_state(&g_pstat[device], dos_mask); } void @@ -667,8 +675,9 @@ oc_sec_decode_pstat(const oc_rep_t *rep, bool from_storage, size_t device) return false; } -void -get_pstat(oc_request_t *request, oc_interface_mask_t iface_mask, void *data) +static void +pstat_resource_get(oc_request_t *request, oc_interface_mask_t iface_mask, + void *data) { (void)data; switch (iface_mask) { @@ -682,8 +691,9 @@ get_pstat(oc_request_t *request, oc_interface_mask_t iface_mask, void *data) } } -void -post_pstat(oc_request_t *request, oc_interface_mask_t iface_mask, void *data) +static void +pstat_resource_post(oc_request_t *request, oc_interface_mask_t iface_mask, + void *data) { (void)iface_mask; (void)data; @@ -796,4 +806,27 @@ oc_reset_devices_in_RFOTM(void) } } } + +void +oc_sec_pstat_create_resource(size_t device) +{ + oc_core_populate_resource( + OCF_SEC_PSTAT, device, OCF_SEC_PSTAT_URI, OC_IF_RW | OC_IF_BASELINE, + OC_IF_RW, OC_DISCOVERABLE | OC_OBSERVABLE, pstat_resource_get, + /*put*/ NULL, pstat_resource_post, /*delete*/ NULL, 1, OCF_SEC_PSTAT_RT); +} + +bool +oc_sec_is_pstat_resource_uri(oc_string_view_t uri) +{ + return oc_resource_match_uri(OC_STRING_VIEW(OCF_SEC_PSTAT_URI), uri); +} + +bool +oc_sec_pstat_is_owned_by(size_t device, oc_uuid_t uuid) +{ + const oc_sec_pstat_t *pstat = oc_sec_get_pstat(device); + return oc_uuid_is_equal(pstat->rowneruuid, uuid); +} + #endif /* OC_SECURITY */ diff --git a/security/oc_pstat_internal.h b/security/oc_pstat_internal.h index 1b49bb51c..eed608e16 100644 --- a/security/oc_pstat_internal.h +++ b/security/oc_pstat_internal.h @@ -19,6 +19,7 @@ #ifndef OC_PSTAT_INTERNAL_H #define OC_PSTAT_INTERNAL_H +#include "api/oc_helpers_internal.h" #include "oc_ri.h" #include "util/oc_compiler.h" @@ -29,12 +30,25 @@ extern "C" { #endif +#define OCF_SEC_PSTAT_URI "/oic/sec/pstat" +#define OCF_SEC_PSTAT_RT "oic.r.pstat" + +/** Create pstat (/oic/sec/pstat) resource for given device. */ +void oc_sec_pstat_create_resource(size_t device); + +/** Check if the URI matches the pstat resource URI (with or without the leading + * slash */ +bool oc_sec_is_pstat_resource_uri(oc_string_view_t uri); + +/** @brief Check if the pstat resource is owned by given UUID */ +bool oc_sec_pstat_is_owned_by(size_t device, oc_uuid_t uuid); + typedef enum { - OC_DOS_RESET = 0, - OC_DOS_RFOTM, - OC_DOS_RFPRO, - OC_DOS_RFNOP, - OC_DOS_SRESET + OC_DOS_RESET = 0, ///< Device reset + OC_DOS_RFOTM, ///< Ready for owner transfer method + OC_DOS_RFPRO, ///< Ready for provisioning + OC_DOS_RFNOP, ///< Ready for normal operation + OC_DOS_SRESET ///< Soft reset } oc_dostype_t; typedef enum { @@ -91,11 +105,6 @@ void oc_sec_pstat_copy(oc_sec_pstat_t *dst, const oc_sec_pstat_t *src); void oc_sec_pstat_clear(oc_sec_pstat_t *pstat, bool resetToDefault) OC_NONNULL(); -void get_pstat(oc_request_t *request, oc_interface_mask_t iface_mask, - void *data); -void post_pstat(oc_request_t *request, oc_interface_mask_t iface_mask, - void *data); - #ifdef OC_SOFTWARE_UPDATE void oc_sec_pstat_set_current_mode(size_t device, oc_dpmtype_t cm); @@ -105,6 +114,19 @@ oc_dpmtype_t oc_sec_pstat_current_mode(size_t device); #define OC_PSTAT_DOS_ID_FLAG(id) (1 << (id)) +/** + * @brief Check if the onboarding state property is in one of the DOS states. + * + * @param ps the pstat to check (cannot be NULL) + * @param dos_mask mask of DOS states to check (created by OR-ing values from + * OC_PSTAT_DOS_ID_FLAG(oc_dostype_t)) + * + * @return true if pstat is in one of the DOS states + * @return false otherwise + */ +bool oc_sec_pstat_is_in_dos_state(const oc_sec_pstat_t *ps, unsigned dos_mask) + OC_NONNULL(); + /** * @brief Check if device is in one of the DOS states. * @@ -114,7 +136,7 @@ oc_dpmtype_t oc_sec_pstat_current_mode(size_t device); * @return true if device is in one of the DOS states * @return false otherwise */ -bool oc_sec_pstat_is_in_dos_state(size_t device, unsigned dos_mask); +bool oc_device_is_in_dos_state(size_t device, unsigned dos_mask); /** * @brief Reset all devices in RFOTM state for shutdown. diff --git a/security/oc_roles.c b/security/oc_roles.c index c5809d303..bf5a790ae 100644 --- a/security/oc_roles.c +++ b/security/oc_roles.c @@ -22,6 +22,7 @@ #include "api/oc_core_res_internal.h" #include "api/oc_helpers_internal.h" +#include "api/oc_resource_internal.h" #include "port/oc_log_internal.h" #include "security/oc_cred_util_internal.h" #include "security/oc_roles_internal.h" @@ -409,4 +410,10 @@ oc_sec_roles_create_resource(size_t device) roles_resource_delete, 1, OCF_SEC_ROLES_RT); } +bool +oc_sec_is_roles_resource_uri(oc_string_view_t uri) +{ + return oc_resource_match_uri(OC_STRING_VIEW(OCF_SEC_ROLES_URI), uri); +} + #endif /* OC_SECURITY && OC_PKI */ diff --git a/security/oc_roles_internal.h b/security/oc_roles_internal.h index 7a48a2166..48bc9bdff 100644 --- a/security/oc_roles_internal.h +++ b/security/oc_roles_internal.h @@ -42,6 +42,8 @@ enum { OCF_SEC_ROLES_MAX_NUM = 2, }; +#define OCF_SEC_ROLE_OWNER "oic.role.owner" + /** * \defgroup server-roles Event timers * @@ -73,8 +75,13 @@ oc_sec_cred_t *oc_sec_roles_add(const oc_tls_peer_t *client, size_t device) */ void oc_sec_roles_create_resource(size_t device); +/** @brief Check if the URI matches the roles resource URI (with or without + * the leading slash */ +bool oc_sec_is_roles_resource_uri(oc_string_view_t uri); + /** - * @brief Remove role from the list of roles for given client and deallocate it. + * @brief Remove role from the list of roles for given client and deallocate + * it. * * @param role role to remove (cannot be NULL) * @param client client asserting the role (cannot be NULL) diff --git a/security/oc_security.c b/security/oc_security.c index 811fdd8b3..cfca32b33 100644 --- a/security/oc_security.c +++ b/security/oc_security.c @@ -82,34 +82,39 @@ oc_mbedtls_init(void) #endif /* OC_DBG_IS_ENABLED */ } -int -oc_sec_self_own(size_t device) +void +oc_sec_own_resources(size_t device, oc_uuid_t uuid) { - OC_DBG("performing self-onboarding of device(%zu)", device); - const oc_uuid_t *uuid = oc_core_get_device_id(device); - if (uuid == NULL) { - return -1; - } - oc_sec_acl_t *acl = oc_sec_get_acl(device); - memcpy(acl->rowneruuid.id, uuid->id, sizeof(uuid->id)); + memcpy(acl->rowneruuid.id, uuid.id, sizeof(uuid.id)); oc_sec_doxm_t *doxm = oc_sec_get_doxm(device); - memcpy(doxm->devowneruuid.id, uuid->id, sizeof(uuid->id)); - memcpy(doxm->deviceuuid.id, uuid->id, sizeof(uuid->id)); - memcpy(doxm->rowneruuid.id, uuid->id, sizeof(uuid->id)); + memcpy(doxm->devowneruuid.id, uuid.id, sizeof(uuid.id)); + memcpy(doxm->deviceuuid.id, uuid.id, sizeof(uuid.id)); + memcpy(doxm->rowneruuid.id, uuid.id, sizeof(uuid.id)); doxm->owned = true; doxm->oxmsel = 0; oc_sec_creds_t *creds = oc_sec_get_creds(device); - memcpy(creds->rowneruuid.id, uuid->id, sizeof(uuid->id)); + memcpy(creds->rowneruuid.id, uuid.id, sizeof(uuid.id)); oc_sec_pstat_t *ps = oc_sec_get_pstat(device); - memcpy(ps->rowneruuid.id, uuid->id, sizeof(uuid->id)); + memcpy(ps->rowneruuid.id, uuid.id, sizeof(uuid.id)); ps->tm = 0; ps->cm = 0; ps->isop = true; ps->s = OC_DOS_RFNOP; +} + +int +oc_sec_self_own(size_t device) +{ + OC_DBG("performing self-onboarding of device(%zu)", device); + const oc_uuid_t *uuid = oc_core_get_device_id(device); + if (uuid == NULL) { + return -1; + } + oc_sec_own_resources(device, *uuid); oc_sec_acl_add_bootstrap_acl(device); diff --git a/security/oc_security_internal.h b/security/oc_security_internal.h index 6c4cc7060..d4027a295 100644 --- a/security/oc_security_internal.h +++ b/security/oc_security_internal.h @@ -29,10 +29,8 @@ extern "C" { #endif -/** - * @brief Configure mbedTLS for IoTivity-lite. - */ -void oc_mbedtls_init(void); +/** Own secure resources by given uuid */ +void oc_sec_own_resources(size_t device, oc_uuid_t uuid); /** * @brief Perform self-onboarding. @@ -56,6 +54,11 @@ int oc_sec_self_own(size_t device); */ void oc_sec_self_disown(size_t device); +/** + * @brief Configure mbedTLS for IoTivity-lite. + */ +void oc_mbedtls_init(void); + #ifdef OC_HAS_FEATURE_PLGD_TIME /** diff --git a/security/oc_svr.c b/security/oc_svr.c index ed35991c8..fa55d2f07 100644 --- a/security/oc_svr.c +++ b/security/oc_svr.c @@ -48,14 +48,8 @@ oc_sec_svr_create(void) for (size_t i = 0; i < oc_core_get_num_devices(); i++) { oc_sec_doxm_create_resource(i); - oc_core_populate_resource(OCF_SEC_PSTAT, i, "/oic/sec/pstat", - OC_IF_RW | OC_IF_BASELINE, OC_IF_RW, - OC_DISCOVERABLE | OC_OBSERVABLE, get_pstat, 0, - post_pstat, 0, 1, "oic.r.pstat"); - oc_core_populate_resource(OCF_SEC_ACL, i, "/oic/sec/acl2", - 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_sec_pstat_create_resource(i); + oc_sec_acl_create_resource(i); 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, diff --git a/security/oc_tls.c b/security/oc_tls.c index 3d6592f29..92949f3d8 100644 --- a/security/oc_tls.c +++ b/security/oc_tls.c @@ -561,6 +561,13 @@ oc_tls_get_peer(const oc_endpoint_t *endpoint) return NULL; } +bool +oc_tls_peer_is_doc(const oc_endpoint_t *endpoint) +{ + const oc_tls_peer_t *peer = oc_tls_get_peer(endpoint); + return peer != NULL && peer->doc; +} + void oc_tls_remove_peer(const oc_endpoint_t *endpoint) { @@ -2720,18 +2727,12 @@ oc_tls_send_message(oc_message_t *message) bool oc_tls_uses_psk_cred(const oc_tls_peer_t *peer) { - if (!peer) { - return false; - } - if (!peer->ssl_ctx.session) { + if (peer == NULL || peer->ssl_ctx.session == NULL) { return false; } int cipher = peer->ssl_ctx.session->ciphersuite; - if (MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 == cipher || - MBEDTLS_TLS_ECDH_ANON_WITH_AES_128_CBC_SHA256 == cipher) { - return true; - } - return false; + return (MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 == cipher || + MBEDTLS_TLS_ECDH_ANON_WITH_AES_128_CBC_SHA256 == cipher); } oc_uuid_t * diff --git a/security/oc_tls_internal.h b/security/oc_tls_internal.h index d47e534f7..527e89341 100644 --- a/security/oc_tls_internal.h +++ b/security/oc_tls_internal.h @@ -192,6 +192,9 @@ oc_tls_peer_t *oc_tls_get_peer(const oc_endpoint_t *endpoint); */ oc_uuid_t *oc_tls_get_peer_uuid(const oc_endpoint_t *endpoint); +/** Check if peer for endpoint exists and it is the DOC connection */ +bool oc_tls_peer_is_doc(const oc_endpoint_t *endpoint); + /** * @brief Count the number of peers in the device. * @@ -220,6 +223,7 @@ bool oc_tls_connected(const oc_endpoint_t *endpoint); * @return -1 on error */ size_t oc_tls_send_message(oc_message_t *message); + bool oc_tls_uses_psk_cred(const oc_tls_peer_t *peer); /* Public APIs for selecting certificate credentials */ diff --git a/security/unittest/acltest.cpp b/security/unittest/acltest.cpp index a8d1b4884..621be3b8d 100644 --- a/security/unittest/acltest.cpp +++ b/security/unittest/acltest.cpp @@ -19,6 +19,7 @@ #ifdef OC_SECURITY #include "api/oc_core_res_internal.h" +#include "api/oc_discovery_internal.h" #include "api/oc_ri_internal.h" #include "api/oc_runtime_internal.h" #include "oc_acl.h" @@ -27,15 +28,44 @@ #include "oc_uuid.h" #include "port/oc_network_event_handler_internal.h" #include "security/oc_acl_internal.h" +#include "security/oc_acl_util_internal.h" #include "security/oc_pstat_internal.h" +#include "security/oc_security_internal.h" +#include "security/oc_svr_internal.h" +#include "security/oc_tls_internal.h" +#include "tests/gtest/Endpoint.h" +#include "tests/gtest/Resource.h" +#include "tests/gtest/tls/Peer.h" #include "util/oc_list.h" +#ifdef OC_PKI +#include "security/oc_certs_internal.h" +#include "security/oc_obt_internal.h" +#include "security/oc_roles_internal.h" +#include "tests/gtest/KeyPair.h" +#include "tests/gtest/PKI.h" +#include "tests/gtest/Role.h" +#endif /* OC_PKI */ + +#ifdef OC_SOFTWARE_UPDATE +#include "api/oc_swupdate_internal.h" +#endif /* OC_SOFTWARE_UPDATE */ + +#ifdef OC_HAS_FEATURE_PLGD_TIME +#include "api/plgd/plgd_time_internal.h" +#endif /* OC_HAS_FEATURE_PLGD_TIME */ + #ifdef OC_HAS_FEATURE_PUSH #include "api/oc_push_internal.h" #endif /* OC_HAS_FEATURE_PUSH */ -#include "gtest/gtest.h" +#include +#include +#include #include +#include + +static constexpr size_t kDeviceID = 0; static const std::string kDeviceURI{ "/oic/d" }; static const std::string kDeviceType{ "oic.d.light" }; @@ -45,37 +75,97 @@ static const std::string kOCFSpecVersion{ "ocf.1.0.0" }; static const std::string kOCFDataModelVersion{ "ocf.res.1.0.0" }; class TestAcl : public testing::Test { -protected: +public: + static void SetUpTestCase() + { + oc_random_init(); +#if defined(OC_DYNAMIC_ALLOCATION) && defined(OC_PKI) + g_root_keypair = oc::GetECPKeyPair(MBEDTLS_ECP_DP_SECP256R1); +#endif /* OC_DYNAMIC_ALLOCATION && OC_PKI */ + oc_set_con_res_announced(true); + } + + static void TearDownTestCase() { oc_random_destroy(); } + void SetUp() override { oc_network_event_handler_mutex_init(); oc_runtime_init(); oc_ri_init(); oc_core_init(); + ASSERT_EQ(0, oc_init_platform("plgd", nullptr, nullptr)); ASSERT_EQ(0, oc_add_device(kDeviceURI.c_str(), kDeviceType.c_str(), kDeviceName.c_str(), kOCFSpecVersion.c_str(), kOCFDataModelVersion.c_str(), nullptr, nullptr)); - device_id_ = 0; - oc_sec_acl_init(); +#ifdef OC_HAS_FEATURE_PLGD_TIME + plgd_time_create_resource(); +#endif /* OC_HAS_FEATURE_PLGD_TIME */ + oc_sec_svr_create(); +#ifdef OC_SOFTWARE_UPDATE + oc_swupdate_create(); +#endif /* OC_SOFTWARE_UPDATE */ + + oc_mbedtls_init(); + +#if defined(OC_DYNAMIC_ALLOCATION) && defined(OC_PKI) + g_root_credid = oc::pki::obt::GenerateSelfSignedRootCertificate( + kDeviceID, g_root_subject, g_root_keypair); + ASSERT_GT(g_root_credid, 0); +#endif /* OC_DYNAMIC_ALLOCATION && OC_PKI */ } void TearDown() override { +#ifdef OC_SOFTWARE_UPDATE + oc_swupdate_free(); +#endif /* OC_SOFTWARE_UPDATE */ + oc_sec_svr_free(); #ifdef OC_HAS_FEATURE_PUSH oc_push_free(); #endif /* OC_HAS_FEATURE_PUSH */ - oc_sec_acl_free(); - oc_connectivity_shutdown(device_id_); + oc_connectivity_shutdown(kDeviceID); oc_core_shutdown(); oc_ri_shutdown(); oc_runtime_shutdown(); oc_network_event_handler_mutex_destroy(); } -public: - size_t device_id_; + template + static std::vector getResources(size_t device, Filter filter) + { + std::vector resources; + for (int i = OCF_P; i <= OCF_D; ++i) { + oc_resource_t *resource = oc_core_get_resource_by_index(i, device); + EXPECT_NE(nullptr, resource); + if (filter(device, resource)) { + resources.push_back(resource); + } + } + return resources; + } + + static std::vector getSVRs(size_t device) + { + return getResources(device, [](size_t dev, const oc_resource_t *resource) { + return oc_core_is_SVR(resource, dev); + }); + } + +#if defined(OC_DYNAMIC_ALLOCATION) && defined(OC_PKI) + static std::string g_root_subject_name; + static std::string g_root_subject; + static oc::keypair_t g_root_keypair; + static int g_root_credid; +#endif /* OC_DYNAMIC_ALLOCATION && OC_PKI */ }; +#if defined(OC_DYNAMIC_ALLOCATION) && defined(OC_PKI) +std::string TestAcl::g_root_subject_name{ "IoTivity-Lite Test" }; +std::string TestAcl::g_root_subject{ "C=US, O=OCF, CN=" + g_root_subject_name }; +oc::keypair_t TestAcl::g_root_keypair{}; +int TestAcl::g_root_credid{ -1 }; +#endif /* OC_DYNAMIC_ALLOCATION && OC_PKI */ + static size_t oc_sec_ace_count(size_t device) { @@ -90,87 +180,81 @@ oc_sec_ace_count(size_t device) TEST_F(TestAcl, oc_sec_acl_add_bootstrap_acl) { - EXPECT_EQ(true, oc_sec_acl_add_bootstrap_acl(device_id_)); - const oc_sec_acl_t *acl = oc_sec_get_acl(device_id_); + EXPECT_EQ(true, oc_sec_acl_add_bootstrap_acl(kDeviceID)); + const oc_sec_acl_t *acl = oc_sec_get_acl(kDeviceID); EXPECT_NE(nullptr, acl); - EXPECT_EQ(1, oc_sec_ace_count(device_id_)); + EXPECT_EQ(1, oc_sec_ace_count(kDeviceID)); } TEST_F(TestAcl, oc_sec_acl_clear) { - oc_ace_subject_t anon_clear; - memset(&anon_clear, 0, sizeof(oc_ace_subject_t)); + oc_ace_subject_t anon_clear{}; anon_clear.conn = OC_CONN_ANON_CLEAR; EXPECT_EQ(true, oc_sec_ace_update_res(OC_SUBJECT_CONN, &anon_clear, -1, OC_PERM_RETRIEVE, nullptr, "/test/a", - OC_ACE_NO_WC, device_id_, nullptr)); + OC_ACE_NO_WC, kDeviceID, nullptr)); - oc_uuid_t uuid = { { 0 } }; + oc_uuid_t uuid{}; oc_gen_uuid(&uuid); - oc_ace_subject_t subject; - memset(&subject, 0, sizeof(oc_ace_subject_t)); - memcpy(&subject.uuid, &uuid, sizeof(oc_uuid_t)); - EXPECT_EQ(true, oc_sec_ace_update_res(OC_SUBJECT_UUID, &subject, -1, + oc_ace_subject_t subject_uuid{}; + subject_uuid.uuid = uuid; + EXPECT_EQ(true, oc_sec_ace_update_res(OC_SUBJECT_UUID, &subject_uuid, -1, OC_PERM_UPDATE, nullptr, "/test/b", - OC_ACE_NO_WC, device_id_, nullptr)); - - memset(&subject, 0, sizeof(oc_ace_subject_t)); - std::string testRole{ "test.role" }; - std::string testAuthority{ "test.authority" }; - oc_new_string(&subject.role.role, testRole.c_str(), testRole.length()); - oc_new_string(&subject.role.authority, testAuthority.c_str(), - testAuthority.length()); - EXPECT_EQ(true, oc_sec_ace_update_res(OC_SUBJECT_ROLE, &subject, -1, + OC_ACE_NO_WC, kDeviceID, nullptr)); + + oc_ace_subject_t subject_role{}; + auto testRole = OC_STRING_LOCAL("test.role"); + auto testAuthority = OC_STRING_LOCAL("test.authority"); + subject_role.role = { testRole, testAuthority }; + EXPECT_EQ(true, oc_sec_ace_update_res(OC_SUBJECT_ROLE, &subject_role, -1, OC_PERM_NOTIFY, nullptr, "/test/c", - OC_ACE_NO_WC, device_id_, nullptr)); - EXPECT_EQ(3, oc_sec_ace_count(device_id_)); + OC_ACE_NO_WC, kDeviceID, nullptr)); + EXPECT_EQ(3, oc_sec_ace_count(kDeviceID)); oc_sec_acl_clear( - device_id_, [](const oc_sec_ace_t *, void *) { return false; }, nullptr); - EXPECT_EQ(3, oc_sec_ace_count(device_id_)); + kDeviceID, [](const oc_sec_ace_t *, void *) { return false; }, nullptr); + EXPECT_EQ(3, oc_sec_ace_count(kDeviceID)); oc_sec_ace_t *ace = - oc_sec_acl_find_subject(nullptr, OC_SUBJECT_CONN, &anon_clear, /*aceid*/ -1, + oc_sec_acl_find_subject(nullptr, OC_SUBJECT_CONN, &anon_clear, /*aceid*/ + -1, /*permission*/ 0, /*tag*/ nullptr, - /*match_tag*/ false, device_id_); + /*match_tag*/ false, kDeviceID); EXPECT_NE(nullptr, ace); oc_sec_acl_clear( - device_id_, + kDeviceID, [](const oc_sec_ace_t *entry, void *) { return entry->subject_type == OC_SUBJECT_CONN; }, nullptr); - EXPECT_EQ(2, oc_sec_ace_count(device_id_)); - ace = - oc_sec_acl_find_subject(nullptr, OC_SUBJECT_CONN, &anon_clear, /*aceid*/ -1, - /*permission*/ 0, /*tag*/ nullptr, - /*match_tag*/ false, device_id_); + EXPECT_EQ(2, oc_sec_ace_count(kDeviceID)); + ace = oc_sec_acl_find_subject(nullptr, OC_SUBJECT_CONN, &anon_clear, /*aceid*/ + -1, + /*permission*/ 0, /*tag*/ nullptr, + /*match_tag*/ false, kDeviceID); EXPECT_EQ(nullptr, ace); ace = - oc_sec_acl_find_subject(nullptr, OC_SUBJECT_ROLE, &subject, + oc_sec_acl_find_subject(nullptr, OC_SUBJECT_ROLE, &subject_role, /*aceid*/ -1, /*permission*/ 0, - /*tag*/ nullptr, /*match_tag*/ false, device_id_); + /*tag*/ nullptr, /*match_tag*/ false, kDeviceID); EXPECT_NE(nullptr, ace); int aceid{ ace->aceid }; oc_sec_acl_clear( - device_id_, + kDeviceID, [](const oc_sec_ace_t *entry, void *data) { const auto *id = static_cast(data); return entry->aceid == *id; }, &aceid); ace = - oc_sec_acl_find_subject(nullptr, OC_SUBJECT_ROLE, &subject, + oc_sec_acl_find_subject(nullptr, OC_SUBJECT_ROLE, &subject_role, /*aceid*/ -1, /*permission*/ 0, - /*tag*/ nullptr, /*match_tag*/ false, device_id_); + /*tag*/ nullptr, /*match_tag*/ false, kDeviceID); EXPECT_EQ(nullptr, ace); - oc_sec_acl_clear(device_id_, nullptr, nullptr); - EXPECT_EQ(0, oc_sec_ace_count(device_id_)); - - oc_free_string(&subject.role.authority); - oc_free_string(&subject.role.role); + oc_sec_acl_clear(kDeviceID, nullptr, nullptr); + ASSERT_EQ(0, oc_sec_ace_count(kDeviceID)); } #ifdef OC_HAS_FEATURE_RESOURCE_ACCESS_IN_RFOTM @@ -185,8 +269,7 @@ onGet(oc_request_t *, oc_interface_mask_t, void *) TEST_F(TestAcl, oc_sec_check_acl_in_RFOTM) { - oc_sec_pstat_init(); - oc_sec_pstat_t *pstat = oc_sec_get_pstat(device_id_); + oc_sec_pstat_t *pstat = oc_sec_get_pstat(kDeviceID); EXPECT_NE(nullptr, pstat); pstat->s = OC_DOS_RFOTM; oc_resource_t *res = @@ -219,4 +302,688 @@ TEST_F(TestAcl, oc_sec_check_acl_in_RFOTM) } #endif /* OC_HAS_FEATURE_RESOURCE_ACCESS_IN_RFOTM */ +TEST_F(TestAcl, oc_sec_check_acl_FailInsecureDOC) +{ + oc_sec_pstat_t *pstat = oc_sec_get_pstat(kDeviceID); + ASSERT_NE(nullptr, pstat); + pstat->s = OC_DOS_RFOTM; + + auto tlsPeer = + oc::tls::MakePeer("coap://[ff02::41]:1336", MBEDTLS_SSL_IS_CLIENT); + oc_endpoint_t ep = oc::endpoint::FromString(tlsPeer.address); + ASSERT_EQ(0, ep.flags & SECURED); + ep.device = kDeviceID; + oc_tls_peer_t *peer = oc_tls_add_or_get_peer(&ep, tlsPeer.role, nullptr); + ASSERT_NE(nullptr, peer); + ASSERT_EQ(1, oc_tls_num_peers(kDeviceID)); + oc_resource_t resource{}; + resource.device = kDeviceID; + EXPECT_FALSE(oc_sec_check_acl(OC_GET, &resource, &ep)); + + oc_tls_remove_peer(&ep); +} + +#ifdef OC_HAS_FEATURE_RESOURCE_ACCESS_IN_RFOTM + +TEST_F(TestAcl, oc_sec_check_acl_AccessInRFOTM) +{ + oc_sec_pstat_t *pstat = oc_sec_get_pstat(kDeviceID); + ASSERT_NE(nullptr, pstat); + pstat->s = OC_DOS_RFOTM; + + oc_resource_t resource{}; + resource.device = kDeviceID; + ASSERT_TRUE( + oc::SetAccessInRFOTM(&resource, false, OC_PERM_RETRIEVE | OC_PERM_UPDATE)); + oc_endpoint_t ep{}; + ep.device = kDeviceID; + EXPECT_TRUE(oc_sec_check_acl(OC_GET, &resource, &ep)); +} + +#endif /* OC_HAS_FEATURE_RESOURCE_ACCESS_IN_RFOTM */ + +TEST_F(TestAcl, oc_sec_check_acl_FailNCRInNonRFNOP) +{ + oc_sec_pstat_t *pstat = oc_sec_get_pstat(kDeviceID); + ASSERT_NE(nullptr, pstat); + + oc_resource_t resource{}; + resource.device = kDeviceID; + ASSERT_FALSE(oc_core_is_DCR(&resource, kDeviceID)); + oc_endpoint_t ep{}; + ep.device = kDeviceID; + // device in non-RFNOP cannot access NCRs + std::vector states{ OC_DOS_RESET, OC_DOS_RFOTM, OC_DOS_RFPRO, + OC_DOS_SRESET }; + for (auto state : states) { + pstat->s = state; + EXPECT_FALSE(oc_sec_check_acl(OC_GET, &resource, &ep)); + } +} + +TEST_F(TestAcl, oc_sec_check_acl_FailInsecureAccessToVerticalResource) +{ + oc_sec_pstat_t *pstat = oc_sec_get_pstat(kDeviceID); + ASSERT_NE(nullptr, pstat); + pstat->s = OC_DOS_RFNOP; + oc_resource_t resource{}; + resource.device = kDeviceID; + ASSERT_FALSE(oc_core_is_DCR(&resource, kDeviceID)); + ASSERT_TRUE(oc_core_is_vertical_resource(&resource, kDeviceID)); + oc_endpoint_t ep{}; + ep.device = kDeviceID; + EXPECT_FALSE(oc_sec_check_acl(OC_GET, &resource, &ep)); +} + +TEST_F(TestAcl, oc_sec_check_acl_DOCAccessToDCR) +{ + // DCR + oc_resource_t *resource = oc_core_get_resource_by_index(OCF_P, kDeviceID); + ASSERT_NE(nullptr, resource); + ASSERT_TRUE(oc_core_is_DCR(resource, kDeviceID)); + // DOC peer + oc_sec_pstat_t *pstat = oc_sec_get_pstat(kDeviceID); + ASSERT_NE(nullptr, pstat); + pstat->s = OC_DOS_RFOTM; + auto tlsPeer = + oc::tls::MakePeer("coaps://[ff02::41]:1336", MBEDTLS_SSL_IS_CLIENT); + oc_endpoint_t ep = oc::endpoint::FromString(tlsPeer.address); + ASSERT_NE(0, ep.flags & SECURED); + ep.device = kDeviceID; + oc_tls_peer_t *peer = oc_tls_add_or_get_peer(&ep, tlsPeer.role, nullptr); + ASSERT_NE(nullptr, peer); + ASSERT_TRUE(peer->doc); + ASSERT_EQ(1, oc_tls_num_peers(kDeviceID)); + + EXPECT_TRUE(oc_sec_check_acl(OC_GET, resource, &ep)); + + oc_tls_remove_peer(&ep); +} + +TEST_F(TestAcl, oc_sec_check_acl_GETinRFOTM) +{ + // oic/d, oic/p and oic/res are accessible to GET requests in RFOTM + oc_sec_pstat_t *pstat = oc_sec_get_pstat(kDeviceID); + ASSERT_NE(nullptr, pstat); + pstat->s = OC_DOS_RFOTM; + + std::vector resources{ OCF_P, OCF_D, OCF_RES }; +#ifdef OC_HAS_FEATURE_PLGD_TIME + resources.push_back(PLGD_TIME); +#endif /* OC_HAS_FEATURE_PLGD_TIME */ +#ifdef OC_WKCORE + resources.push_back(WELLKNOWNCORE); +#endif /* OC_WKCORE */ + + for (auto type : resources) { + oc_resource_t *resource = oc_core_get_resource_by_index(type, kDeviceID); + ASSERT_NE(nullptr, resource); + oc_endpoint_t ep{}; + ep.device = kDeviceID; + EXPECT_TRUE(oc_sec_check_acl(OC_GET, resource, &ep)); + } +} + +TEST_F(TestAcl, oc_sec_check_acl_PriorToDOCAccessToDoxmInRFOTM) +{ + oc_sec_pstat_t *pstat = oc_sec_get_pstat(kDeviceID); + ASSERT_NE(nullptr, pstat); + pstat->s = OC_DOS_RFOTM; + oc_resource_t *resource = + oc_core_get_resource_by_index(OCF_SEC_DOXM, kDeviceID); + ASSERT_NE(nullptr, resource); + oc_endpoint_t ep{}; + ep.device = kDeviceID; + std::vector methods{ OC_GET, OC_POST, OC_PUT, OC_DELETE }; + for (auto method : methods) { + EXPECT_TRUE(oc_sec_check_acl(method, resource, &ep)); + } +} + +TEST_F(TestAcl, oc_sec_check_acl_PriorToDOCGetAccessToPstatInRFOTM) +{ + oc_sec_pstat_t *pstat = oc_sec_get_pstat(kDeviceID); + ASSERT_NE(nullptr, pstat); + pstat->s = OC_DOS_RFOTM; + oc_resource_t *resource = + oc_core_get_resource_by_index(OCF_SEC_PSTAT, kDeviceID); + ASSERT_NE(nullptr, resource); + oc_endpoint_t ep{}; + ep.device = kDeviceID; + EXPECT_TRUE(oc_sec_check_acl(OC_GET, resource, &ep)); + std::vector methods{ OC_POST, OC_PUT, OC_DELETE }; + for (auto method : methods) { + EXPECT_FALSE(oc_sec_check_acl(method, resource, &ep)); + } +} + +TEST_F(TestAcl, oc_sec_check_acl_FailInsecureAccessToSecurityVerticalResource) +{ + oc_sec_pstat_t *pstat = oc_sec_get_pstat(kDeviceID); + ASSERT_NE(nullptr, pstat); + + /* anon-clear requests to SVRs while the dos is RFPRO, RFNOP or SRESET should + * not be authorized regardless of the ACL configuration */ + + auto svrs = getSVRs(kDeviceID); + for (auto svr : svrs) { + ASSERT_TRUE(oc_core_is_SVR(svr, kDeviceID)); + oc_endpoint_t ep{}; + ep.device = kDeviceID; + ASSERT_EQ(0, ep.flags & SECURED); + + pstat->s = OC_DOS_RFPRO; + EXPECT_FALSE(oc_sec_check_acl(OC_GET, svr, &ep)); + pstat->s = OC_DOS_RFNOP; + EXPECT_FALSE(oc_sec_check_acl(OC_GET, svr, &ep)); + pstat->s = OC_DOS_SRESET; + EXPECT_FALSE(oc_sec_check_acl(OC_GET, svr, &ep)); + } +} + +TEST_F(TestAcl, oc_sec_check_acl_AccessByOwner) +{ + oc_uuid_t uuid{}; + oc_gen_uuid(&uuid); + auto tlsPeer = + oc::tls::MakePeer("coaps://[ff02::41]:1336", MBEDTLS_SSL_IS_CLIENT); + oc_endpoint_t ep = oc::endpoint::FromString(tlsPeer.address); + ASSERT_NE(0, ep.flags & SECURED); + ep.device = kDeviceID; + ep.di = uuid; + + oc_sec_own_resources(kDeviceID, uuid); + struct input_t + { + oc_resource_t *resource; + oc_method_t method; + }; + std::vector inputs{ + { + oc_core_get_resource_by_index(OCF_SEC_ACL, kDeviceID), + OC_GET, + }, + { + oc_core_get_resource_by_index(OCF_SEC_CRED, kDeviceID), + OC_POST, + }, + { + oc_core_get_resource_by_index(OCF_SEC_DOXM, kDeviceID), + OC_PUT, + }, + { + oc_core_get_resource_by_index(OCF_SEC_PSTAT, kDeviceID), + OC_DELETE, + }, + }; + for (auto input : inputs) { + EXPECT_TRUE(oc_sec_check_acl(input.method, input.resource, &ep)); + } + + // when owned by different uuid, it should fail + oc_uuid_t uuid2{}; + do { + oc_gen_uuid(&uuid2); + } while (oc_uuid_is_equal(uuid, uuid2)); + ep.di = uuid2; + for (auto input : inputs) { + EXPECT_FALSE(oc_sec_check_acl(input.method, input.resource, &ep)); + } +} + +#ifdef OC_PKI + +TEST_F(TestAcl, oc_sec_check_acl_AccessToRoles) +{ + auto tlsPeer = + oc::tls::MakePeer("coaps://[ff02::41]:1336", MBEDTLS_SSL_IS_CLIENT); + oc_endpoint_t ep = oc::endpoint::FromString(tlsPeer.address); + ASSERT_NE(0, ep.flags & SECURED); + ep.device = kDeviceID; + + oc_sec_pstat_t *pstat = oc_sec_get_pstat(kDeviceID); + ASSERT_NE(nullptr, pstat); + + oc_resource_t *roles = + oc_core_get_resource_by_index(OCF_SEC_ROLES, kDeviceID); + // peer has implicit access to /oic/sec/roles in RFPRO, RFNOP, SRESET + ASSERT_NE(nullptr, roles); + pstat->s = OC_DOS_RFPRO; + EXPECT_TRUE(oc_sec_check_acl(OC_GET, roles, &ep)); + pstat->s = OC_DOS_RFNOP; + EXPECT_TRUE(oc_sec_check_acl(OC_POST, roles, &ep)); + pstat->s = OC_DOS_SRESET; + EXPECT_TRUE(oc_sec_check_acl(OC_DELETE, roles, &ep)); + + // but not in RESET and RFOTM + pstat->s = OC_DOS_RESET; + EXPECT_FALSE(oc_sec_check_acl(OC_GET, roles, &ep)); + pstat->s = OC_DOS_RFOTM; + EXPECT_FALSE(oc_sec_check_acl(OC_GET, roles, &ep)); +} + +#endif /* OC_PKI */ + +static void +assertUnauthorizedAccessToResource(const oc_resource_t *resource, size_t device, + const oc_endpoint_t *ep, bool isSVR) +{ + ASSERT_EQ(isSVR, oc_core_is_SVR(resource, device)); + ASSERT_FALSE(oc_sec_check_acl(OC_GET, resource, ep)); + ASSERT_FALSE(oc_sec_check_acl(OC_POST, resource, ep)); + ASSERT_FALSE(oc_sec_check_acl(OC_PUT, resource, ep)); + ASSERT_FALSE(oc_sec_check_acl(OC_DELETE, resource, ep)); +} + +static void +checkAccessToResource(const oc_resource_t *resource, const oc_endpoint_t *ep, + bool allowGet = true, bool allowPost = true, + bool allowPut = true, bool allowDelete = true) +{ + EXPECT_EQ(allowGet, oc_sec_check_acl(OC_GET, resource, ep)); + EXPECT_EQ(allowPost, oc_sec_check_acl(OC_POST, resource, ep)); + EXPECT_EQ(allowPut, oc_sec_check_acl(OC_PUT, resource, ep)); + EXPECT_EQ(allowDelete, oc_sec_check_acl(OC_DELETE, resource, ep)); +} + +TEST_F(TestAcl, oc_sec_check_acl_AccessToSVRBySubject) +{ + oc_uuid_t uuid{}; + oc_gen_uuid(&uuid); + auto tlsPeer = + oc::tls::MakePeer("coaps://[ff02::41]:1336", MBEDTLS_SSL_IS_CLIENT); + oc_endpoint_t ep = oc::endpoint::FromString(tlsPeer.address); + ASSERT_NE(0, ep.flags & SECURED); + ep.device = kDeviceID; + ep.di = uuid; + + oc_tls_peer_t *peer = oc_tls_add_or_get_peer(&ep, tlsPeer.role, nullptr); + ASSERT_NE(nullptr, peer); + ASSERT_EQ(1, oc_tls_num_peers(kDeviceID)); + + oc_sec_pstat_t *pstat = oc_sec_get_pstat(kDeviceID); + ASSERT_NE(nullptr, pstat); + pstat->s = OC_DOS_RFOTM; + + // we use doxm to represent all SVRs + auto *doxm = oc_core_get_resource_by_index(OCF_SEC_DOXM, kDeviceID); + ASSERT_NE(nullptr, doxm); + assertUnauthorizedAccessToResource(doxm, kDeviceID, &ep, true); + + oc_ace_subject_t subject{}; + memcpy(&subject.uuid, &uuid, sizeof(oc_uuid_t)); + + // allowing retrieve or notify should allow GET access + for (auto perm : std::vector( + { OC_PERM_RETRIEVE, OC_PERM_NOTIFY })) { + ASSERT_EQ(true, oc_sec_ace_update_res(OC_SUBJECT_UUID, &subject, -1, perm, + nullptr, oc_string(doxm->uri), + OC_ACE_NO_WC, kDeviceID, nullptr)); + checkAccessToResource(doxm, &ep, true, false, false, false); + EXPECT_FALSE(oc_sec_check_acl(OC_FETCH, doxm, &ep)); + oc_sec_acl_clear(kDeviceID, nullptr, nullptr); + } + + // allowing create or update should allow POST and PUT access + for (auto perm : + std::vector({ OC_PERM_CREATE, OC_PERM_UPDATE })) { + ASSERT_EQ(true, oc_sec_ace_update_res(OC_SUBJECT_UUID, &subject, -1, perm, + nullptr, oc_string(doxm->uri), + OC_ACE_NO_WC, kDeviceID, nullptr)); + checkAccessToResource(doxm, &ep, false, true, true, false); + EXPECT_FALSE(oc_sec_check_acl(OC_FETCH, doxm, &ep)); + oc_sec_acl_clear(kDeviceID, nullptr, nullptr); + } + + // allowing delete should allow DELETE access + ASSERT_EQ(true, oc_sec_ace_update_res( + OC_SUBJECT_UUID, &subject, -1, OC_PERM_DELETE, nullptr, + oc_string(doxm->uri), OC_ACE_NO_WC, kDeviceID, nullptr)); + checkAccessToResource(doxm, &ep, false, false, false, true); + EXPECT_FALSE(oc_sec_check_acl(OC_FETCH, doxm, &ep)); + oc_sec_acl_clear(kDeviceID, nullptr, nullptr); + + oc_tls_remove_peer(&ep); +} + +TEST_F(TestAcl, oc_sec_check_acl_AccessToSVRByPSK) +{ + oc_uuid_t uuid{}; + oc_gen_uuid(&uuid); + auto tlsPeer = + oc::tls::MakePeer("coaps://[ff02::41]:1336", MBEDTLS_SSL_IS_CLIENT); + oc_endpoint_t ep = oc::endpoint::FromString(tlsPeer.address); + ASSERT_NE(0, ep.flags & SECURED); + ep.device = kDeviceID; + ep.di = uuid; + + oc_tls_peer_t *peer = oc_tls_add_or_get_peer(&ep, tlsPeer.role, nullptr); + ASSERT_NE(nullptr, peer); + ASSERT_EQ(1, oc_tls_num_peers(kDeviceID)); + // pretend that we have a PSK session + mbedtls_ssl_session session{}; + session.ciphersuite = MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256; + peer->ssl_ctx.session = &session; + ASSERT_TRUE(oc_tls_uses_psk_cred(peer)); + + oc_sec_pstat_t *pstat = oc_sec_get_pstat(kDeviceID); + ASSERT_NE(nullptr, pstat); + pstat->s = OC_DOS_RFOTM; + + // we use doxm to represent all SVRs + auto *doxm = oc_core_get_resource_by_index(OCF_SEC_DOXM, kDeviceID); + ASSERT_NE(nullptr, doxm); + assertUnauthorizedAccessToResource(doxm, kDeviceID, &ep, true); + + // add PSK cred + std::vector pin{ '1', '2', '3' }; + // 16 = SYMMETRIC_KEY_128BIT_LEN + std::array key{}; + ASSERT_EQ(0, oc_tls_pbkdf2(pin.data(), pin.size(), &uuid, 100, + MBEDTLS_MD_SHA256, &key[0], key.size())); + std::array uuid_str{}; + ASSERT_NE(-1, oc_uuid_to_str_v1(&uuid, &uuid_str[0], uuid_str.size())); + oc_sec_encoded_data_t privatedata = { key.data(), key.size(), + OC_ENCODING_RAW }; + auto role = OC_STRING_LOCAL("role"); + auto authority = OC_STRING_LOCAL("authority"); + int credid = oc_sec_add_new_cred( + kDeviceID, false, nullptr, -1, OC_CREDTYPE_PSK, OC_CREDUSAGE_NULL, + uuid_str.data(), privatedata, { nullptr, 0, OC_ENCODING_UNSUPPORTED }, + oc_string_view2(&role), oc_string_view2(&authority), OC_STRING_VIEW_NULL, + nullptr); + ASSERT_NE(-1, credid); + + // allowing retrieve or notify should allow GET access + for (auto perm : std::vector( + { OC_PERM_RETRIEVE, OC_PERM_NOTIFY })) { + oc_ace_subject_t subject{}; + subject.role = { role, authority }; + ASSERT_EQ(true, oc_sec_ace_update_res(OC_SUBJECT_ROLE, &subject, -1, perm, + nullptr, oc_string(doxm->uri), + OC_ACE_NO_WC, kDeviceID, nullptr)); + checkAccessToResource(doxm, &ep, true, false, false, false); + EXPECT_FALSE(oc_sec_check_acl(OC_FETCH, doxm, &ep)); + oc_sec_acl_clear(kDeviceID, nullptr, nullptr); + } + + // allowing create or update should allow POST and PUT access + for (auto perm : + std::vector({ OC_PERM_CREATE, OC_PERM_UPDATE })) { + oc_ace_subject_t subject{}; + subject.role = { role, authority }; + ASSERT_EQ(true, oc_sec_ace_update_res(OC_SUBJECT_ROLE, &subject, -1, perm, + nullptr, oc_string(doxm->uri), + OC_ACE_NO_WC, kDeviceID, nullptr)); + checkAccessToResource(doxm, &ep, false, true, true, false); + EXPECT_FALSE(oc_sec_check_acl(OC_FETCH, doxm, &ep)); + oc_sec_acl_clear(kDeviceID, nullptr, nullptr); + } + + // allowing delete should allow DELETE access + oc_ace_subject_t subject{}; + subject.role = { role, authority }; + ASSERT_EQ(true, oc_sec_ace_update_res( + OC_SUBJECT_ROLE, &subject, -1, OC_PERM_DELETE, nullptr, + oc_string(doxm->uri), OC_ACE_NO_WC, kDeviceID, nullptr)); + checkAccessToResource(doxm, &ep, false, false, false, true); + EXPECT_FALSE(oc_sec_check_acl(OC_FETCH, doxm, &ep)); + oc_sec_acl_clear(kDeviceID, nullptr, nullptr); + + peer->ssl_ctx.session = nullptr; + oc_tls_remove_peer(&ep); +} + +#if defined(OC_DYNAMIC_ALLOCATION) && defined(OC_PKI) + +TEST_F(TestAcl, oc_sec_check_acl_AccessToSVRByOwnerRoleCred) +{ + oc_uuid_t uuid{}; + oc_gen_uuid(&uuid); + auto tlsPeer = + oc::tls::MakePeer("coaps://[ff02::41]:1336", MBEDTLS_SSL_IS_CLIENT); + oc_endpoint_t ep = oc::endpoint::FromString(tlsPeer.address); + ASSERT_NE(0, ep.flags & SECURED); + ep.device = kDeviceID; + ep.di = uuid; + + oc_tls_peer_t *peer = oc_tls_add_or_get_peer(&ep, tlsPeer.role, nullptr); + ASSERT_NE(nullptr, peer); + peer->uuid = uuid; + ASSERT_EQ(1, oc_tls_num_peers(kDeviceID)); + + oc_sec_pstat_t *pstat = oc_sec_get_pstat(kDeviceID); + ASSERT_NE(nullptr, pstat); + pstat->s = OC_DOS_RFOTM; + + // we use doxm to represent all SVRs + auto *doxm = oc_core_get_resource_by_index(OCF_SEC_DOXM, kDeviceID); + ASSERT_NE(nullptr, doxm); + assertUnauthorizedAccessToResource(doxm, kDeviceID, &ep, true); + + std::array uuid_buf{}; + ASSERT_TRUE( + oc_certs_encode_CN_with_UUID(&uuid, uuid_buf.data(), uuid_buf.size())); + + oc::Roles roles{}; + roles.Add(OCF_SEC_ROLE_OWNER, "owner"); + + auto role_pem = oc::pki::obt::GenerateRoleCertificate( + uuid_buf.data(), g_root_subject, g_root_keypair, roles); + ASSERT_FALSE(role_pem.empty()); + + oc_sec_encoded_data_t publicdata = { role_pem.data(), role_pem.size() - 1, + OC_ENCODING_PEM }; + int credid = oc_sec_add_new_cred( + kDeviceID, true, peer, -1, OC_CREDTYPE_CERT, OC_CREDUSAGE_ROLE_CERT, "*", + { nullptr, 0, OC_ENCODING_UNSUPPORTED }, publicdata, OC_STRING_VIEW_NULL, + OC_STRING_VIEW_NULL, OC_STRING_VIEW_NULL, nullptr); + ASSERT_NE(-1, credid); + checkAccessToResource(doxm, &ep); + + oc_tls_remove_peer(&ep); +} + +TEST_F(TestAcl, oc_sec_check_acl_AccessToSVRByNonOwnerRoleCred) +{ + oc_uuid_t uuid{}; + oc_gen_uuid(&uuid); + auto tlsPeer = + oc::tls::MakePeer("coaps://[ff02::41]:1336", MBEDTLS_SSL_IS_CLIENT); + oc_endpoint_t ep = oc::endpoint::FromString(tlsPeer.address); + ASSERT_NE(0, ep.flags & SECURED); + ep.device = kDeviceID; + ep.di = uuid; + + oc_tls_peer_t *peer = oc_tls_add_or_get_peer(&ep, tlsPeer.role, nullptr); + ASSERT_NE(nullptr, peer); + peer->uuid = uuid; + ASSERT_EQ(1, oc_tls_num_peers(kDeviceID)); + + oc_sec_pstat_t *pstat = oc_sec_get_pstat(kDeviceID); + ASSERT_NE(nullptr, pstat); + pstat->s = OC_DOS_RFOTM; + + // we use doxm to represent all SVRs + auto *doxm = oc_core_get_resource_by_index(OCF_SEC_DOXM, kDeviceID); + ASSERT_NE(nullptr, doxm); + assertUnauthorizedAccessToResource(doxm, kDeviceID, &ep, true); + + std::array uuid_buf{}; + ASSERT_TRUE( + oc_certs_encode_CN_with_UUID(&uuid, uuid_buf.data(), uuid_buf.size())); + + auto role = OC_STRING_LOCAL("user"); + auto writeAuth = OC_STRING_LOCAL("write"); + oc::Roles roles{}; + roles.Add(static_cast(role.ptr), + static_cast(writeAuth.ptr)); + + auto role_pem = oc::pki::obt::GenerateRoleCertificate( + uuid_buf.data(), g_root_subject, g_root_keypair, roles); + ASSERT_FALSE(role_pem.empty()); + + oc_sec_encoded_data_t publicdata = { role_pem.data(), role_pem.size() - 1, + OC_ENCODING_PEM }; + int credid = oc_sec_add_new_cred( + kDeviceID, true, peer, -1, OC_CREDTYPE_CERT, OC_CREDUSAGE_ROLE_CERT, "*", + { nullptr, 0, OC_ENCODING_UNSUPPORTED }, publicdata, OC_STRING_VIEW_NULL, + OC_STRING_VIEW_NULL, OC_STRING_VIEW_NULL, nullptr); + ASSERT_NE(-1, credid); + + oc_ace_subject_t write{}; + write.role = { role, writeAuth }; + ASSERT_TRUE(oc_sec_ace_update_res(OC_SUBJECT_ROLE, &write, -1, OC_PERM_UPDATE, + nullptr, oc_string(doxm->uri), OC_ACE_NO_WC, + kDeviceID, nullptr)); + checkAccessToResource(doxm, &ep, false, true, true, false); + + oc_sec_acl_clear(kDeviceID, nullptr, nullptr); + oc_tls_remove_peer(&ep); +} + +#endif /* OC_DYNAMIC_ALLOCATION && OC_PKI */ + +TEST_F(TestAcl, oc_sec_check_acl_AccessToNonSVRByCryptConn) +{ + oc_endpoint_t ep = oc::endpoint::FromString("coaps://[ff02::41]:1336"); + ASSERT_NE(0, ep.flags & SECURED); + ep.device = kDeviceID; + + oc_sec_pstat_t *pstat = oc_sec_get_pstat(kDeviceID); + ASSERT_NE(nullptr, pstat); + pstat->s = OC_DOS_RFNOP; + + auto resources = + getResources(kDeviceID, [](size_t device, const oc_resource_t *resource) { + oc_string_view_t uriv = oc_string_view2(&resource->uri); +#ifdef OC_HAS_FEATURE_PLGD_TIME + if (plgd_is_time_resource_uri(uriv)) { + return false; + } +#endif /* OC_HAS_FEATURE_PLGD_TIME */ +#ifdef OC_WKCORE + if (oc_is_wkcore_resource_uri(uriv)) { + return false; + } +#endif /* OC_WKCORE */ + return !oc_core_is_SVR(resource, device); + }); + for (auto res : resources) { + assertUnauthorizedAccessToResource(res, kDeviceID, &ep, false); + } + + auto setAnonConnPermission = [&resources](oc_ace_permissions_t permission) { + for (auto res : resources) { + oc_ace_subject_t anon_crypt{}; + anon_crypt.conn = OC_CONN_AUTH_CRYPT; + if (!oc_sec_ace_update_res(OC_SUBJECT_CONN, &anon_crypt, -1, permission, + nullptr, oc_string(res->uri), OC_ACE_NO_WC, + kDeviceID, nullptr)) { + return false; + } + } + return true; + }; + + // allow delete access to all resources + ASSERT_TRUE(setAnonConnPermission(OC_PERM_DELETE)); + for (auto res : resources) { + checkAccessToResource(res, &ep, false, false, false, true); + } + + // allow update access to all resources + ASSERT_TRUE(setAnonConnPermission(OC_PERM_UPDATE)); + for (auto res : resources) { + checkAccessToResource(res, &ep, false, true, true, true); + } + + // allow retrieve access to all resources + ASSERT_TRUE(setAnonConnPermission(OC_PERM_RETRIEVE)); + for (auto res : resources) { + checkAccessToResource(res, &ep); + } + + // but using anon connection shouldn't work + oc_endpoint_t epAnon = oc::endpoint::FromString("coap://[ff02::41]:1336"); + ASSERT_EQ(0, epAnon.flags & SECURED); + epAnon.device = kDeviceID; + for (auto res : resources) { + checkAccessToResource(res, &epAnon, false, false, false, false); + } + + oc_sec_acl_clear(kDeviceID, nullptr, nullptr); +} + +TEST_F(TestAcl, oc_sec_check_acl_AccessToNonSVRByAnonConn) +{ + oc_endpoint_t ep = oc::endpoint::FromString("coap://[ff02::41]:1336"); + ASSERT_EQ(0, ep.flags & SECURED); + ep.device = kDeviceID; + + oc_sec_pstat_t *pstat = oc_sec_get_pstat(kDeviceID); + ASSERT_NE(nullptr, pstat); + pstat->s = OC_DOS_RFNOP; + + auto resources = + getResources(kDeviceID, [](size_t device, const oc_resource_t *resource) { + oc_string_view_t uriv = oc_string_view2(&resource->uri); +#ifdef OC_HAS_FEATURE_PLGD_TIME + if (plgd_is_time_resource_uri(uriv)) { + return false; + } +#endif /* OC_HAS_FEATURE_PLGD_TIME */ +#ifdef OC_WKCORE + if (oc_is_wkcore_resource_uri(uriv)) { + return false; + } +#endif /* OC_WKCORE */ + return !oc_core_is_SVR(resource, device); + }); + for (auto res : resources) { + assertUnauthorizedAccessToResource(res, kDeviceID, &ep, false); + } + + auto setAnonConnPermission = [&resources](oc_ace_permissions_t permission) { + for (auto res : resources) { + oc_ace_subject_t anon_clear{}; + anon_clear.conn = OC_CONN_ANON_CLEAR; + if (!oc_sec_ace_update_res(OC_SUBJECT_CONN, &anon_clear, -1, permission, + nullptr, oc_string(res->uri), OC_ACE_NO_WC, + kDeviceID, nullptr)) { + return false; + } + } + return true; + }; + + // allow retrieve access to all resources + ASSERT_TRUE(setAnonConnPermission(OC_PERM_RETRIEVE)); + for (auto res : resources) { + checkAccessToResource(res, &ep, true, false, false, false); + } + + // allow update access to all resources + ASSERT_TRUE(setAnonConnPermission(OC_PERM_UPDATE)); + for (auto res : resources) { + checkAccessToResource(res, &ep, true, true, true, false); + } + + // allow delete access to all resources + ASSERT_TRUE(setAnonConnPermission(OC_PERM_DELETE)); + for (auto res : resources) { + checkAccessToResource(res, &ep, true, true, true, true); + } + + // using secure connection should also work + oc_endpoint_t epCrypt = oc::endpoint::FromString("coaps://[ff02::41]:1336"); + ASSERT_NE(0, epCrypt.flags & SECURED); + epCrypt.device = kDeviceID; + for (auto res : resources) { + checkAccessToResource(res, &epCrypt); + } + + oc_sec_acl_clear(kDeviceID, nullptr, nullptr); +} + #endif /* OC_SECURITY */ diff --git a/security/unittest/keypairtest.cpp b/security/unittest/keypairtest.cpp index 2a6ce8118..362f71c4f 100644 --- a/security/unittest/keypairtest.cpp +++ b/security/unittest/keypairtest.cpp @@ -276,25 +276,18 @@ TEST_F(TestKeyPair, GenerateForDevice) { auto generate_keypair = [](mbedtls_ecp_group_id grpid) { OC_DBG("generate ecdsa keypair with elliptic-curve %d", (int)grpid); - EXPECT_EQ(0, oc_sec_ecdsa_count_keypairs()) - << "error for ec(" << grpid << ")"; + EXPECT_EQ(0, oc_sec_ecdsa_count_keypairs()); EXPECT_TRUE( oc_sec_ecdsa_update_or_generate_keypair_for_device(grpid, - /*device*/ 0)) - << "error for ec(" << grpid << ")"; - EXPECT_EQ(1, oc_sec_ecdsa_count_keypairs()) - << "error for ec(" << grpid << ")"; + /*device*/ 0)); + EXPECT_EQ(1, oc_sec_ecdsa_count_keypairs()); EXPECT_TRUE( oc_sec_ecdsa_update_or_generate_keypair_for_device(grpid, - /*device*/ 0)) - << "error for ec(" << grpid << ")"; - EXPECT_EQ(1, oc_sec_ecdsa_count_keypairs()) - << "error for ec(" << grpid << ")"; + /*device*/ 0)); + EXPECT_EQ(1, oc_sec_ecdsa_count_keypairs()); - EXPECT_NE(nullptr, oc_sec_ecdsa_get_keypair(/*device*/ 0)) - << "error for ec(" << grpid << ")"; - EXPECT_EQ(nullptr, oc_sec_ecdsa_get_keypair(/*device*/ 1)) - << "error for ec(" << grpid << ")"; + EXPECT_NE(nullptr, oc_sec_ecdsa_get_keypair(/*device*/ 0)); + EXPECT_EQ(nullptr, oc_sec_ecdsa_get_keypair(/*device*/ 1)); oc_sec_ecdsa_free_keypairs(); }; diff --git a/security/unittest/obt_certstest.cpp b/security/unittest/obt_certstest.cpp index 772169842..e5d363040 100644 --- a/security/unittest/obt_certstest.cpp +++ b/security/unittest/obt_certstest.cpp @@ -28,6 +28,7 @@ #include "security/oc_security_internal.h" #include "tests/gtest/Device.h" #include "tests/gtest/KeyPair.h" +#include "tests/gtest/PKI.h" #include "tests/gtest/Role.h" #include @@ -86,14 +87,6 @@ class TestObtCerts : public testing::Test { return std::string(container.begin(), container.end()); } - static std::vector GetPEM(std::vector &data); - std::vector GenerateSelfSignedRootCertificate( - oc::keypair_t &kp, mbedtls_md_type_t sig_alg = MBEDTLS_MD_SHA256) const; - std::vector GenerateIdentityCertificate( - oc::keypair_t &kp, mbedtls_md_type_t sig_alg = MBEDTLS_MD_SHA256) const; - std::vector GenerateRoleCertificate( - oc::keypair_t &kp, mbedtls_md_type_t sig_alg = MBEDTLS_MD_SHA256) const; - std::string uuid_{}; oc::keypair_t kp256_{}; oc::keypair_t kp384_{}; @@ -104,90 +97,6 @@ static const std::string g_root_subject_name{ "IoTivity-Lite Test" }; static const std::string g_root_subject{ "C=US, O=OCF, CN=" + g_root_subject_name }; -std::vector -TestObtCerts::GetPEM(std::vector &data) -{ - auto it = - std::find(data.begin(), data.end(), static_cast('\0')); - size_t data_len = - std::distance(data.begin(), it) + 1; // size with NULL terminator - EXPECT_NE(data.end(), it); - - EXPECT_TRUE(oc_certs_is_PEM(&data[0], data_len)); - data.resize(data_len); - return data; -} - -std::vector -TestObtCerts::GenerateSelfSignedRootCertificate(oc::keypair_t &kp, - mbedtls_md_type_t sig_alg) const -{ - oc_obt_generate_root_cert_data_t cert_data = { - /*.subject_name = */ g_root_subject.c_str(), - /*.public_key =*/kp.public_key.data(), - /*.public_key_size =*/kp.public_key_size, - /*.private_key =*/kp.private_key.data(), - /*.private_key_size =*/kp.private_key_size, - /*.signature_md_alg=*/sig_alg, - }; - - std::vector cert_buf{}; - cert_buf.resize(4096, '\0'); - int err = oc_obt_generate_self_signed_root_cert_pem( - cert_data, cert_buf.data(), cert_buf.size()); - EXPECT_EQ(0, err); - - return GetPEM(cert_buf); -} - -std::vector -TestObtCerts::GenerateIdentityCertificate(oc::keypair_t &kp, - mbedtls_md_type_t sig_alg) const -{ - oc_obt_generate_identity_cert_data_t cert_data = { - /*.subject_name =*/uuid_.c_str(), - /*.public_key =*/kp.public_key.data(), - /*.public_key_size =*/kp.public_key_size, - /*.issuer_name =*/g_root_subject.c_str(), - /*.issuer_private_key =*/kp.private_key.data(), - /*.issuer_private_key_size =*/kp.private_key_size, - /*.signature_md_alg=*/sig_alg, - }; - - std::vector cert_buf{}; - cert_buf.resize(4096, '\0'); - int err = oc_obt_generate_identity_cert_pem(cert_data, cert_buf.data(), - cert_buf.size()); - EXPECT_EQ(0, err); - return GetPEM(cert_buf); -} - -std::vector -TestObtCerts::GenerateRoleCertificate(oc::keypair_t &kp, - mbedtls_md_type_t sig_alg) const -{ - oc_obt_generate_role_cert_data_t cert_data = { - /*.roles =*/roles_.Head(), - /*.subject_name =*/uuid_.c_str(), - /*.public_key =*/kp.public_key.data(), - /*.public_key_size =*/kp.public_key_size, - /*.issuer_name =*/g_root_subject.c_str(), - /*.issuer_private_key =*/kp.private_key.data(), - /*.issuer_private_key_size =*/kp.private_key_size, - /*.signature_md_alg=*/sig_alg, - }; - - std::vector cert_buf{}; - cert_buf.resize(4096, '\0'); - int err = - oc_obt_generate_role_cert_pem(cert_data, cert_buf.data(), cert_buf.size()); - EXPECT_EQ(0, err); - if (err != 0) { - return {}; - } - return GetPEM(cert_buf); -} - TEST_F(TestObtCerts, GenerateSelfSignedRootCertificateFail) { oc_obt_generate_root_cert_data_t cert_data = { @@ -235,7 +144,8 @@ TEST_F(TestObtCerts, GenerateSelfSignedRootCertificateFail) TEST_F(TestObtCerts, GenerateValidSelfSignedCertificate) { - auto cert_buf = GenerateSelfSignedRootCertificate(kp256_, MBEDTLS_MD_SHA384); + auto cert_buf = oc::pki::obt::GenerateSelfSignedRootCertificate( + g_root_subject, kp256_, MBEDTLS_MD_SHA384); std::array serial{}; int ret = oc_certs_parse_serial_number(&cert_buf[0], cert_buf.size(), @@ -260,7 +170,8 @@ TEST_F(TestObtCerts, GenerateValidSelfSignedCertificate) TEST_F(TestObtCerts, SerializeSelfSignedCertificate) { - auto root_cert = GenerateSelfSignedRootCertificate(kp384_, MBEDTLS_MD_SHA384); + auto root_cert = oc::pki::obt::GenerateSelfSignedRootCertificate( + g_root_subject, kp384_, MBEDTLS_MD_SHA384); mbedtls_x509_crt crt; mbedtls_x509_crt_init(&crt); @@ -281,7 +192,8 @@ TEST_F(TestObtCerts, SerializeSelfSignedCertificate) TEST_F(TestObtCerts, ValidateSelfSignedCertificate) { - auto root_cert = GenerateSelfSignedRootCertificate(kp384_); + auto root_cert = + oc::pki::obt::GenerateSelfSignedRootCertificate(g_root_subject, kp384_); mbedtls_x509_crt crt; mbedtls_x509_crt_init(&crt); @@ -350,7 +262,8 @@ TEST_F(TestObtCerts, GenerateIdentityCertificateFail) TEST_F(TestObtCerts, GenerateValidIdentityCertificate) { - auto id_cert = GenerateIdentityCertificate(kp256_, MBEDTLS_MD_SHA384); + auto id_cert = oc::pki::obt::GenerateIdentityCertificate( + uuid_, g_root_subject, kp256_, MBEDTLS_MD_SHA384); std::array serial{}; int ret = oc_certs_parse_serial_number(&id_cert[0], id_cert.size(), @@ -384,7 +297,8 @@ TEST_F(TestObtCerts, GenerateValidIdentityCertificate) TEST_F(TestObtCerts, SerializeIdentityCertificate) { - auto id_cert = GenerateIdentityCertificate(kp384_); + auto id_cert = + oc::pki::obt::GenerateIdentityCertificate(uuid_, g_root_subject, kp384_); mbedtls_x509_crt crt; mbedtls_x509_crt_init(&crt); @@ -405,7 +319,8 @@ TEST_F(TestObtCerts, SerializeIdentityCertificate) TEST_F(TestObtCerts, ValidateIdentityCertificate) { - auto cert_buf = GenerateIdentityCertificate(kp384_, MBEDTLS_MD_SHA384); + auto cert_buf = oc::pki::obt::GenerateIdentityCertificate( + uuid_, g_root_subject, kp384_, MBEDTLS_MD_SHA384); mbedtls_x509_crt crt; mbedtls_x509_crt_init(&crt); @@ -490,7 +405,8 @@ TEST_F(TestObtCerts, GenerateRoleCertificateFail) TEST_F(TestObtCerts, GenerateValidRoleCertificate) { - auto role_cert = GenerateRoleCertificate(kp256_, MBEDTLS_MD_SHA384); + auto role_cert = oc::pki::obt::GenerateRoleCertificate( + uuid_, g_root_subject, kp256_, roles_, MBEDTLS_MD_SHA384); ASSERT_FALSE(role_cert.empty()); std::array serial{}; @@ -536,7 +452,10 @@ TEST_F(TestObtCerts, GenerateValidRoleCertificate) TEST_F(TestObtCerts, SerializeRoleCertificate) { - auto role_cert = GenerateRoleCertificate(kp384_); + auto role_cert = oc::pki::obt::GenerateRoleCertificate(uuid_, g_root_subject, + kp384_, roles_); + ASSERT_FALSE(role_cert.empty()); + mbedtls_x509_crt crt; mbedtls_x509_crt_init(&crt); @@ -557,7 +476,9 @@ TEST_F(TestObtCerts, SerializeRoleCertificate) TEST_F(TestObtCerts, ValidateRoleCertificate) { - auto cert_buf = GenerateRoleCertificate(kp384_, MBEDTLS_MD_SHA384); + auto cert_buf = oc::pki::obt::GenerateRoleCertificate( + uuid_, g_root_subject, kp384_, roles_, MBEDTLS_MD_SHA384); + ASSERT_FALSE(cert_buf.empty()); mbedtls_x509_crt crt; mbedtls_x509_crt_init(&crt); @@ -574,6 +495,8 @@ TEST_F(TestObtCerts, ValidateRoleCertificate) mbedtls_x509_crt_free(&crt); } +static constexpr size_t kDeviceID{ 0 }; + class TestObtCertsWithDevice : public testing::Test { public: static void SetUpTestCase() @@ -617,21 +540,11 @@ class TestObtCertsWithDevice : public testing::Test { TEST_F(TestObtCertsWithDevice, RootCertificateCredential) { oc::keypair_t kp{ oc::GetECPKeyPair(MBEDTLS_ECP_DP_SECP256R1) }; - - oc_obt_generate_root_cert_data_t cert_data = { - /*.subject_name = */ g_root_subject.c_str(), - /*.public_key =*/kp.public_key.data(), - /*.public_key_size =*/kp.public_key_size, - /*.private_key =*/kp.private_key.data(), - /*.private_key_size =*/kp.private_key_size, - /*.signature_md_alg=*/MBEDTLS_MD_SHA256, - }; - - size_t device = 0; - int credid = oc_obt_generate_self_signed_root_cert(cert_data, device); + int credid = oc::pki::obt::GenerateSelfSignedRootCertificate( + kDeviceID, g_root_subject, kp); ASSERT_LT(0, credid); - oc_sec_cred_t *cred = oc_sec_get_cred_by_credid(credid, device); + oc_sec_cred_t *cred = oc_sec_get_cred_by_credid(credid, kDeviceID); EXPECT_NE(nullptr, cred); // is root CA EXPECT_EQ(OC_CREDUSAGE_TRUSTCA, cred->credusage); @@ -656,18 +569,9 @@ TEST_F(TestObtCertsWithDevice, RoleCertificateCredential) oc::keypair_t kp{ oc::GetECPKeyPair(MBEDTLS_ECP_DP_SECP384R1) }; // need a trust anchor to verify Role Cert - oc_obt_generate_root_cert_data_t root_cert = { - /*.subject_name = */ g_root_subject.c_str(), - /*.public_key =*/kp.public_key.data(), - /*.public_key_size =*/kp.public_key_size, - /*.private_key =*/kp.private_key.data(), - /*.private_key_size =*/kp.private_key_size, - /*.signature_md_alg=*/MBEDTLS_MD_SHA384, - }; - - size_t device = 0; - int credid = oc_obt_generate_self_signed_root_cert(root_cert, device); - EXPECT_LT(0, credid); + int credid = oc::pki::obt::GenerateSelfSignedRootCertificate( + kDeviceID, g_root_subject, kp); + ASSERT_LT(0, credid); oc_obt_generate_role_cert_data_t role_cert = { /*.roles =*/roles_.Head(), @@ -689,7 +593,7 @@ TEST_F(TestObtCertsWithDevice, RoleCertificateCredential) oc_uuid_t subjectuuid{}; subjectuuid.id[0] = '*'; oc_sec_cred_t *cred = oc_sec_allocate_cred(&subjectuuid, OC_CREDTYPE_CERT, - OC_CREDUSAGE_ROLE_CERT, device); + OC_CREDUSAGE_ROLE_CERT, kDeviceID); EXPECT_NE(nullptr, cred); EXPECT_EQ(0, oc_certs_parse_role_certificate(&cert_buf[0], cert_buf.size(), cred, false)); @@ -699,7 +603,7 @@ TEST_F(TestObtCertsWithDevice, RoleCertificateCredential) OC_DBG("authority: %s", oc_string(cred->role.authority)); EXPECT_STREQ(g_root_subject_name.c_str(), oc_string(cred->role.authority)); - oc_sec_remove_cred(cred, device); + oc_sec_remove_cred(cred, kDeviceID); } #endif /* OC_SECURITY && OC_PKI && OC_DYNAMIC_ALLOCATION */ diff --git a/security/unittest/pstattest.cpp b/security/unittest/pstattest.cpp index e9b6fbe1b..317c43560 100644 --- a/security/unittest/pstattest.cpp +++ b/security/unittest/pstattest.cpp @@ -178,7 +178,7 @@ TEST_F(TestPstatWithServer, PostRequest_FailMethodNotAuthorized) auto epOpt = oc::TestDevice::GetEndpoint(kDeviceID); ASSERT_TRUE(epOpt.has_value()); auto ep = std::move(*epOpt); - oc::testNotSupportedMethod(OC_POST, &ep, "/oic/sec/pstat", nullptr, + oc::testNotSupportedMethod(OC_POST, &ep, OCF_SEC_PSTAT_URI, nullptr, OC_STATUS_UNAUTHORIZED); } @@ -194,7 +194,7 @@ TEST_F(TestPstatWithServer, PutRequest_Fail) #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, "/oic/sec/pstat", nullptr, + oc::testNotSupportedMethod(OC_PUT, &ep, OCF_SEC_PSTAT_URI, nullptr, error_code); } @@ -208,7 +208,7 @@ TEST_F(TestPstatWithServer, DeleteRequest_Fail) #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_DELETE, &ep, "/oic/sec/pstat", nullptr, + oc::testNotSupportedMethod(OC_DELETE, &ep, OCF_SEC_PSTAT_URI, nullptr, error_code); } diff --git a/security/unittest/rolestest.cpp b/security/unittest/rolestest.cpp index 636270584..50594fef0 100644 --- a/security/unittest/rolestest.cpp +++ b/security/unittest/rolestest.cpp @@ -36,6 +36,7 @@ #include "tests/gtest/Device.h" #include "tests/gtest/Endpoint.h" #include "tests/gtest/KeyPair.h" +#include "tests/gtest/PKI.h" #include "tests/gtest/RepPool.h" #include "tests/gtest/Resource.h" #include "tests/gtest/Role.h" @@ -67,9 +68,9 @@ class TestRolesWithServer : public testing::Test { ASSERT_NE(nullptr, g_uuid); #ifdef OC_DYNAMIC_ALLOCATION g_root_keypair = oc::GetECPKeyPair(MBEDTLS_ECP_DP_SECP256R1); - g_root_credid = generateSelfSignedRootCertificate( - g_root_keypair, g_root_subject, MBEDTLS_MD_SHA256); - ASSERT_GT(g_root_credid, 0); + g_root_credid = oc::pki::obt::GenerateSelfSignedRootCertificate( + kDeviceID, g_root_subject, g_root_keypair); + ASSERT_LT(0, g_root_credid); #endif /* OC_DYNAMIC_ALLOCATION */ } @@ -88,15 +89,6 @@ class TestRolesWithServer : public testing::Test { oc_tls_peer_t *addPeer(const oc_endpoint_t *ep); #ifdef OC_DYNAMIC_ALLOCATION - static int generateSelfSignedRootCertificate(const oc::keypair_t &kp, - const std::string &subject_name, - mbedtls_md_type_t sig_alg); - - static std::vector generateRoleCertificatePEM( - const oc::keypair_t &kp, const oc::Roles &roles, - const std::string &subject_name, const std::string &issuer_name, - mbedtls_md_type_t sig_alg); - static bool addRolesByCertificate(const oc_uuid_t *uuid, const oc::keypair_t &kp, const oc::Roles &roles, @@ -143,62 +135,6 @@ TestRolesWithServer::addPeer(const oc_endpoint_t *ep) #ifdef OC_DYNAMIC_ALLOCATION -static std::vector -getPEM(std::vector &data) -{ - auto it = - std::find(data.begin(), data.end(), static_cast('\0')); - size_t data_len = - std::distance(data.begin(), it) + 1; // size with NULL terminator - EXPECT_NE(data.end(), it); - - EXPECT_TRUE(oc_certs_is_PEM(&data[0], data_len)); - data.resize(data_len); - return data; -} - -int -TestRolesWithServer::generateSelfSignedRootCertificate( - const oc::keypair_t &kp, const std::string &subject_name, - mbedtls_md_type_t sig_alg) -{ - oc_obt_generate_root_cert_data_t cert_data = { - /*.subject_name = */ subject_name.c_str(), - /*.public_key =*/kp.public_key.data(), - /*.public_key_size =*/kp.public_key_size, - /*.private_key =*/kp.private_key.data(), - /*.private_key_size =*/kp.private_key_size, - /*.signature_md_alg=*/sig_alg, - }; - - return oc_obt_generate_self_signed_root_cert(cert_data, kDeviceID); -} - -std::vector -TestRolesWithServer::generateRoleCertificatePEM(const oc::keypair_t &kp, - const oc::Roles &roles, - const std::string &subject_name, - const std::string &issuer_name, - mbedtls_md_type_t sig_alg) -{ - oc_obt_generate_role_cert_data_t cert_data = { - /*.roles =*/roles.Head(), - /*.subject_name =*/subject_name.c_str(), - /*.public_key =*/kp.public_key.data(), - /*.public_key_size =*/kp.public_key_size, - /*.issuer_name =*/issuer_name.c_str(), - /*.issuer_private_key =*/kp.private_key.data(), - /*.issuer_private_key_size =*/kp.private_key_size, - /*.signature_md_alg=*/sig_alg, - }; - - std::vector cert_buf{}; - cert_buf.resize(4096, '\0'); - EXPECT_EQ(0, oc_obt_generate_role_cert_pem(cert_data, cert_buf.data(), - cert_buf.size())); - return getPEM(cert_buf); -} - bool TestRolesWithServer::addRolesByCertificate(const oc_uuid_t *uuid, const oc::keypair_t &kp, @@ -210,8 +146,8 @@ TestRolesWithServer::addRolesByCertificate(const oc_uuid_t *uuid, if (!oc_certs_encode_CN_with_UUID(uuid, uuid_buf.data(), uuid_buf.size())) { return false; } - auto role_pem = generateRoleCertificatePEM(kp, roles, uuid_buf.data(), - subject_name, MBEDTLS_MD_SHA256); + auto role_pem = oc::pki::obt::GenerateRoleCertificate( + uuid_buf.data(), subject_name, kp, roles); if (role_pem.empty()) { return false; } @@ -315,7 +251,7 @@ TEST_F(TestRolesWithServer, AddRole_FailAssertion) TEST_F(TestRolesWithServer, AddRole_AssertAllowed) { oc::Roles roles{}; - roles.Add("oic.role.owner"); + roles.Add(OCF_SEC_ROLE_OWNER); oc_endpoint_t ep = oc::endpoint::FromString("coaps://[::1]:42"); const auto *peer = addPeer(&ep); diff --git a/tests/gtest/PKI.cpp b/tests/gtest/PKI.cpp index d1e910ef5..c5e5fa2e7 100644 --- a/tests/gtest/PKI.cpp +++ b/tests/gtest/PKI.cpp @@ -19,10 +19,15 @@ #ifdef OC_PKI #include "oc_pki.h" -#include "PKI.h" #include "port/oc_log_internal.h" #include "security/oc_certs_internal.h" #include "security/oc_entropy_internal.h" +#include "tests/gtest/PKI.h" +#include "tests/gtest/Role.h" + +#ifdef OC_DYNAMIC_ALLOCATION +#include "security/oc_obt_internal.h" +#endif /* OC_DYNAMIC_ALLOCATION */ #include #include @@ -54,6 +59,23 @@ ReadPem(const std::string &path) return data; } +static std::vector +GetPEM(std::vector &data) +{ + auto it = + std::find(data.begin(), data.end(), static_cast('\0')); + if (data.end() == it) { + return {}; + } + size_t data_len = + std::distance(data.begin(), it) + 1; // size with NULL terminator + if (!oc_certs_is_PEM(&data[0], data_len)) { + return {}; + } + data.resize(data_len); + return data; +} + #if defined(OC_DYNAMIC_ALLOCATION) || defined(OC_TEST) static constexpr std::string_view kRootSubjectName{ "IoTivity-Lite Test" }; @@ -73,16 +95,7 @@ GenerateCertificate(const oc_certs_generate_t &generate) if (err != 0) { return {}; } - - auto it = std::find(cert_buf.begin(), cert_buf.end(), - static_cast('\0')); - size_t data_len = - std::distance(cert_buf.begin(), it) + 1; // size with NULL terminator - if (cert_buf.end() == it || !oc_certs_is_PEM(&cert_buf[0], data_len)) { - return {}; - } - cert_buf.resize(data_len); - return cert_buf; + return GetPEM(cert_buf); } std::vector @@ -171,14 +184,7 @@ KeyParser::GetPrivateKey(const unsigned char *key, size_t keylen) } mbedtls_pk_free(&pk); - auto it = std::find(pem.begin(), pem.end(), static_cast('\0')); - if (pem.end() == it) { - return {}; - } - size_t dataSize = - std::distance(pem.begin(), it) + 1; // include null terminator - pem.resize(dataSize); - return pem; + return GetPEM(pem); } PemData::PemData(const std::string &path) @@ -326,6 +332,104 @@ PKDummyFunctions::GetPKFunctions() return pk_functions; } +#ifdef OC_DYNAMIC_ALLOCATION + +namespace obt { + +int +GenerateSelfSignedRootCertificate(size_t device, + const std::string &subject_name, + const oc::keypair_t &kp, + mbedtls_md_type_t sig_alg) +{ + oc_obt_generate_root_cert_data_t root_cert_data = { + /*.subject_name = */ subject_name.c_str(), + /*.public_key =*/kp.public_key.data(), + /*.public_key_size =*/kp.public_key_size, + /*.private_key =*/kp.private_key.data(), + /*.private_key_size =*/kp.private_key_size, + /*.signature_md_alg=*/sig_alg, + }; + return oc_obt_generate_self_signed_root_cert(root_cert_data, device); +} + +std::vector +GenerateSelfSignedRootCertificate(const std::string &subject_name, + const oc::keypair_t &kp, + mbedtls_md_type_t sig_alg) +{ + oc_obt_generate_root_cert_data_t cert_data = { + /*.subject_name = */ subject_name.c_str(), + /*.public_key =*/kp.public_key.data(), + /*.public_key_size =*/kp.public_key_size, + /*.private_key =*/kp.private_key.data(), + /*.private_key_size =*/kp.private_key_size, + /*.signature_md_alg=*/sig_alg, + }; + + std::vector cert_buf{}; + cert_buf.resize(4096, '\0'); + int err = oc_obt_generate_self_signed_root_cert_pem( + cert_data, cert_buf.data(), cert_buf.size()); + EXPECT_EQ(0, err); + + return GetPEM(cert_buf); +} + +std::vector +GenerateIdentityCertificate(const std::string &subject_name, + const std::string &issuer_name, + const oc::keypair_t &kp, mbedtls_md_type_t sig_alg) +{ + oc_obt_generate_identity_cert_data_t cert_data = { + /*.subject_name =*/subject_name.c_str(), + /*.public_key =*/kp.public_key.data(), + /*.public_key_size =*/kp.public_key_size, + /*.issuer_name =*/issuer_name.c_str(), + /*.issuer_private_key =*/kp.private_key.data(), + /*.issuer_private_key_size =*/kp.private_key_size, + /*.signature_md_alg=*/sig_alg, + }; + + std::vector cert_buf{}; + cert_buf.resize(4096, '\0'); + int err = oc_obt_generate_identity_cert_pem(cert_data, cert_buf.data(), + cert_buf.size()); + EXPECT_EQ(0, err); + return GetPEM(cert_buf); +} + +std::vector +GenerateRoleCertificate(const std::string &subject_name, + const std::string &issuer_name, const oc::keypair_t &kp, + const oc::Roles &roles, mbedtls_md_type_t sig_alg) +{ + oc_obt_generate_role_cert_data_t cert_data = { + /*.roles =*/roles.Head(), + /*.subject_name =*/subject_name.c_str(), + /*.public_key =*/kp.public_key.data(), + /*.public_key_size =*/kp.public_key_size, + /*.issuer_name =*/issuer_name.c_str(), + /*.issuer_private_key =*/kp.private_key.data(), + /*.issuer_private_key_size =*/kp.private_key_size, + /*.signature_md_alg=*/sig_alg, + }; + + std::vector cert_buf{}; + cert_buf.resize(4096, '\0'); + int err = + oc_obt_generate_role_cert_pem(cert_data, cert_buf.data(), cert_buf.size()); + EXPECT_EQ(0, err); + if (err != 0) { + return {}; + } + return GetPEM(cert_buf); +} + +} // namespace obt + +#endif /* OC_DYNAMIC_ALLOCATION */ + } // namespace oc::pki #endif /* OC_PKI */ diff --git a/tests/gtest/PKI.h b/tests/gtest/PKI.h index 77f391291..12d159175 100644 --- a/tests/gtest/PKI.h +++ b/tests/gtest/PKI.h @@ -23,6 +23,7 @@ #include "oc_pki.h" #include "security/oc_certs_generate_internal.h" #include "tests/gtest/KeyPair.h" +#include "tests/gtest/Role.h" #include #include @@ -174,6 +175,31 @@ class PKDummyFunctions { static bool parseKeyInvoked; }; +#ifdef OC_DYNAMIC_ALLOCATION + +namespace obt { + +int GenerateSelfSignedRootCertificate( + size_t device, const std::string &subject_name, const oc::keypair_t &kp, + mbedtls_md_type_t sig_alg = MBEDTLS_MD_SHA256); + +std::vector GenerateSelfSignedRootCertificate( + const std::string &subject_name, const oc::keypair_t &kp, + mbedtls_md_type_t sig_alg = MBEDTLS_MD_SHA256); + +std::vector GenerateIdentityCertificate( + const std::string &subject_name, const std::string &issuer_name, + const oc::keypair_t &kp, mbedtls_md_type_t sig_alg = MBEDTLS_MD_SHA256); + +std::vector GenerateRoleCertificate( + const std::string &subject_name, const std::string &issuer_name, + const oc::keypair_t &kp, const oc::Roles &roles = {}, + mbedtls_md_type_t sig_alg = MBEDTLS_MD_SHA256); + +} // namespace obt + +#endif /* OC_DYNAMIC_ALLOCATION */ + } // namespace oc::pki #endif /* OC_PKI */ diff --git a/util/oc_atomic.h b/util/oc_atomic.h index b7b7823ce..4fd0e7dda 100644 --- a/util/oc_atomic.h +++ b/util/oc_atomic.h @@ -38,10 +38,10 @@ extern "C" { (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1))) #define OC_ATOMIC -#define OC_ATOMIC_INT8_T int8_t -#define OC_ATOMIC_UINT8_T uint8_t -#define OC_ATOMIC_INT32_T int32_t -#define OC_ATOMIC_UINT32_T uint32_t +typedef int8_t OC_ATOMIC_INT8_T; +typedef uint8_t OC_ATOMIC_UINT8_T; +typedef int32_t OC_ATOMIC_INT32_T; +typedef uint32_t OC_ATOMIC_UINT32_T; #define OC_ATOMIC_LOAD32(x) __atomic_load_n(&(x), __ATOMIC_SEQ_CST) @@ -80,10 +80,10 @@ extern "C" { #include #define OC_ATOMIC -#define OC_ATOMIC_INT8_T int8_t -#define OC_ATOMIC_UINT8_T uint8_t -#define OC_ATOMIC_INT32_T int32_t -#define OC_ATOMIC_UINT32_T uint32_t +typedef int8_t OC_ATOMIC_INT8_T; +typedef uint8_t OC_ATOMIC_UINT8_T; +typedef int32_t OC_ATOMIC_INT32_T; +typedef uint32_t OC_ATOMIC_UINT32_T; #define OC_ATOMIC_LOAD8(x) _InterlockedOr8((&x), 0) #define OC_ATOMIC_LOAD32(x) _InterlockedOr((&x), 0) diff --git a/util/unittest/memblocktest.cpp b/util/unittest/memblocktest.cpp new file mode 100644 index 000000000..3ea54c20a --- /dev/null +++ b/util/unittest/memblocktest.cpp @@ -0,0 +1,122 @@ +/**************************************************************************** + * + * Copyright (c) 2024 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. + * + ****************************************************************************/ + +#include "util/oc_memb.h" + +#include +#include + +struct test_data_t +{ + int a; + int b; +}; + +class TestMemoryBlock : public testing::Test {}; + +TEST_F(TestMemoryBlock, Init) +{ + OC_MEMB_LOCAL(oc_test_data, test_data_t, 13); + EXPECT_EQ(oc_test_data.size, sizeof(test_data_t)); + oc_memb_init(&oc_test_data); +#ifdef OC_DYNAMIC_ALLOCATION + EXPECT_EQ(oc_test_data.num, 0); +#else /* !OC_DYNAMIC_ALLOCATION */ + EXPECT_EQ(oc_test_data.num, 13); +#endif /* OC_DYNAMIC_ALLOCATION */ +} + +TEST_F(TestMemoryBlock, Alloc_Fail) +{ + EXPECT_EQ(nullptr, oc_memb_alloc(nullptr)); +} + +TEST_F(TestMemoryBlock, Alloc) +{ + OC_MEMB_LOCAL(oc_test_data, test_data_t, 13); + oc_memb_init(&oc_test_data); + auto *block1 = static_cast(oc_memb_alloc(&oc_test_data)); + EXPECT_NE(nullptr, block1); + auto *block2 = static_cast(oc_memb_alloc(&oc_test_data)); + EXPECT_NE(nullptr, block2); + EXPECT_NE(block1, block2); + // Add more assertions as necessary to test initialization of allocated blocks + EXPECT_FALSE(oc_memb_free(&oc_test_data, block2)); + EXPECT_FALSE(oc_memb_free(&oc_test_data, block1)); +} + +TEST_F(TestMemoryBlock, Dealloc_Fail) +{ + EXPECT_EQ(-1, oc_memb_free(nullptr, nullptr)); +} + +TEST_F(TestMemoryBlock, AllocExceedsLimit) +{ + OC_MEMB_LOCAL(oc_test_data, test_data_t, 13); + oc_memb_init(&oc_test_data); + std::array blocks; + for (int i = 0; i < 13; ++i) { + blocks[i] = static_cast(oc_memb_alloc(&oc_test_data)); + EXPECT_NE(nullptr, blocks[i]); + } + blocks[13] = static_cast(oc_memb_alloc(&oc_test_data)); +#ifdef OC_DYNAMIC_ALLOCATION + EXPECT_NE(nullptr, blocks[13]); // Expecting the allocation to succeed + oc_memb_free(&oc_test_data, blocks[13]); +#else /* !OC_DYNAMIC_ALLOCATION */ + EXPECT_EQ(nullptr, + blocks[13]); // Expecting the allocation to fail beyond the limit +#endif /* OC_DYNAMIC_ALLOCATION */ + for (int i = 0; i < 13; ++i) { + oc_memb_free(&oc_test_data, blocks[i]); + } +} + +#ifndef OC_DYNAMIC_ALLOCATION + +TEST_F(TestMemoryBlock, Realloc) +{ + OC_MEMB_LOCAL(oc_test_data, test_data_t, 13); + oc_memb_init(&oc_test_data); + auto *block = static_cast(oc_memb_alloc(&oc_test_data)); + EXPECT_NE(block, nullptr); + oc_memb_free(&oc_test_data, block); + auto *block_realloc = + static_cast(oc_memb_alloc(&oc_test_data)); + EXPECT_EQ(block, block_realloc); + oc_memb_free(&oc_test_data, block_realloc); +} + +TEST_F(TestMemoryBlock, InBlock) +{ + OC_MEMB_LOCAL(oc_test_data1, test_data_t, 13); + oc_memb_init(&oc_test_data1); + OC_MEMB_LOCAL(oc_test_data2, test_data_t, 37); + oc_memb_init(&oc_test_data2); + auto td1 = static_cast(oc_memb_alloc(&oc_test_data1)); + EXPECT_TRUE(oc_memb_inmemb(&oc_test_data1, td1)); + EXPECT_FALSE(oc_memb_inmemb(&oc_test_data2, td1)); + auto td2 = static_cast(oc_memb_alloc(&oc_test_data2)); + EXPECT_TRUE(oc_memb_inmemb(&oc_test_data2, td2)); + EXPECT_FALSE(oc_memb_inmemb(&oc_test_data1, td2)); + + oc_memb_free(&oc_test_data2, td2); + oc_memb_free(&oc_test_data1, td1); +} + +#endif /* !OC_DYNAMIC_ALLOCATION */