From 5fb21343380e3fc8239ca45dc8517a52ec32c683 Mon Sep 17 00:00:00 2001 From: Jarek Pelczar Date: Tue, 21 Nov 2017 14:07:48 +0100 Subject: [PATCH 01/16] Initial source commit Change-Id: Ia5a0abd9f6b64e37aca07ecfb0b3b7476a0f7394 Signed-off-by: Jaroslaw Pelczar --- CMakeLists.txt | 59 +++ LICENSE | 203 ++++++++++ cmake/CStandard.cmake | 65 ++++ cmake/CheckFrameworks.cmake | 60 +++ cmake/dcm_build_config.h.in | 5 + dcm-client/CMakeLists.txt | 83 ++++ dcm-client/dcm_client.h | 82 ++++ dcm-client/dcm_client_p.h | 67 ++++ dcm-client/dcm_hw_interface.cpp | 325 ++++++++++++++++ dcm-client/dcm_hw_interface.h | 91 +++++ dcm-client/dcm_support.proto | 81 ++++ dcm-client/dcmclient.cpp | 385 ++++++++++++++++++ dcm-client/device-certificate-manager.pc.in | 11 + dcm-daemon/CMakeLists.txt | 116 ++++++ dcm-daemon/abstractcryptobackend.cpp | 27 ++ dcm-daemon/abstractcryptobackend.h | 48 +++ dcm-daemon/abstractcryptobackendcontext.cpp | 27 ++ dcm-daemon/abstractcryptobackendcontext.h | 43 ++ dcm-daemon/cryptobackendroster.cpp | 31 ++ dcm-daemon/cryptobackendroster.h | 48 +++ dcm-daemon/dcmserver.cpp | 80 ++++ dcm-daemon/dcmserver.h | 50 +++ dcm-daemon/dcmsession.cpp | 431 +++++++++++++++++++++ dcm-daemon/dcmsession.h | 70 ++++ dcm-daemon/dllresolver.cpp | 107 +++++ dcm-daemon/dllresolver.h | 55 +++ dcm-daemon/dummy-backend/CMakeLists.txt | 54 +++ dcm-daemon/dummy-backend/dummycryptobackend.cpp | 54 +++ dcm-daemon/dummy-backend/dummycryptobackend.h | 43 ++ .../dummy-backend/dummycryptobackendcontext.cpp | 175 +++++++++ .../dummy-backend/dummycryptobackendcontext.h | 48 +++ dcm-daemon/exception_translator.h | 59 +++ dcm-daemon/logging.h | 50 +++ dcm-daemon/main.cpp | 204 ++++++++++ dcm-daemon/see-backend/CMakeLists.txt | 8 + dcm-daemon/see-backend/artik_security.h | 7 + dcm-daemon/see-backend/seebackend.cpp | 103 +++++ dcm-daemon/see-backend/seebackend.h | 59 +++ dcm-daemon/see-backend/seebackendcontext.cpp | 146 +++++++ dcm-daemon/see-backend/seebackendcontext.h | 46 +++ dcm-daemon/serviceadapter.cpp | 102 +++++ dcm-daemon/serviceadapter.h | 43 ++ .../device-certificate-manager-devel.manifest | 7 + .../device-certificate-manager-tests.manifest.in | 9 + packaging/device-certificate-manager.manifest | 5 + packaging/device-certificate-manager.spec | 119 ++++++ shared/boost_log_dlog_sink.h | 108 ++++++ shared/protobuf_asio.cpp | 97 +++++ shared/protobuf_asio.h | 119 ++++++ systemd/CMakeLists.txt | 17 + .../device-certificate-manager-control.socket.in | 13 + systemd/device-certificate-manager.service.in | 14 + systemd/device-certificate-manager.target | 3 + tests/CMakeLists.txt | 11 + tests/example_client.cpp | 39 ++ tests/hw_api_test.cpp | 119 ++++++ tools/CMakeLists.txt | 3 + tools/bin2c.c | 34 ++ 58 files changed, 4568 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 LICENSE create mode 100644 cmake/CStandard.cmake create mode 100644 cmake/CheckFrameworks.cmake create mode 100644 cmake/dcm_build_config.h.in create mode 100644 dcm-client/CMakeLists.txt create mode 100644 dcm-client/dcm_client.h create mode 100644 dcm-client/dcm_client_p.h create mode 100644 dcm-client/dcm_hw_interface.cpp create mode 100644 dcm-client/dcm_hw_interface.h create mode 100644 dcm-client/dcm_support.proto create mode 100644 dcm-client/dcmclient.cpp create mode 100644 dcm-client/device-certificate-manager.pc.in create mode 100644 dcm-daemon/CMakeLists.txt create mode 100644 dcm-daemon/abstractcryptobackend.cpp create mode 100644 dcm-daemon/abstractcryptobackend.h create mode 100644 dcm-daemon/abstractcryptobackendcontext.cpp create mode 100644 dcm-daemon/abstractcryptobackendcontext.h create mode 100644 dcm-daemon/cryptobackendroster.cpp create mode 100644 dcm-daemon/cryptobackendroster.h create mode 100644 dcm-daemon/dcmserver.cpp create mode 100644 dcm-daemon/dcmserver.h create mode 100644 dcm-daemon/dcmsession.cpp create mode 100644 dcm-daemon/dcmsession.h create mode 100644 dcm-daemon/dllresolver.cpp create mode 100644 dcm-daemon/dllresolver.h create mode 100644 dcm-daemon/dummy-backend/CMakeLists.txt create mode 100644 dcm-daemon/dummy-backend/dummycryptobackend.cpp create mode 100644 dcm-daemon/dummy-backend/dummycryptobackend.h create mode 100644 dcm-daemon/dummy-backend/dummycryptobackendcontext.cpp create mode 100644 dcm-daemon/dummy-backend/dummycryptobackendcontext.h create mode 100644 dcm-daemon/exception_translator.h create mode 100644 dcm-daemon/logging.h create mode 100644 dcm-daemon/main.cpp create mode 100644 dcm-daemon/see-backend/CMakeLists.txt create mode 100644 dcm-daemon/see-backend/artik_security.h create mode 100644 dcm-daemon/see-backend/seebackend.cpp create mode 100644 dcm-daemon/see-backend/seebackend.h create mode 100644 dcm-daemon/see-backend/seebackendcontext.cpp create mode 100644 dcm-daemon/see-backend/seebackendcontext.h create mode 100644 dcm-daemon/serviceadapter.cpp create mode 100644 dcm-daemon/serviceadapter.h create mode 100644 packaging/device-certificate-manager-devel.manifest create mode 100644 packaging/device-certificate-manager-tests.manifest.in create mode 100644 packaging/device-certificate-manager.manifest create mode 100644 packaging/device-certificate-manager.spec create mode 100644 shared/boost_log_dlog_sink.h create mode 100644 shared/protobuf_asio.cpp create mode 100644 shared/protobuf_asio.h create mode 100644 systemd/CMakeLists.txt create mode 100644 systemd/device-certificate-manager-control.socket.in create mode 100644 systemd/device-certificate-manager.service.in create mode 100644 systemd/device-certificate-manager.target create mode 100644 tests/CMakeLists.txt create mode 100644 tests/example_client.cpp create mode 100644 tests/hw_api_test.cpp create mode 100644 tools/CMakeLists.txt create mode 100644 tools/bin2c.c diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..5027e44 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,59 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.6) + +##### Configure project version, especially when using +##### outdated tools like CMake v2 + +IF(POLICY CMP0048) + CMAKE_POLICY(SET CMP0048 NEW) +ENDIF() + +IF(POLICY CMP0069) + CMAKE_POLICY(SET CMP0069 NEW) +ENDIF() + +IF(CMAKE_VERSION VERSION_LESS 3.0) + PROJECT(device-certificate-manager CXX C) + SET(PROJECT_VERSION "1.0") +ELSE() + PROJECT(device-certificate-manager VERSION 1.0 LANGUAGES C CXX) +ENDIF() + +INCLUDE(GNUInstallDirs) + +IF(NOT (CMAKE_VERSION VERSION_LESS 3.9)) + INCLUDE(CheckIPOSupported) + check_ipo_supported(RESULT IPO_ALLOWED) +ELSE() + SET(IPO_ALLOWED YES) +ENDIF() + +find_package(Threads REQUIRED) + +INCLUDE(cmake/CheckFrameworks.cmake) +INCLUDE(cmake/CStandard.cmake) + +SET(ENABLE_DUMMY_BACKEND OFF) + +IF(NOT ARTIK_SECURITY_FOUND) + SET(ENABLE_DUMMY_BACKEND ON) + message(WARNING "Dummy backend enabled as no usable frameworks found") +ENDIF() + +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/dcm_build_config.h.in + ${CMAKE_CURRENT_BINARY_DIR}/dcm_build_config.h) + +configure_file(packaging/device-certificate-manager-tests.manifest.in + ${CMAKE_CURRENT_BINARY_DIR}/device-certificate-manager-tests.manifest) + +include_directories(${CMAKE_CURRENT_BINARY_DIR}) +include_directories(shared) + +SET(DCM_UNIX_SOCKET_PATH "/run/device-certificate-manager.socket") +add_definitions(-DDCM_UNIX_SOCKET_PATH="${DCM_UNIX_SOCKET_PATH}") + +add_subdirectory(dcm-daemon) +add_subdirectory(dcm-client) +add_subdirectory(tests) +add_subdirectory(tools) + +add_subdirectory(systemd) diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..8534b2c --- /dev/null +++ b/LICENSE @@ -0,0 +1,203 @@ +Copyright (c) 2000 - 2015 Samsung Electronics Co., Ltd. All rights reserved. + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/cmake/CStandard.cmake b/cmake/CStandard.cmake new file mode 100644 index 0000000..dd407d9 --- /dev/null +++ b/cmake/CStandard.cmake @@ -0,0 +1,65 @@ +IF(CMAKE_VERSION VERSION_LESS 3.1) + include(CheckCXXCompilerFlag) + include(CheckCCompilerFlag) + + message(STATUS "Using old cmake, will detect C++11 and C11 manually") + + CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11) + CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X) + CHECK_CXX_COMPILER_FLAG("-std=gnu++11" COMPILER_SUPPORTS_GNUXX11) + CHECK_CXX_COMPILER_FLAG("-std=gnu++0x" COMPILER_SUPPORTS_GNUXX0X) + CHECK_CXX_COMPILER_FLAG("/std:c++14" COMPILER_SUPPORTS_STDCXX14) + CHECK_CXX_COMPILER_FLAG("/std:c++latest" COMPILER_SUPPORTS_STDCXXLATEST) + + IF(COMPILER_SUPPORTS_CXX11) + SET(OLD_CMAKE_CXXFLAGS_CXX11 "-std=c++11") + ELSEIF(COMPILER_SUPPORTS_CXX0X) + SET(OLD_CMAKE_CXXFLAGS_CXX11 "-std=c++0x") + ELSEIF(COMPILER_SUPPORTS_GNUXX11) + SET(OLD_CMAKE_CXXFLAGS_CXX11 "-std=gnu++11") + ELSEIF(COMPILER_SUPPORTS_GNUXX0X) + SET(OLD_CMAKE_CXXFLAGS_CXX11 "-std=gnu++0x") + ELSEIF(COMPILER_SUPPORTS_STDCXXLATEST) + SET(OLD_CMAKE_CXXFLAGS_CXX11 "/std:c++latest") + ELSEIF(COMPILER_SUPPORTS_STDCXX14) + SET(OLD_CMAKE_CXXFLAGS_CXX11 "/std:c++14") + ELSE() + MESSAGE(FATAL_ERROR "No known way to enable C++11. Please upgrade cmake or compiler") + ENDIF() + + CHECK_C_COMPILER_FLAG("-std=c11" COMPILER_SUPPORTS_C11) + CHECK_C_COMPILER_FLAG("-std=gnu11" COMPILER_SUPPORTS_GNU11) + + IF(COMPILER_SUPPORTS_C11) + SET(OLD_CMAKE_CLAGS_C11 "-std=c11") + ELSEIF(COMPILER_SUPPORTS_GNU11) + SET(OLD_CMAKE_CLAGS_C11 "-std=gnu11") + ELSE() + IF((CMAKE_C_COMPILER_ID MATCHES "Clang") OR (CMAKE_C_COMPILER_ID MATCHES "GNU")) + MESSAGE(FATAL_ERROR "Can't find way to enable C11") + ENDIF() + ENDIF() +ENDIF() + +MACRO(ApplyCxx11Standard TargetName) + IF(CMAKE_VERSION VERSION_LESS 3.1) + IF(NOT __OLD_CMAKE_CXX11_ALREADY_APPLIED) + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OLD_CMAKE_CXXFLAGS_CXX11}") + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OLD_CMAKE_CLAGS_C11}") + SET(__OLD_CMAKE_CXX11_ALREADY_APPLIED TRUE) + ENDIF() + ELSE() + set_property(TARGET ${TargetName} + PROPERTY + CXX_STANDARD 11) + set_property(TARGET ${TargetName} + PROPERTY + CXX_STANDARD_REQUIRED TRUE) + set_property(TARGET ${TargetName} + PROPERTY + C_STANDARD 11) + set_property(TARGET ${TargetName} + PROPERTY + C_STANDARD_REQUIRED TRUE) + ENDIF() +ENDMACRO(ApplyCxx11Standard) diff --git a/cmake/CheckFrameworks.cmake b/cmake/CheckFrameworks.cmake new file mode 100644 index 0000000..ff961e0 --- /dev/null +++ b/cmake/CheckFrameworks.cmake @@ -0,0 +1,60 @@ +INCLUDE(CheckIncludeFile) +INCLUDE(CheckIncludeFileCXX) +INCLUDE(CheckLibraryExists) +INCLUDE(CheckFunctionExists) +INCLUDE(CheckIncludeFiles) + +CHECK_INCLUDE_FILE("pkix_interface.h" HAVE_PKIX_INTERFACE) + +FIND_PACKAGE(Boost 1.54 + REQUIRED + COMPONENTS + serialization + filesystem + program_options + log + thread + system + ) + +FIND_PACKAGE(PkgConfig REQUIRED) + +PKG_CHECK_MODULES(ARTIK_SECURITY artik-security) + +IF(ARTIK_SECURITY_FOUND) + SET(HAVE_ARTIK_SECURITY_LIBRARY TRUE) +ENDIF() + +PKG_CHECK_MODULES(DLOG dlog) + +PKG_CHECK_MODULES(SECURITY_MANAGER security-manager) + +CHECK_FUNCTION_EXISTS(fork HAVE_FORK) + +find_package(Protobuf REQUIRED) + +#### Find mbedtls #### + +find_library(MBEDTLS_LIB + mbedtls) + +find_library(MBEDCRYPTO_LIB + mbedcrypto) + +IF(MBEDTLS_LIB-NOTFOUND) + message(FATAL_ERROR "mbedtls not found ...") +ENDIF() + +IF(MBEDCRYPTO_LIB-NOTFOUND) + message(FATAL_ERROR "mbedcrypto not found ...") +ENDIF() + +CHECK_INCLUDE_FILES("mbedtls/rsa.h;mbedtls/ecdsa.h" MBEDTLS_HEADERS_OK) + +IF(NOT MBEDTLS_HEADERS_OK) + message(FATAL_ERROR "No mbedtls headers") +ENDIF() + +PKG_CHECK_MODULES(SYSTEMD libsystemd) + +PKG_CHECK_MODULES(SMACK libsmack) diff --git a/cmake/dcm_build_config.h.in b/cmake/dcm_build_config.h.in new file mode 100644 index 0000000..fe814a1 --- /dev/null +++ b/cmake/dcm_build_config.h.in @@ -0,0 +1,5 @@ +#cmakedefine HAVE_PKIX_INTERFACE +#cmakedefine HAVE_ARTIK_SECURITY_LIBRARY +#cmakedefine HAVE_FORK + +#define PROJECT_VERSION "@PROJECT_VERSION@" diff --git a/dcm-client/CMakeLists.txt b/dcm-client/CMakeLists.txt new file mode 100644 index 0000000..cb61bc4 --- /dev/null +++ b/dcm-client/CMakeLists.txt @@ -0,0 +1,83 @@ +# +# DCM client library build script +# Jaroslaw Pelczar +# + +include(GenerateExportHeader) + +configure_file(device-certificate-manager.pc.in + ${CMAKE_CURRENT_BINARY_DIR}/device-certificate-manager.pc + @ONLY) + +###### Include and library directories ###### + +IF(DLOG_FOUND) + include_directories(${DLOG_INCLUDE_DIRS}) + link_directories(${DLOG_LIBRARY_DIRS}) + add_definitions(-DUSE_DLOG_LOGGING=1) +ENDIF() + +include_directories(${PROTOBUF_INCLUDE_DIRS}) +link_directories(${PROTOBUF_LIBRARY_DIRS}) + +include_directories(${Boost_INCLUDE_DIRS}) +link_directories(${Boost_LIBRARY_DIRS}) + +include_directories(${CMAKE_CURRENT_BINARY_DIR}) + +###### Protobuf generator ####### + +PROTOBUF_GENERATE_CPP(PROTO_SRCS PROTO_HDRS dcm_support.proto) + +###### Library sources ###### + +add_library(device-certificate-manager + SHARED + dcmclient.cpp + dcm_hw_interface.cpp + ../shared/protobuf_asio.cpp + ${PROTO_SRCS} + ${PROTO_HDRS}) + +###### Export header generation ###### + +GENERATE_EXPORT_HEADER(device-certificate-manager + BASE_NAME DEVICE_CERTIFICATE_MANAGER + PREFIX_NAME API_ + ) + +###### Linking ###### + +ApplyCxx11Standard(device-certificate-manager) + +target_link_libraries(device-certificate-manager + ${Boost_LIBRARIES} + ${CMAKE_THREAD_LIBS_INIT} + ${PROTOBUF_LIBRARIES} + ${MBEDTLS_LIB} + ${MBEDCRYPTO_LIB}) + +IF(DLOG_FOUND) + target_link_libraries(device-certificate-manager ${DLOG_LIBRARIES}) +ENDIF() + +###### Properties of library ###### + +set_property(TARGET device-certificate-manager PROPERTY DEFINE_SYMBOL DEVICE_CERTIFICATE_MANAGER_EXPORT) +set_property(TARGET device-certificate-manager PROPERTY VISIBILITY_INLINES_HIDDEN TRUE) +set_property(TARGET device-certificate-manager PROPERTY VERSION 1.0) +set_property(TARGET device-certificate-manager PROPERTY C_VISIBILITY_PRESET hidden) +set_property(TARGET device-certificate-manager PROPERTY CXX_VISIBILITY_PRESET hidden) + +###### Installation ###### + +install(TARGETS device-certificate-manager + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) + +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/device_certificate_manager_export.h + dcm_client.h + dcm_hw_interface.h + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/device-certificate-manager) + +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/device-certificate-manager.pc + DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) diff --git a/dcm-client/dcm_client.h b/dcm-client/dcm_client.h new file mode 100644 index 0000000..1531f81 --- /dev/null +++ b/dcm-client/dcm_client.h @@ -0,0 +1,82 @@ +/****************************************************************** + * + * Copyright 2017 Samsung Electronics All Rights Reserved. + * + * Author: Jaroslaw Pelczar + * + * 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 DCM_CLIENT_DCM_CLIENT_H_ +#define DCM_CLIENT_DCM_CLIENT_H_ + +#include "device_certificate_manager_export.h" +#include +#include +#include + +class API_DEVICE_CERTIFICATE_MANAGER_EXPORT dcm_client_connection : + public std::enable_shared_from_this +{ +private: + dcm_client_connection(const dcm_client_connection&) = delete; + dcm_client_connection& operator = (const dcm_client_connection&) = delete; + +protected: + dcm_client_connection() API_DEVICE_CERTIFICATE_MANAGER_NO_EXPORT; + virtual ~dcm_client_connection() API_DEVICE_CERTIFICATE_MANAGER_NO_EXPORT; + +public: + /*! + * Initialize default instance of the client connection. + * + * Standard C++ exceptions may be thrown in case of error + */ + static std::shared_ptr create(); + + /*! + * Associate key context with this object. This function can be called + * only once. This function doesn't throw any exceptions. + */ + virtual bool create_context(const std::string& serviceName, + const std::string& usage, + const std::string& key_type) noexcept = 0; + + + /*! + * Request certificate chain associated with this context + */ + virtual int get_certificate_chain(std::vector& chain) noexcept = 0; + + /*! + * Return name of key type (UNKNOWN, RSA or ECDSA for now) + */ + virtual const std::string& key_type() const noexcept = 0; + + /*! + * Return length of the crypto key in bits + */ + virtual unsigned int key_length() const noexcept = 0; + + /*! + * Sign data with context certificate + * + * This function returns error codes from the mbedtls error value space + */ + virtual int sign_data(mbedtls_md_type_t digestType, + const void * hash_data, size_t hash_size, + std::vector& digest) noexcept = 0; +}; + +#endif /* DCM_CLIENT_DCM_CLIENT_H_ */ diff --git a/dcm-client/dcm_client_p.h b/dcm-client/dcm_client_p.h new file mode 100644 index 0000000..f468e39 --- /dev/null +++ b/dcm-client/dcm_client_p.h @@ -0,0 +1,67 @@ +/****************************************************************** + * + * Copyright 2017 Samsung Electronics All Rights Reserved. + * + * Author: Jaroslaw Pelczar + * + * 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 DCM_CLIENT_DCM_CLIENT_P_H_ +#define DCM_CLIENT_DCM_CLIENT_P_H_ + +#include "dcm_client.h" +#include "dcm_support.pb.h" +#include +#include +#include +#include + +class API_DEVICE_CERTIFICATE_MANAGER_NO_EXPORT dcm_client_connection_impl final : public dcm_client_connection +{ +public: + dcm_client_connection_impl(); + dcm_client_connection_impl(boost::asio::io_service& ioService); + virtual ~dcm_client_connection_impl(); + + bool create_context(const std::string& serviceName, + const std::string& usage, + const std::string& key_type) noexcept override; + + int get_certificate_chain(std::vector& chain) noexcept override; + + const std::string& key_type() const noexcept override; + + unsigned int key_length() const noexcept override; + + int sign_data(mbedtls_md_type_t digestType, + const void * hash_data, size_t hash_size, + std::vector& digest) noexcept override; + + void sendReceive(RequestMessage& request, ResponseMessage& response); + +private: + void ensureSocketConnected(); + + +private: + boost::asio::io_service fIOService; + std::mutex fLock; + uint64_t fCookie = 0; + std::unique_ptr fSocket; + CryptoKeyType fKeyType = CRYPTO_KEY_TYPE_INVALID; + unsigned int fKeyLength = 0; +}; + +#endif /* DCM_CLIENT_DCM_CLIENT_P_H_ */ diff --git a/dcm-client/dcm_hw_interface.cpp b/dcm-client/dcm_hw_interface.cpp new file mode 100644 index 0000000..3e2ad58 --- /dev/null +++ b/dcm-client/dcm_hw_interface.cpp @@ -0,0 +1,325 @@ +/****************************************************************** + * + * Copyright 2017 Samsung Electronics All Rights Reserved. + * + * Author: Jaroslaw Pelczar + * + * 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 "dcm_hw_interface.h" +#include "dcm_client.h" +#include +#include +#include +#include +#include +#include +#include + +#ifdef USE_DLOG_LOGGING +#define LOG_TAG "dcm-client" +#include +#endif + +struct dcm_key_context_internal { + std::shared_ptr connection; + std::vector cached_cert_chain; + mbedtls_pk_info_t ec_info; +}; + +static std::map> sEDCSAContexts; +static std::mutex sEDCSAContextsMutex; + +void* DCM_HWGetKeyContext(const char* service, const char* usage, const char* keytype) { +#ifdef USE_DLOG_LOGGING + LOGD("Create new context for"); +#endif + + try { + std::unique_ptr context(new dcm_key_context_internal()); + + std::string service_string(service ? service : ""); + std::string usage_string(usage ? usage : ""); + std::string keytype_string(keytype ? keytype : ""); + + context->connection = dcm_client_connection::create(); + + if(!context->connection->create_context(service_string, + usage_string, + keytype_string)) + { +#ifdef USE_DLOG_LOGGING + LOGE("Can't create connection context"); +#endif + return nullptr; + } + +#ifdef USE_DLOG_LOGGING + LOGD("Created context %p", context.get()); +#endif + + return context.release(); + } catch(...) { +#ifdef USE_DLOG_LOGGING + LOGE("Context creation failure"); +#endif + return nullptr; + } +} + +int DCM_HWFreeKeyContext(void* keyContext) +{ + if(!keyContext) { + return HWIF_ERR_INVALID_PARAM; + } + +#ifdef USE_DLOG_LOGGING + LOGD("Delete context %p", keyContext); +#endif + + delete reinterpret_cast(keyContext); + + return HWIF_SUCCESS; +} + +int DCM_HWGetOwnCertificateChain(const void* keyContext, + unsigned char** cert_chain, size_t* cert_chain_len) +{ + dcm_key_context_internal * context = reinterpret_cast( + const_cast(keyContext)); + + if(!keyContext || !cert_chain || !cert_chain_len) { + return HWIF_ERR_INVALID_PARAM; + } + +#ifdef USE_DLOG_LOGGING + LOGD("Request certificate chain for session %p", keyContext); +#endif + + if(!context->cached_cert_chain.empty()) { +#ifdef USE_DLOG_LOGGING + LOGD("Use cached certificate chain"); +#endif + + *cert_chain = &context->cached_cert_chain[0]; + *cert_chain_len = context->cached_cert_chain.size(); + return HWIF_SUCCESS; + } + + int result; + + if((result = context->connection->get_certificate_chain(context->cached_cert_chain)) == 0) { + *cert_chain = &context->cached_cert_chain[0]; + *cert_chain_len = context->cached_cert_chain.size(); + } + + return result; +} + +static int pk_rsa_alt_decrypt_func( void *ctx, int mode, size_t *olen, + const unsigned char *input, unsigned char *output, + size_t output_max_len ) +{ +#ifdef USE_DLOG_LOGGING + LOGE("Can't use this API to decrypt RSA data"); +#endif + + return MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE; +} + +static int pk_rsa_alt_sign_func( void *ctx, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng, + int mode, mbedtls_md_type_t md_alg, unsigned int hashlen, + const unsigned char *hash, unsigned char *sig ) +{ + if(!ctx) { + return MBEDTLS_ERR_PK_BAD_INPUT_DATA; + } + + if(mode != MBEDTLS_RSA_PRIVATE) { + return MBEDTLS_ERR_PK_BAD_INPUT_DATA; + } + + dcm_key_context_internal * context = reinterpret_cast(ctx); + + try { + std::vector digest; + + int error = context->connection->sign_data(md_alg, + hash, + hashlen, + digest); + + if(error == 0) { + if(digest.size() > MBEDTLS_MPI_MAX_SIZE) { + return MBEDTLS_ERR_PK_BAD_INPUT_DATA; + } + + memcpy(sig, &digest[0], digest.size()); + } + + return error; + } catch(...) { + return MBEDTLS_ERR_PK_ALLOC_FAILED; + } +} + + +static size_t pk_rsa_alt_key_len_func( void *ctx ) +{ + if(!ctx) { + return MBEDTLS_ERR_PK_BAD_INPUT_DATA; + } + + dcm_key_context_internal * context = reinterpret_cast(ctx); + + return (context->connection->key_length() + 7) / 8; +} + +static int SetupRSAContext(mbedtls_pk_context* ctx, void* key_context) { + return mbedtls_pk_setup_rsa_alt(ctx, + key_context, + pk_rsa_alt_decrypt_func, + pk_rsa_alt_sign_func, + pk_rsa_alt_key_len_func) ? HWIF_ERROR : HWIF_SUCCESS; +} + +static int ecdsa_sign_alt( void *ctx, mbedtls_md_type_t md_alg, + const unsigned char *hash, size_t hash_len, + unsigned char *sig, size_t *sig_len, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng ) +{ + if(!ctx) { + return MBEDTLS_ERR_PK_BAD_INPUT_DATA; + } + + std::unique_lock locker(sEDCSAContextsMutex); + auto context = sEDCSAContexts[ctx].lock(); + + if(!context) { +#ifdef USE_DLOG_LOGGING + LOGE("Trying to sign ECDSA data on deleted context"); +#endif + return MBEDTLS_ERR_PK_BAD_INPUT_DATA; + } + + locker.unlock(); + + try { + std::vector digest; + + int error = context->sign_data(md_alg, + hash, + hash_len, + digest); + + if(error == 0) { + if(digest.size() > MBEDTLS_MPI_MAX_SIZE) { + return MBEDTLS_ERR_PK_BAD_INPUT_DATA; + } + + memcpy(sig, &digest[0], digest.size()); + *sig_len = digest.size(); + } + + return error; + } catch(...) { + return MBEDTLS_ERR_PK_ALLOC_FAILED; + } +} + +static void eckey_free_wrap_alt( void *ctx ) +{ + const mbedtls_pk_info_t *mbedtls_ec_info; + mbedtls_ec_info = mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY); + mbedtls_ec_info->ctx_free_func(ctx); + std::unique_lock locker(sEDCSAContextsMutex); + sEDCSAContexts.erase(ctx); +} + +static size_t eckey_get_bitlen_alt( const void *ctx ) +{ + std::unique_lock locker(sEDCSAContextsMutex); + auto context = sEDCSAContexts[ctx].lock(); + + if(!context) { +#ifdef USE_DLOG_LOGGING + LOGE("Trying to sign ECDSA data on deleted context"); +#endif + return 0; + } + + return context->key_length(); +} + +static int SetupECDSAContext(mbedtls_pk_context* ctx, void* key_context) { + const mbedtls_pk_info_t *mbedtls_ec_info; + dcm_key_context_internal * context = reinterpret_cast(key_context); + + mbedtls_ec_info = mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY); + + if(!mbedtls_ec_info) { + return HWIF_ERROR; + } + + context->ec_info = *mbedtls_ec_info; + context->ec_info.sign_func = ecdsa_sign_alt; + context->ec_info.ctx_free_func = eckey_free_wrap_alt; + context->ec_info.get_bitlen = eckey_get_bitlen_alt; + + if(mbedtls_pk_setup(ctx, &context->ec_info)) { + return HWIF_ERROR; + } + + try { + std::unique_lock locker(sEDCSAContextsMutex); + sEDCSAContexts.emplace(ctx->pk_ctx, context->connection); + } catch(...) { +#ifdef USE_DLOG_LOGGING + LOGE("Got exception when inserting to map"); +#endif + mbedtls_pk_free(ctx); + return HWIF_ERR_OUT_OF_MEMORY; + } + + return HWIF_SUCCESS; +} + +int DCM_HWSetupPkContext(mbedtls_pk_context* ctx, void* key_context) +{ + dcm_key_context_internal * context = reinterpret_cast(key_context); + + if(!context || !ctx) { + return HWIF_ERR_INVALID_PARAM; + } + +#ifdef USE_DLOG_LOGGING + LOGD("Setup PK context %p with key context %p", ctx, key_context); +#endif + + const auto& key_type(context->connection->key_type()); + + if(key_type == "RSA") { + return SetupRSAContext(ctx, key_context); + } else if(key_type == "ECDSA") { + return SetupECDSAContext(ctx, key_context); + } else { +#ifdef USE_DLOG_LOGGING + LOGE("Unsupported key type received from server"); +#endif + } + + return HWIF_ERR_INVALID_PARAM; +} diff --git a/dcm-client/dcm_hw_interface.h b/dcm-client/dcm_hw_interface.h new file mode 100644 index 0000000..55239a0 --- /dev/null +++ b/dcm-client/dcm_hw_interface.h @@ -0,0 +1,91 @@ +/****************************************************************** + * + * Copyright 2017 Samsung Electronics All Rights Reserved. + * + * Author: Jaroslaw Pelczar + * + * 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 DCM_CLIENT_DCM_HW_INTERFACE_H_ +#define DCM_CLIENT_DCM_HW_INTERFACE_H_ + +#include "device_certificate_manager_export.h" +#include +#include + +/** + * Error-definition for hw interface + */ + +typedef enum HwifResult { + HWIF_ERR_INVALID_PARAM = -EINVAL, /**< Invalid Paramter */ + HWIF_ERR_OUT_OF_MEMORY = -ENOMEM, /**< Out of memory */ + HWIF_ERR_NO_DATA = -ENODATA, /**< No data found */ + HWIF_ERROR = -EFAULT, /**< Internal Error */ + HWIF_SUCCESS = 0, /**< No Error */ +} HwifResult_t; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * This callback will be invoked to get a key context based on specific name indication + * (service name, key usage, key type). The key context may be same with the alias name. + * + * @param[in] service service name indicates first category name + * @param[in] usage usage name indicates sub-category name + * @param[in] keytype (optional) key type name indication if any, otherwise it usually will be NULL + * @return void type pointer value on success, otherwise NULL + */ +API_DEVICE_CERTIFICATE_MANAGER_EXPORT void* DCM_HWGetKeyContext(const char* service, const char* usage, const char* keytype); + +/** + * This callback will deallocate the key context that was retrieved from TZ + * by calling GetHwKeyContext callback. + * + * @param[in] keyContext key context object to be deallocated, + * which was obtained from GetHwKeyContext callback function + * @return 0 on success, otherwise a negative value + */ +API_DEVICE_CERTIFICATE_MANAGER_EXPORT int DCM_HWFreeKeyContext(void* keyContext); + +/** + * This callback will be invoked to load own(i.e., pre-injected) certificate from HW(e.g., TZ, eSE) + * + * @param[in] keyContext key context object that identifies proper certificate chain + * @param[out] cert_chain certificate chain in binary + * @param[out] cert_chain_len total length of certificate chain + * @return 0 on success, otherwise a negative value + */ +API_DEVICE_CERTIFICATE_MANAGER_EXPORT int DCM_HWGetOwnCertificateChain(const void* keyContext, + unsigned char** cert_chain, size_t* cert_chain_len); + +/** + * This callback should provide setting up alternative functions (e.g., rsa_sign, key_len, etc) + * of which HW(e.g., TZ, eSE) management library to the specified mbedtls context + * that will be used during handshake. + * + * @param[in] ctx pointer of pk context of mbedtls + * @param[in] keyContext key context object that identifies proper public/private key + * @return 0 on success, otherwise a negative value + */ +API_DEVICE_CERTIFICATE_MANAGER_EXPORT int DCM_HWSetupPkContext(mbedtls_pk_context* ctx, void* key_context); + +#ifdef __cplusplus +} +#endif + +#endif /* DCM_CLIENT_DCM_HW_INTERFACE_H_ */ diff --git a/dcm-client/dcm_support.proto b/dcm-client/dcm_support.proto new file mode 100644 index 0000000..3487971 --- /dev/null +++ b/dcm-client/dcm_support.proto @@ -0,0 +1,81 @@ +syntax = "proto2"; + +/* + * Type of the crypto key + */ +enum CryptoKeyType { + CRYPTO_KEY_TYPE_INVALID = 0; + CRYPTO_KEY_TYPE_ECDSA = 1; + CRYPTO_KEY_TYPE_RSA = 2; +} + +/* + * These values match the mbedtls ones + */ +enum MessageDigestType { + MD_NONE = 0; + MD_MD2 = 1; + MD_MD4 = 2; + MD_MD5 = 3; + MD_SHA1 = 4; + MD_SHA224 = 5; + MD_SHA256 = 6; + MD_SHA384 = 7; + MD_SHA512 = 8; + MD_RIPEMD160 = 9; +} + +message AssociateKeyContext +{ + required string service = 1; + required string usage = 2; + required string key_type = 3; +} + +message AssociateKeyContextResponse +{ + required int32 result = 1; + optional uint64 context_cookie = 2; + optional CryptoKeyType key_type = 3; + optional uint32 key_length = 4; +} + +message RequestCertificateChain +{ + required uint64 context_cookie = 1; +} + +message RequestCertificateChainResponse +{ + required int32 result = 1; + optional bytes cert_chain = 2; +} + +message SignRequest +{ + required uint64 context_cookie = 1; + required bytes data_to_sign = 2; + required MessageDigestType digest_type = 3; +} + +message SignResponse +{ + required int32 result = 1; + optional bytes signature = 2; +} + +message RequestMessage { + oneof request_oneof { + AssociateKeyContext associate_context = 1; + RequestCertificateChain request_chain = 2; + SignRequest sign_data = 3; + } +} + +message ResponseMessage { + oneof reply_oneof { + AssociateKeyContextResponse associate_context = 1; + RequestCertificateChainResponse request_chain = 2; + SignResponse sign_data = 3; + } +} diff --git a/dcm-client/dcmclient.cpp b/dcm-client/dcmclient.cpp new file mode 100644 index 0000000..22f9bb9 --- /dev/null +++ b/dcm-client/dcmclient.cpp @@ -0,0 +1,385 @@ +/****************************************************************** + * + * Copyright 2017 Samsung Electronics All Rights Reserved. + * + * Author: Jaroslaw Pelczar + * + * 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 "dcm_client_p.h" +#include "dcm_support.pb.h" +#include "dcm_hw_interface.h" +#include +#include +#include +#include +#include +#ifdef USE_DLOG_LOGGING +#define LOG_TAG "dcm-client" +#include +#endif + +static_assert(MD_NONE == (unsigned int)MBEDTLS_MD_NONE, "MBEDTLS_MD_NONE mismatch"); +static_assert(MD_MD2 == (unsigned int)MBEDTLS_MD_MD2, "MBEDTLS_MD_MD2 mismatch"); +static_assert(MD_MD4 == (unsigned int)MBEDTLS_MD_MD4, "MBEDTLS_MD_MD4 mismatch"); +static_assert(MD_MD5 == (unsigned int)MBEDTLS_MD_MD5, "MBEDTLS_MD_MD5 mismatch"); +static_assert(MD_SHA1 == (unsigned int)MBEDTLS_MD_SHA1, "MBEDTLS_MD_SHA1 mismatch"); +static_assert(MD_SHA224 == (unsigned int)MBEDTLS_MD_SHA224, "MBEDTLS_MD_SHA224 mismatch"); +static_assert(MD_SHA256 == (unsigned int)MBEDTLS_MD_SHA256, "MBEDTLS_MD_SHA256 mismatch"); +static_assert(MD_SHA384 == (unsigned int)MBEDTLS_MD_SHA384, "MBEDTLS_MD_SHA384 mismatch"); +static_assert(MD_SHA512 == (unsigned int)MBEDTLS_MD_SHA512, "MBEDTLS_MD_SHA512 mismatch"); +static_assert(MD_RIPEMD160 == (unsigned int)MBEDTLS_MD_RIPEMD160, "MBEDTLS_MD_RIPEMD160 mismatch"); + +static std::string sKeyTypeUnknown("UNKNOWN"); +static std::string sKeyTypeRSA("RSA"); +static std::string sKeyTypeECDSA("ECDSA"); + +dcm_client_connection_impl::dcm_client_connection_impl() +{ +} + +dcm_client_connection_impl::~dcm_client_connection_impl() +{ +} + +dcm_client_connection::dcm_client_connection() +{ +#ifdef USE_DLOG_LOGGING + LOGD("dcm_client_connection: Allocated new client connection at %p", this); +#endif +} + +dcm_client_connection::~dcm_client_connection() +{ +#ifdef USE_DLOG_LOGGING + LOGD("dcm_client_connection: Deallocated client connection at %p", this); +#endif +} + +std::shared_ptr dcm_client_connection::create() +{ + return std::make_shared(); +} + +void dcm_client_connection_impl::sendReceive(RequestMessage& request, ResponseMessage& response) +{ +#ifdef USE_DLOG_LOGGING + LOGD("Send request to server in connection %p of type %d", this, request.request_oneof_case()); +#endif + + protobuf_sync_message_serialization(*fSocket).encodeMessage(request); + + protobuf_sync_message_deserialization(*fSocket).decodeMessage(response); + +#ifdef USE_DLOG_LOGGING + LOGD("Received response from server in connection %p of type %d", this, response.reply_oneof_case()); +#endif +} + +bool dcm_client_connection_impl::create_context(const std::string& serviceName, + const std::string& usage, + const std::string& key_type) noexcept +{ + std::lock_guard locker(fLock); + + if(fCookie) { +#ifdef USE_DLOG_LOGGING + LOGE("%s: Cookie has already been requested for session %p", __FUNCTION__, this); +#endif + // Already created + return false; + } + + if(!fSocket) { +#ifdef USE_DLOG_LOGGING + LOGD("%s: Ensure that socket is connected for session %p", __FUNCTION__, this); +#endif + try { + ensureSocketConnected(); + } catch(std::exception& ex) { +#ifdef USE_DLOG_LOGGING + LOGE("%s: Caught exception \"%s\" when connecting socket for session %p", __FUNCTION__, ex.what(), this); +#endif + return false; + } catch(...) { +#ifdef USE_DLOG_LOGGING + LOGE("%s: Caught unknown exception when connecting socket for session %p", __FUNCTION__, this); +#endif + return false; + } + } + +#ifdef USE_DLOG_LOGGING + LOGD("%s: Connection established. Requesting cookie", __FUNCTION__); +#endif + + try { + RequestMessage request; + ResponseMessage response; + + auto * assoc_req = request.mutable_associate_context(); + + assoc_req->set_service(serviceName); + assoc_req->set_usage(usage); + assoc_req->set_key_type(key_type); + + sendReceive(request, response); + + if(!response.has_associate_context()) { +#ifdef USE_DLOG_LOGGING + LOGE("%s: received response is not context association message in context %p", __FUNCTION__, this); +#endif + return false; + } + + auto& assoc_message(response.associate_context()); + + if(assoc_message.result() != 0) { +#ifdef USE_DLOG_LOGGING + LOGE("%s: Received context association message with error %d in %p", __FUNCTION__, assoc_message.result(), this); +#endif + return false; + } + + fCookie = assoc_message.context_cookie(); + fKeyType = assoc_message.key_type(); + fKeyLength = assoc_message.key_length(); + +#ifdef USE_DLOG_LOGGING + LOGD("%s: Received cookie %" PRIx64 " with key type %s and length %zd for session %p", + __FUNCTION__, + fCookie, + this->key_type().c_str(), + fKeyLength, + this); +#endif + } catch(std::exception& ex) { +#ifdef USE_DLOG_LOGGING + LOGE("%s: Caught exception \"%s\" when establishing cookie for session %p", __FUNCTION__, ex.what(), this); +#endif + fSocket.reset(); + return false; + } catch(...) { +#ifdef USE_DLOG_LOGGING + LOGE("%s: Caught unknown exception when establishing cookie for session %p", __FUNCTION__, this); +#endif + fSocket.reset(); + return false; + } + + return true; +} + +void dcm_client_connection_impl::ensureSocketConnected() +{ + fSocket.reset(new boost::asio::local::stream_protocol::socket(fIOService)); + boost::asio::local::stream_protocol::endpoint endpoint(DCM_UNIX_SOCKET_PATH); + fSocket->connect(endpoint); +} + +int dcm_client_connection_impl::get_certificate_chain(std::vector& chain) noexcept +{ + std::lock_guard locker(fLock); + + if(!fCookie) { +#ifdef USE_DLOG_LOGGING + LOGE("%s: Trying to request certificate in session %p without connection", __FUNCTION__, this); +#endif + return HWIF_ERR_INVALID_PARAM; + } + +#ifdef USE_DLOG_LOGGING + LOGD("%s: Requesting certificate chain for session %p", __FUNCTION__, this); +#endif + + try { + RequestMessage request; + ResponseMessage response; + + auto * cert_req = request.mutable_request_chain(); + + cert_req->set_context_cookie(fCookie); + + sendReceive(request, response); + + if(!response.has_request_chain()) { +#ifdef USE_DLOG_LOGGING + LOGE("%s: Response from server is not certificate chain response on session %p", __FUNCTION__, this); +#endif + return HWIF_ERR_NO_DATA; + } + + auto& cert_resp(response.request_chain()); + + if(cert_resp.result() != 0) { +#ifdef USE_DLOG_LOGGING + LOGE("%s: Server can't respond with certificate chain: error %d in session %p", __FUNCTION__, + cert_resp.result(), this); +#endif + return HWIF_ERR_NO_DATA; + } + + if(cert_resp.cert_chain().size() == 0) { +#ifdef USE_DLOG_LOGGING + LOGE("%s: Server can't respond with certificate chain: certificate empty", __FUNCTION__); +#endif + return HWIF_ERR_NO_DATA; + } + + // Add space for last zero byte if needed + chain.reserve(cert_resp.cert_chain().size() + 1); + chain.resize(cert_resp.cert_chain().size()); + + memcpy(&chain[0], cert_resp.cert_chain().c_str(), cert_resp.cert_chain().size()); + + if(chain[chain.size() - 1] != 0) { + // Pad with zero + chain.push_back(0); + } + +#ifdef USE_DLOG_LOGGING + LOGD("%s: Received %zd bytes of certificate for session %p", __FUNCTION__, cert_resp.cert_chain().size(), this); +#endif + } catch(std::bad_alloc&) { +#ifdef USE_DLOG_LOGGING + LOGE("%s: Out of memory when requesting certificate for session %p", __FUNCTION__, this); +#endif + return HWIF_ERR_OUT_OF_MEMORY; + } catch(std::invalid_argument&) { +#ifdef USE_DLOG_LOGGING + LOGE("%s: Invalid argument passed for certificate request for session %p", __FUNCTION__, this); +#endif + return HWIF_ERR_INVALID_PARAM; + } catch(std::exception& ex) { +#ifdef USE_DLOG_LOGGING + LOGE("%s: When requesting certificate for session %p received exception : %s", __FUNCTION__, this, ex.what()); +#endif + return HWIF_ERROR; + } catch(...) { +#ifdef USE_DLOG_LOGGING + LOGE("%s: When requesting certificate for session %p received exception : %s", __FUNCTION__, this, "Unknown error"); +#endif + return HWIF_ERROR; + } + + return HWIF_SUCCESS; +} + +const std::string& dcm_client_connection_impl::key_type() const noexcept { + switch(fKeyType) { + case CRYPTO_KEY_TYPE_RSA: return sKeyTypeRSA; + case CRYPTO_KEY_TYPE_ECDSA: return sKeyTypeECDSA; + default: return sKeyTypeUnknown; + } +} + +int dcm_client_connection_impl::sign_data(mbedtls_md_type_t digestType, const void * hash_data, + size_t hash_size, std::vector& digest) noexcept +{ + std::lock_guard locker(fLock); + + if(!fCookie) { +#ifdef USE_DLOG_LOGGING + LOGD("%s: Trying to request data signing in object %p but there is no connection", __FUNCTION__); +#endif + return MBEDTLS_ERR_PK_BAD_INPUT_DATA; + } + + const mbedtls_md_info_t * md_info = mbedtls_md_info_from_type(digestType); + + if(!md_info) { +#ifdef USE_DLOG_LOGGING + LOGD("%s: Can't find hash data for digest type %d", __FUNCTION__, digestType); +#endif + return MBEDTLS_ERR_PK_BAD_INPUT_DATA; + } + + if(hash_size == 0) { +#ifdef USE_DLOG_LOGGING + LOGD("%s: Overriding hash size to %zd bytes", __FUNCTION__, hash_size); +#endif + hash_size = mbedtls_md_get_size(md_info); + } else if(hash_size != mbedtls_md_get_size(md_info)) { +#ifdef USE_DLOG_LOGGING + LOGE("%s: Hash size mismatch. Expected %zd but got %zd", __FUNCTION__, hash_size, (size_t)mbedtls_md_get_size(md_info)); +#endif + return MBEDTLS_ERR_PK_BAD_INPUT_DATA; + } + + try { + RequestMessage request; + ResponseMessage response; + + auto * sign_req = request.mutable_sign_data(); + + sign_req->set_context_cookie(fCookie); + sign_req->set_data_to_sign(hash_data, hash_size); + sign_req->set_digest_type(static_cast(digestType)); + + sendReceive(request, response); + + if(!response.has_sign_data()) { +#ifdef USE_DLOG_LOGGING + LOGD("%s: Response for hash signature has no signature data", __FUNCTION__); +#endif + return MBEDTLS_ERR_PK_BAD_INPUT_DATA; + } + + auto& sign_resp(response.sign_data()); + + if(sign_resp.result() != 0) { +#ifdef USE_DLOG_LOGGING + LOGE("%s: Signature request for session %p received error %d", __FUNCTION__, this, sign_resp.result()); +#endif + return sign_resp.result(); + } + + const auto& signature = sign_resp.signature(); + + if(signature.empty()) { +#ifdef USE_DLOG_LOGGING + LOGE("%s: Received signature object is empty for session %p", __FUNCTION__, this); +#endif + return MBEDTLS_ERR_PK_BAD_INPUT_DATA; + } + + digest.resize(signature.size()); + memcpy(&digest[0], signature.c_str(), signature.size()); + +#ifdef USE_DLOG_LOGGING + LOGD("%s: Received %zd bytes of signed object for session %p", __FUNCTION__, signature.size(), this); +#endif + } catch(std::bad_alloc&) { +#ifdef USE_DLOG_LOGGING + LOGE("%s: Out of memory when processing sign request for session %p", __FUNCTION__, this); +#endif + return MBEDTLS_ERR_PK_ALLOC_FAILED; + } catch(std::exception& ex) { +#ifdef USE_DLOG_LOGGING + LOGE("%s: When processing signature for session %p got exception : %s", __FUNCTION__, this, ex.what()); +#endif + } catch(...) { +#ifdef USE_DLOG_LOGGING + LOGE("%s: When processing signature for session %p got exception : %s", __FUNCTION__, this, "Unknown error"); +#endif + return MBEDTLS_ERR_PK_BAD_INPUT_DATA; + } + + return 0; +} + +unsigned int dcm_client_connection_impl::key_length() const noexcept { + return fKeyLength; +} diff --git a/dcm-client/device-certificate-manager.pc.in b/dcm-client/device-certificate-manager.pc.in new file mode 100644 index 0000000..eb07416 --- /dev/null +++ b/dcm-client/device-certificate-manager.pc.in @@ -0,0 +1,11 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=${prefix} +libdir=@CMAKE_INSTALL_FULL_LIBDIR@ +includedir=${prefix}/include + +Name: device-certificate-manager +Description: Device Certificate Manager Package +Version: @VERSION@ +Requires: iotivity +Libs: -L${libdir} -ldevice-certificate-manager +Cflags: -I${includedir}/device-certificate-manager diff --git a/dcm-daemon/CMakeLists.txt b/dcm-daemon/CMakeLists.txt new file mode 100644 index 0000000..bfddd42 --- /dev/null +++ b/dcm-daemon/CMakeLists.txt @@ -0,0 +1,116 @@ +# +# DCM daemon build script +# Jaroslaw Pelczar +# + +###### Protobuf generator ####### + +PROTOBUF_GENERATE_CPP(PROTO_SRCS PROTO_HDRS ../dcm-client/dcm_support.proto) + +add_custom_target(protobuf_generated + DEPENDS ${PROTO_SRCS} ${PROTO_HDRS}) + +###### Setup include paths and library directories ####### + +ADD_DEFINITIONS(-DBOOST_LOG_DYN_LINK) + +IF(DLOG_FOUND) + include_directories(${DLOG_INCLUDE_DIRS}) + link_directories(${DLOG_LIBRARY_DIRS}) + add_definitions(${DLOG_CFLAGS_OTHER}) + add_definitions(-DUSE_DLOG_LOGGING=1) +ENDIF(DLOG_FOUND) + +IF(SECURITY_MANAGER_FOUND) + include_directories(${SECURITY_MANAGER_INCLUDE_DIRS}) + link_directories(${SECURITY_MANAGER_LIBRARY_DIRS}) + add_definitions(${SECURITY_MANAGER_CFLAGS_OTHER}) + add_definitions(-DUSE_SECURITY_MANAGER=1) +ENDIF(SECURITY_MANAGER_FOUND) + +IF(SYSTEMD_FOUND) + include_directories(${SYSTEMD_INCLUDE_DIRS}) + link_directories(${SYSTEMD_LIBRARY_DIRS}) + add_definitions(${SYSTEMD_CFLAGS_OTHER}) + add_definitions(-DUSE_SYSTEMD_API=1) +ENDIF(SYSTEMD_FOUND) + +IF(ARTIK_SECURITY_FOUND) + include_directories(${ARTIK_SECURITY_INCLUDE_DIRS}) + add_definitions(${ARTIK_SECURITY_CFLAGS_OTHER}) +ENDIF(ARTIK_SECURITY_FOUND) + +IF(SMACK_FOUND) + include_directories(${SMACK_INCLUDE_DIRS}) + link_directories(${SMACK_LIBRARY_DIRS}) + add_definitions(${SMACK_CFLAGS_OTHER}) + add_definitions(-DUSE_SMACK=1) +ENDIF(SMACK_FOUND) + +include_directories(${Boost_INCLUDE_DIRS}) +link_directories(${Boost_LIBRARY_DIRS}) + +include_directories(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}) + +###### Crypto Backends ####### + +add_subdirectory(dummy-backend) +add_subdirectory(see-backend) + +IF(ENABLE_DUMMY_BACKEND) + SET(DUMMY_BACKEND_OBJECTS $) +ENDIF(ENABLE_DUMMY_BACKEND) + +IF(ARTIK_SECURITY_FOUND) + SET(SEE_BACKEND_OBJECTS $) +ENDIF(ARTIK_SECURITY_FOUND) + +###### Main executable ####### + +add_executable(device-certificate-managerd + main.cpp + dcmserver.cpp + dcmsession.cpp + serviceadapter.cpp + ../shared/protobuf_asio.cpp + abstractcryptobackend.cpp + abstractcryptobackendcontext.cpp + cryptobackendroster.cpp + dllresolver.cpp + ${PROTO_SRCS} + ${PROTO_HDRS} + ${DUMMY_BACKEND_OBJECTS} + ${SEE_BACKEND_OBJECTS} + ) + +add_dependencies(device-certificate-managerd protobuf_generated) + +ApplyCxx11Standard(device-certificate-managerd) + +###### Framework linking ####### + +target_link_libraries(device-certificate-managerd ${Boost_LIBRARIES} + ${CMAKE_THREAD_LIBS_INIT} + device-certificate-manager + dl) + +IF(DLOG_FOUND) + target_link_libraries(device-certificate-managerd ${DLOG_LIBRARIES}) +ENDIF(DLOG_FOUND) + +IF(SECURITY_MANAGER_FOUND) + target_link_libraries(device-certificate-managerd ${SECURITY_MANAGER_LIBRARIES}) +ENDIF(SECURITY_MANAGER_FOUND) + +IF(SYSTEMD_FOUND) + target_link_libraries(device-certificate-managerd ${SYSTEMD_LIBRARIES}) +ENDIF(SYSTEMD_FOUND) + +IF(SMACK_FOUND) + target_link_libraries(device-certificate-managerd ${SMACK_LIBRARIES}) +ENDIF(SMACK_FOUND) + +###### Installation ####### + +install(TARGETS device-certificate-managerd + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) diff --git a/dcm-daemon/abstractcryptobackend.cpp b/dcm-daemon/abstractcryptobackend.cpp new file mode 100644 index 0000000..7978ada --- /dev/null +++ b/dcm-daemon/abstractcryptobackend.cpp @@ -0,0 +1,27 @@ +/****************************************************************** + * + * Copyright 2017 Samsung Electronics All Rights Reserved. + * + * Author: Jaroslaw Pelczar + * + * 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 "abstractcryptobackend.h" + +asbtract_crypto_backend::asbtract_crypto_backend() { +} + +asbtract_crypto_backend::~asbtract_crypto_backend() { +} diff --git a/dcm-daemon/abstractcryptobackend.h b/dcm-daemon/abstractcryptobackend.h new file mode 100644 index 0000000..90683fd --- /dev/null +++ b/dcm-daemon/abstractcryptobackend.h @@ -0,0 +1,48 @@ +/****************************************************************** + * + * Copyright 2017 Samsung Electronics All Rights Reserved. + * + * Author: Jaroslaw Pelczar + * + * 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 DCM_DAEMON_ABSTRACTCRYPTOBACKEND_H_ +#define DCM_DAEMON_ABSTRACTCRYPTOBACKEND_H_ + +#include +#include +#include "dcm_support.pb.h" + +class abstract_crypto_backend_context; + +class asbtract_crypto_backend : public std::enable_shared_from_this, + public boost::noncopyable +{ +protected: + asbtract_crypto_backend(); + +public: + virtual ~asbtract_crypto_backend(); + + virtual float will_handle_service(const std::string& serviceName, + const std::string& usage) = 0; + + virtual std::shared_ptr create_client_context( + const std::string& serviceName, + const std::string& usage, + const std::string& key_type) = 0; +}; + +#endif /* DCM_DAEMON_ABSTRACTCRYPTOBACKEND_H_ */ diff --git a/dcm-daemon/abstractcryptobackendcontext.cpp b/dcm-daemon/abstractcryptobackendcontext.cpp new file mode 100644 index 0000000..4e0bdc1 --- /dev/null +++ b/dcm-daemon/abstractcryptobackendcontext.cpp @@ -0,0 +1,27 @@ +/****************************************************************** + * + * Copyright 2017 Samsung Electronics All Rights Reserved. + * + * Author: Jaroslaw Pelczar + * + * 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 "abstractcryptobackendcontext.h" + +abstract_crypto_backend_context::abstract_crypto_backend_context() { +} + +abstract_crypto_backend_context::~abstract_crypto_backend_context() { +} diff --git a/dcm-daemon/abstractcryptobackendcontext.h b/dcm-daemon/abstractcryptobackendcontext.h new file mode 100644 index 0000000..0f8fdc9 --- /dev/null +++ b/dcm-daemon/abstractcryptobackendcontext.h @@ -0,0 +1,43 @@ +/****************************************************************** + * + * Copyright 2017 Samsung Electronics All Rights Reserved. + * + * Author: Jaroslaw Pelczar + * + * 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 DCM_DAEMON_ABSTRACTCRYPTOBACKENDCONTEXT_H_ +#define DCM_DAEMON_ABSTRACTCRYPTOBACKENDCONTEXT_H_ + +#include "abstractcryptobackend.h" + +class abstract_crypto_backend_context : public std::enable_shared_from_this { +protected: + abstract_crypto_backend_context(); + +public: + virtual ~abstract_crypto_backend_context(); + + virtual int request_certificate_chain(std::string& mutable_chain) = 0; + + virtual int sign_crypto_data(MessageDigestType digestType, const std::string& dataToSign, + std::string& digestResult) = 0; + + virtual CryptoKeyType key_type() = 0; + + virtual unsigned int key_length() = 0; +}; + +#endif /* DCM_DAEMON_ABSTRACTCRYPTOBACKENDCONTEXT_H_ */ diff --git a/dcm-daemon/cryptobackendroster.cpp b/dcm-daemon/cryptobackendroster.cpp new file mode 100644 index 0000000..bc30fbb --- /dev/null +++ b/dcm-daemon/cryptobackendroster.cpp @@ -0,0 +1,31 @@ +#include "cryptobackendroster.h" + +crypto_backend_roster& crypto_backend_roster::instance() { + static crypto_backend_roster sInstance; + return sInstance; +} + +void crypto_backend_roster::register_backend(std::shared_ptr context) +{ + std::unique_lock locker(fLock); + fBackends.push_back(context); +} + +std::shared_ptr crypto_backend_roster::choose_backend(const std::string& serviceName, + const std::string& usage) +{ + std::shared_ptr best; + float best_score = -1.0f; + + std::unique_lock locker(fLock); + + for(const auto& backend : fBackends) { + float score = backend->will_handle_service(serviceName, usage); + if(score > best_score) { + best_score = score; + best = backend; + } + } + + return best; +} diff --git a/dcm-daemon/cryptobackendroster.h b/dcm-daemon/cryptobackendroster.h new file mode 100644 index 0000000..2514412 --- /dev/null +++ b/dcm-daemon/cryptobackendroster.h @@ -0,0 +1,48 @@ +/****************************************************************** + * + * Copyright 2017 Samsung Electronics All Rights Reserved. + * + * Author: Jaroslaw Pelczar + * + * 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 DCM_DAEMON_CRYPTOBACKENDROSTER_H_ +#define DCM_DAEMON_CRYPTOBACKENDROSTER_H_ + +#include +#include +#include "abstractcryptobackend.h" + +class crypto_backend_roster { +public: + static crypto_backend_roster& instance(); + + void register_backend(std::shared_ptr context); + + std::shared_ptr choose_backend(const std::string& serviceName, + const std::string& usage); + +private: + std::mutex fLock; + std::list> fBackends; +}; + +template struct crypto_backend_registration { + crypto_backend_registration() { + crypto_backend_roster::instance().register_backend(std::make_shared()); + } +}; + +#endif /* DCM_DAEMON_CRYPTOBACKENDROSTER_H_ */ diff --git a/dcm-daemon/dcmserver.cpp b/dcm-daemon/dcmserver.cpp new file mode 100644 index 0000000..ba7a6dd --- /dev/null +++ b/dcm-daemon/dcmserver.cpp @@ -0,0 +1,80 @@ +/****************************************************************** + * + * Copyright 2017 Samsung Electronics All Rights Reserved. + * + * Author: Jaroslaw Pelczar + * + * 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 "dcmserver.h" +#include "dcmsession.h" +#include "logging.h" + +dcm_server::dcm_server(boost::asio::io_service& io_service, boost::asio::local::stream_protocol::acceptor&& acceptor) : + fService(io_service), + fAcceptor(std::move(acceptor)) +{ + BOOST_LOG_FUNCTION(); + BOOST_LOG_SEV(dcm_logger::get(), log_severity::debug) << "Construct server object"; +} + +dcm_server::~dcm_server() +{ + BOOST_LOG_FUNCTION(); + BOOST_LOG_SEV(dcm_logger::get(), log_severity::debug) << "Destroy server object"; +} + +void dcm_server::start() +{ + do_accept(); +} + +void dcm_server::do_accept() +{ + BOOST_LOG_FUNCTION(); + + auto self(this->shared_from_this()); + std::shared_ptr session; + + try { + session = std::make_shared(fService, shared_from_this()); + } catch(std::bad_alloc& ex) { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << "Out of memory when trying to allocate new session"; + return; + } catch(std::exception& ex) { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::debug) << "Can't create new session object: " << ex.what(); + return; + } + + BOOST_LOG_SEV(dcm_logger::get(), log_severity::debug) << "Start accepting connections"; + + fAcceptor.async_accept(session->socket(), + [session, self](boost::system::error_code error_code) + { + BOOST_LOG_FUNCTION(); + if(!error_code) { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::debug) << "Accepted session"; + session->start(); + } else { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << "Can't accept new session " << error_code; + } + self->do_accept(); + }); +} + +std::shared_ptr dcm_server::crypto_backend() { + std::unique_lock locker(this->fLock); + return fCryptoBackend; +} diff --git a/dcm-daemon/dcmserver.h b/dcm-daemon/dcmserver.h new file mode 100644 index 0000000..2835619 --- /dev/null +++ b/dcm-daemon/dcmserver.h @@ -0,0 +1,50 @@ +/****************************************************************** + * + * Copyright 2017 Samsung Electronics All Rights Reserved. + * + * Author: Jaroslaw Pelczar + * + * 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 DCM_DAEMON_DCMSERVER_H_ +#define DCM_DAEMON_DCMSERVER_H_ + +#include +#include +#include +#include + +class asbtract_crypto_backend; + +class dcm_server final : public boost::noncopyable, public std::enable_shared_from_this { +public: + dcm_server(boost::asio::io_service& io_service, boost::asio::local::stream_protocol::acceptor&& acceptor); + ~dcm_server(); + + void start(); + + std::shared_ptr crypto_backend(); + +private: + void do_accept(); + +private: + boost::asio::io_service& fService; + boost::asio::local::stream_protocol::acceptor fAcceptor; + std::mutex fLock; + std::shared_ptr fCryptoBackend; +}; + +#endif /* DCM_DAEMON_DCMSERVER_H_ */ diff --git a/dcm-daemon/dcmsession.cpp b/dcm-daemon/dcmsession.cpp new file mode 100644 index 0000000..068c76e --- /dev/null +++ b/dcm-daemon/dcmsession.cpp @@ -0,0 +1,431 @@ +/****************************************************************** + * + * Copyright 2017 Samsung Electronics All Rights Reserved. + * + * Author: Jaroslaw Pelczar + * + * 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 "dcmsession.h" +#include "logging.h" +#include "exception_translator.h" +#include "dcmserver.h" +#include "cryptobackendroster.h" + +#include +#include +#include +#include +#include +#include + +#ifdef USE_SMACK +#include +#endif + +#ifdef USE_SECURITY_MANAGER +#include +#endif + +#ifdef USE_SMACK +static char const *const OWNER_ID_SYSTEM = "/System"; +#endif + +#if defined(USE_SECURITY_MANAGER) && defined(USE_SMACK) +static std::map sPackageIdMapping; +static std::mutex sPackageIdMappingMutex; +#endif + +dcm_session::dcm_session(boost::asio::io_service& io_service, const std::shared_ptr& server) : + fService(io_service), + fSocket(io_service), + fServer(server) +{ + BOOST_LOG_SEV(dcm_logger::get(), log_severity::debug) << "Create new session object " << this; +} + +dcm_session::~dcm_session() +{ + BOOST_LOG_SEV(dcm_logger::get(), log_severity::debug) << "Destroy session object " << this; +} + +#if defined(USE_SMACK) && defined(USE_SECURITY_MANAGER) +static int assignToString(std::vector &vec, socklen_t len, std::string &res) +{ + if (vec.size() <= len) + return -1; + vec[len] = 0; // old implementation getsockopt returns cstring without 0 + if (vec[len - 1] == 0) --len;// new implementation of getsockopt returns cstring size+1 + res.assign(vec.data(), len); + return 0; +} + +static int getCredentialsFromSocket(int sock, std::string &res) +{ + std::vector result(SMACK_LABEL_LEN + 1); + socklen_t length = SMACK_LABEL_LEN; + + if (0 == getsockopt(sock, SOL_SOCKET, SO_PEERSEC, result.data(), &length)) { + return assignToString(result, length, res); + } + + if (errno != ERANGE) { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << "getsockopt failed"; + return -1; + } + + result.resize(length + 1); + + if (0 > getsockopt(sock, SOL_SOCKET, SO_PEERSEC, result.data(), &length)) { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << "getsockopt failed with errno: " << errno; + return -1; + } + + return assignToString(result, length, res); +} + +static int getPkgIdFromSocket(int sock, std::string &pkgId) +{ + char *pkg = nullptr; + + int ret = security_manager_identify_app_from_socket(sock, &pkg, nullptr); + + if (ret == SECURITY_MANAGER_ERROR_NO_SUCH_OBJECT) { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << "Owner of socket is not connected with pkgid. " + "This case must be special-labled client. e.g. User, System"; + return 1; + } + + if (ret != SECURITY_MANAGER_SUCCESS) { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << "security_manager_identify_app_from_socket failed with error: " + << ret; + return -1; + } + + try { + pkgId = pkg; + } catch(...) { + free(pkg); + throw; + } + + free(pkg); + BOOST_LOG_SEV(dcm_logger::get(), log_severity::debug) << "Socket: " << sock << " Was translated to owner id: " << pkgId; + return 0; +} + +static void mapToDomainLabel(std::string &label) +{ + static const std::string subdomainSep = "::"; + static const auto systemLabelLen = strlen(OWNER_ID_SYSTEM); + + if (label.length() > systemLabelLen + subdomainSep.length() && + label.compare(0, systemLabelLen, OWNER_ID_SYSTEM) == 0 && + label.compare(systemLabelLen, subdomainSep.length(), subdomainSep) == 0) { + label = OWNER_ID_SYSTEM; + } +} +#endif + +bool dcm_session::get_client_id(int handle, std::string& result) +{ + BOOST_LOG_FUNCTION(); + +#if defined(USE_SMACK) && defined(USE_SECURITY_MANAGER) + try { + std::string smackLabel; + int error = getCredentialsFromSocket(handle, smackLabel); + + if(error < 0) { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << "Socket access failure. Disconnecting"; + return false; + } + + BOOST_LOG_SEV(dcm_logger::get(), log_severity::debug) << "Client credential is " << smackLabel; + + std::unique_lock locker(sPackageIdMappingMutex); + + auto it = sPackageIdMapping.find(smackLabel); + + if(it != sPackageIdMapping.end()) { + result = it->second; + return true; + } + + std::string pkgId; + int retCode = getPkgIdFromSocket(handle, pkgId); + + if (retCode < 0) { + return false; + } + + if (retCode == 1) { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::debug) << "Special smack label case. label: " << smackLabel; + pkgId = "/" + smackLabel; + } + + mapToDomainLabel(pkgId); + + BOOST_LOG_SEV(dcm_logger::get(), log_severity::debug) << smackLabel << " mapped to " << pkgId; + + result = pkgId; + sPackageIdMapping.emplace(std::move(smackLabel), std::move(pkgId)); + + return true; + } catch(std::exception& ex) { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << "Caught exception when translating socket: " << ex.what(); + return false; + } catch(...) { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << "Caught unknown exception when translating socket"; + return false; + } +#endif + + return true; +} + +void dcm_session::start() +{ + BOOST_LOG_FUNCTION(); + + int handle = fSocket.native_handle(); + BOOST_LOG_SEV(dcm_logger::get(), log_severity::debug) << "Accepted connection with socket " << fSocket.native_handle(); + + std::string label; + if(get_client_id(handle, label)) { + do_receive(); + } +} + +void dcm_session::do_receive() noexcept +{ + BOOST_LOG_FUNCTION(); + + try { + auto self(shared_from_this()); + + BOOST_LOG_SEV(dcm_logger::get(), log_severity::debug) << "Read new message"; + + fDeserializer.read_message(fSocket, + [self, this](const boost::system::error_code& error, std::size_t bytes_read) { + BOOST_LOG_FUNCTION(); + if(!error) { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::debug) << "Received " << bytes_read << " bytes from client"; + decode_message(); + } else { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << "Client disconnected: " << error; + // Connection object will be released by shared ptr + } + }); + } catch(std::exception& ex) { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << "Caught exception while trying to read message : " << ex.what(); + // Connection object will be released by shared ptr + } catch(...) { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << "Caught exception while trying to read message : " << "unknown"; + // Connection object will be released by shared ptr + } +} + +void dcm_session::decode_message() noexcept +{ + BOOST_LOG_FUNCTION(); + try { + // Try to decode whole message + RequestMessage requestMessage; + + if(!fDeserializer.decode_received_message(requestMessage)) { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << "Can't parse message from stream"; + // This will terminate connection + return; + } + + switch(requestMessage.request_oneof_case()) + { + case RequestMessage::kAssociateContext: + handle_context_association(requestMessage.associate_context()); + break; + case RequestMessage::kRequestChain: + handle_cert_chain(requestMessage.request_chain()); + break; + case RequestMessage::kSignData: + handle_sign_request(requestMessage.sign_data()); + break; + default: + BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << "Incorrect request message type"; + // This will terminate connection + return; + } + } catch(std::exception& ex) { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << "Caught exception while parsing message : " << ex.what(); + // Connection object will be released by shared ptr + } catch(...) { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << "Caught exception while parsing message : " << "unknown"; + // Connection object will be released by shared ptr + } +} + +void dcm_session::reply(const ResponseMessage& resp) noexcept +{ + BOOST_LOG_FUNCTION(); + try { + auto self(shared_from_this()); + + fSerializer.encodeMessage(resp); + + fSerializer.async_write(fSocket, + [self, this](const boost::system::error_code& error, std::size_t bytes_written) + { + BOOST_LOG_FUNCTION(); + if(!error) { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::debug) << "Written " << bytes_written << " to socket"; + do_receive(); + } else { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << "Server disconnected: " << error; + // Connection object will be released by shared ptr + } + }); + } catch(std::exception& ex) { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << "Caught exception while sending message : " << ex.what(); + // Connection object will be released by shared ptr + } catch(...) { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << "Caught exception while sending message : " << "unknown"; + // Connection object will be released by shared ptr + } +} + +void dcm_session::handle_context_association(const AssociateKeyContext& message) +{ + BOOST_LOG_FUNCTION(); + BOOST_LOG_SEV(dcm_logger::get(), log_severity::debug) << "Associate context"; + + ResponseMessage msg; + auto * contextResponse = msg.mutable_associate_context(); + + if(fCryptoContext) { + contextResponse->set_result(EEXIST); + reply(msg); + return; + } + + BOOST_LOG_SEV(dcm_logger::get(), log_severity::debug) << "Associate context from service " << + message.service() << " with usage " << message.usage() << " and key type " << message.key_type(); + + auto server = fServer.lock(); + + if(!server) { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << "Server object gone while handling message"; + return; + } + + int error = run_with_exception_handler([&]() { + auto backend = crypto_backend_roster::instance().choose_backend(message.service(), message.usage()); + + if(!backend) { + throw std::invalid_argument("Unable to find crypto backend"); + } + + fCryptoContext = backend->create_client_context(message.service(), message.usage(), message.key_type()); + fCookie = (uintptr_t)fCryptoContext.get(); + + contextResponse->set_key_type(fCryptoContext->key_type()); + contextResponse->set_key_length(fCryptoContext->key_length()); + contextResponse->set_context_cookie(fCookie); + }); + + contextResponse->set_result(error); + + reply(msg); +} + +void dcm_session::handle_cert_chain(const RequestCertificateChain& message) +{ + BOOST_LOG_FUNCTION(); + + ResponseMessage msg; + auto * certificateResponse = msg.mutable_request_chain(); + + BOOST_LOG_SEV(dcm_logger::get(), log_severity::debug) << "Request certificate chain"; + + if(message.context_cookie() != fCookie) { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << "Received unknown context cookie"; + certificateResponse->set_result(-EINVAL); + reply(msg); + return; + } + + if(!fCryptoContext) { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << "Context not associated with connection"; + certificateResponse->set_result(-EINVAL); + reply(msg); + return; + } + + certificateResponse->set_result( + fCryptoContext->request_certificate_chain( + *certificateResponse->mutable_cert_chain())); + + reply(msg); +} + +void dcm_session::handle_sign_request(const SignRequest& message) +{ + BOOST_LOG_FUNCTION(); + BOOST_LOG_SEV(dcm_logger::get(), log_severity::debug) << "Request data signing"; + + ResponseMessage msg; + auto * signingResponse = msg.mutable_sign_data(); + + if(message.context_cookie() != fCookie) { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << "Received unknown context cookie"; + signingResponse->set_result(MBEDTLS_ERR_PK_BAD_INPUT_DATA); + reply(msg); + return; + } + + if(!fCryptoContext) { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << "Context not associated with connection"; + signingResponse->set_result(MBEDTLS_ERR_PK_BAD_INPUT_DATA); + reply(msg); + return; + } + + const mbedtls_md_info_t * md_info = mbedtls_md_info_from_type( + static_cast( + message.digest_type())); + + if(!md_info) { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << "Can't find crypto algorithm specified by caller"; + signingResponse->set_result(MBEDTLS_ERR_PK_BAD_INPUT_DATA); + reply(msg); + return; + } + + if(message.data_to_sign().size() != mbedtls_md_get_size(md_info)) { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << + "Input hash length mismatch. It is " << + message.data_to_sign().size() << " but should be " << + mbedtls_md_get_size(md_info); + signingResponse->set_result(MBEDTLS_ERR_PK_BAD_INPUT_DATA); + reply(msg); + return; + } + + signingResponse->set_result( + fCryptoContext->sign_crypto_data(message.digest_type(), + message.data_to_sign(), + *signingResponse->mutable_signature())); + + reply(msg); +} diff --git a/dcm-daemon/dcmsession.h b/dcm-daemon/dcmsession.h new file mode 100644 index 0000000..a2458cf --- /dev/null +++ b/dcm-daemon/dcmsession.h @@ -0,0 +1,70 @@ +/****************************************************************** + * + * Copyright 2017 Samsung Electronics All Rights Reserved. + * + * Author: Jaroslaw Pelczar + * + * 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 DCM_DAEMON_DCMSESSION_H_ +#define DCM_DAEMON_DCMSESSION_H_ + +#include +#include +#include +#include "dcm_support.pb.h" +#include "abstractcryptobackendcontext.h" +#include + +#include +#include + +class dcm_server; + +class dcm_session final : public std::enable_shared_from_this, + public boost::noncopyable +{ +public: + dcm_session(boost::asio::io_service& io_service, const std::shared_ptr& server); + ~dcm_session(); + + void start(); + + inline boost::asio::local::stream_protocol::socket& socket() { + return fSocket; + } + +private: + void do_receive() noexcept; + void decode_message() noexcept; + void reply(const ResponseMessage& resp) noexcept; + + bool get_client_id(int handle, std::string& result); + + void handle_context_association(const AssociateKeyContext& message); + void handle_cert_chain(const RequestCertificateChain& message); + void handle_sign_request(const SignRequest& message); + +private: + boost::asio::io_service& fService; + boost::asio::local::stream_protocol::socket fSocket; + protobuf_async_message_serialization fSerializer; + protobuf_async_message_deserialization fDeserializer; + std::weak_ptr fServer; + std::shared_ptr fCryptoContext; + uint64_t fCookie = 0; +}; + +#endif /* DCM_DAEMON_DCMSESSION_H_ */ diff --git a/dcm-daemon/dllresolver.cpp b/dcm-daemon/dllresolver.cpp new file mode 100644 index 0000000..a709f98 --- /dev/null +++ b/dcm-daemon/dllresolver.cpp @@ -0,0 +1,107 @@ +/****************************************************************** + * + * Copyright 2017 Samsung Electronics All Rights Reserved. + * + * Author: Jaroslaw Pelczar + * + * 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 "dllresolver.h" +#include "logging.h" +#include + +dll_resolver::dll_resolver(const std::string& libraryName) : + fLibraryName(libraryName), + fLibraryHandle(nullptr) +{ +} + +dll_resolver::~dll_resolver() +{ + if(fLibraryHandle.load(std::memory_order_relaxed)) { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::debug) << "Unloading library " << fLibraryName; + dlclose(fLibraryHandle.exchange(nullptr, std::memory_order_relaxed)); + BOOST_LOG_SEV(dcm_logger::get(), log_severity::debug) << "Unloaded library " << fLibraryName; + } +} + +void * dll_resolver::resolve_function(const int * key_ptr, const char * name) noexcept +{ + BOOST_LOG_FUNCTION(); + + std::unique_lock locker(fCacheLock); + + if(key_ptr) { + auto it = fCache.find(key_ptr); + + if(it != fCache.end()) + return it->second; + } + + void * handle = fLibraryHandle.load(std::memory_order_relaxed); + + if(handle) { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::debug) << "Resolving symbol " << name << " from " << fLibraryName; + void * sym = dlsym(handle, name); + if(!sym) { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << "Unable to resolve symbol " << name << " from " << + fLibraryName << ": Error is " << dlerror(); + } else { + try { + if(key_ptr) { + fCache.emplace(key_ptr, sym); + } + } catch(...) { + } + } + return sym; + } + + BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << "Trying to resolve symbol " << name << " from not loaded library " << fLibraryName; + + return nullptr; +} + +bool dll_resolver::ensure_loaded() noexcept +{ + BOOST_LOG_FUNCTION(); + + std::unique_lock locker(fCacheLock); + + void * handle = fLibraryHandle.load(std::memory_order_acquire); + + if(handle) + return true; + + BOOST_LOG_SEV(dcm_logger::get(), log_severity::debug) << "Loading library " << fLibraryName; + + handle = dlopen(fLibraryName.c_str(), RTLD_LAZY | RTLD_LOCAL); + + if(!handle) { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << "Unable to load library " << fLibraryName << ": " << dlerror(); + return false; + } + + BOOST_LOG_SEV(dcm_logger::get(), log_severity::debug) << "Library loaded " << fLibraryName; + + void * expectedValue = nullptr; + + if(!fLibraryHandle.compare_exchange_strong(expectedValue, handle, std::memory_order_release)) { + // Someone else have opened the library + dlclose(handle); + } + + return true; +} diff --git a/dcm-daemon/dllresolver.h b/dcm-daemon/dllresolver.h new file mode 100644 index 0000000..8b40118 --- /dev/null +++ b/dcm-daemon/dllresolver.h @@ -0,0 +1,55 @@ +/****************************************************************** + * + * Copyright 2017 Samsung Electronics All Rights Reserved. + * + * Author: Jaroslaw Pelczar + * + * 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 DCM_DAEMON_DLLRESOLVER_H_ +#define DCM_DAEMON_DLLRESOLVER_H_ + +#include +#include +#include +#include +#include +#include + +class dll_resolver : public boost::noncopyable { +public: + dll_resolver(const std::string& libraryName); + ~dll_resolver(); + + bool ensure_loaded() noexcept; + void * resolve_function(const int * key_ptr, const char * name) noexcept; + + template ReturnValue invoke(const int * __key_ptr, const char * name, Args... args) { + typedef ReturnValue (* function_t)(Args...); + function_t func = (function_t)resolve_function(__key_ptr, name); + if(!func) { + throw std::runtime_error("Trying to call unresolved function"); + } + return func(args...); + } + +private: + std::string fLibraryName; + std::atomic fLibraryHandle; + std::mutex fCacheLock; + std::map fCache; +}; + +#endif /* DCM_DAEMON_DLLRESOLVER_H_ */ diff --git a/dcm-daemon/dummy-backend/CMakeLists.txt b/dcm-daemon/dummy-backend/CMakeLists.txt new file mode 100644 index 0000000..4b858d1 --- /dev/null +++ b/dcm-daemon/dummy-backend/CMakeLists.txt @@ -0,0 +1,54 @@ +IF(ENABLE_DUMMY_BACKEND) + +find_program(OPENSSL_TOOL openssl) + +IF(NOT OPENSSL_TOOL) + MESSAGE(FATAL_ERROR "openssl required to build dummy CA") +ENDIF() + +add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/rootCA.key + COMMAND ${OPENSSL_TOOL} genrsa -out ${CMAKE_CURRENT_BINARY_DIR}/rootCA.key 1024) + +add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/rootCA.pem + COMMAND ${OPENSSL_TOOL} req -x509 -new -nodes -key ${CMAKE_CURRENT_BINARY_DIR}/rootCA.key + -sha256 -days 1024 -out ${CMAKE_CURRENT_BINARY_DIR}/rootCA.pem + -subj "/C=PL/ST=Test1/L=Test2/O=Dis/CN=www.example.com" + DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/rootCA.key) + +add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/rootECDSA.key + COMMAND ${OPENSSL_TOOL} ecparam -name secp521r1 -genkey -noout -out ${CMAKE_CURRENT_BINARY_DIR}/rootECDSA.key) + +add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/rootECDSA.pem + COMMAND ${OPENSSL_TOOL} req -x509 -new -nodes -key ${CMAKE_CURRENT_BINARY_DIR}/rootECDSA.key + -sha256 -days 1024 -out ${CMAKE_CURRENT_BINARY_DIR}/rootECDSA.pem + -subj "/C=PL/ST=Test1/L=Test2/O=Dis/CN=www.example.com" + DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/rootECDSA.key) + +add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/rootCA_rsa_key.c + COMMAND $ ${CMAKE_CURRENT_BINARY_DIR}/rootCA.key ${CMAKE_CURRENT_BINARY_DIR}/rootCA_rsa_key.c dummy_rootca_rsa_key + DEPENDS helper_bin2c ${CMAKE_CURRENT_BINARY_DIR}/rootCA.key) + +add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/rootCA_rsa_cert.c + COMMAND $ ${CMAKE_CURRENT_BINARY_DIR}/rootCA.pem ${CMAKE_CURRENT_BINARY_DIR}/rootCA_rsa_cert.c dummy_rootca_rsa_cert + DEPENDS helper_bin2c ${CMAKE_CURRENT_BINARY_DIR}/rootCA.pem) + +add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/rootCA_ecdsa_key.c + COMMAND $ ${CMAKE_CURRENT_BINARY_DIR}/rootECDSA.key ${CMAKE_CURRENT_BINARY_DIR}/rootCA_ecdsa_key.c dummy_rootca_ecdsa_key + DEPENDS helper_bin2c ${CMAKE_CURRENT_BINARY_DIR}/rootECDSA.key) + +add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/rootCA_ecdsa_cert.c + COMMAND $ ${CMAKE_CURRENT_BINARY_DIR}/rootECDSA.pem ${CMAKE_CURRENT_BINARY_DIR}/rootCA_ecdsa_cert.c dummy_rootca_ecdsa_cert + DEPENDS helper_bin2c ${CMAKE_CURRENT_BINARY_DIR}/rootECDSA.pem) + +add_library(dummy_backend_objects + OBJECT + dummycryptobackend.cpp + dummycryptobackendcontext.cpp + ${CMAKE_CURRENT_BINARY_DIR}/rootCA_ecdsa_key.c + ${CMAKE_CURRENT_BINARY_DIR}/rootCA_ecdsa_cert.c + ${CMAKE_CURRENT_BINARY_DIR}/rootCA_rsa_key.c + ${CMAKE_CURRENT_BINARY_DIR}/rootCA_rsa_cert.c) + +add_dependencies(dummy_backend_objects protobuf_generated) + +ENDIF() diff --git a/dcm-daemon/dummy-backend/dummycryptobackend.cpp b/dcm-daemon/dummy-backend/dummycryptobackend.cpp new file mode 100644 index 0000000..059e37b --- /dev/null +++ b/dcm-daemon/dummy-backend/dummycryptobackend.cpp @@ -0,0 +1,54 @@ +/****************************************************************** + * + * Copyright 2017 Samsung Electronics All Rights Reserved. + * + * Author: Jaroslaw Pelczar + * + * 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 "dummycryptobackend.h" +#include "dummycryptobackendcontext.h" +#include +#include "logging.h" + +crypto_backend_registration dummy_crypto_backend::dummy_crypto_backend_registration; + +dummy_crypto_backend::dummy_crypto_backend() { + +} + +dummy_crypto_backend::~dummy_crypto_backend() { +} + +std::shared_ptr dummy_crypto_backend::create_client_context( + const std::string& serviceName, + const std::string& usage, + const std::string& key_type) +{ + BOOST_LOG_FUNCTION(); + BOOST_LOG_SEV(dcm_logger::get(), log_severity::debug) << + "Create new client context for service" << serviceName << + " and usage " << usage << + " with key type " << key_type; + + + return std::make_shared(key_type); +} + +float dummy_crypto_backend::will_handle_service(const std::string&, + const std::string&) +{ + return 0.01f; +} diff --git a/dcm-daemon/dummy-backend/dummycryptobackend.h b/dcm-daemon/dummy-backend/dummycryptobackend.h new file mode 100644 index 0000000..8e7211d --- /dev/null +++ b/dcm-daemon/dummy-backend/dummycryptobackend.h @@ -0,0 +1,43 @@ +/****************************************************************** + * + * Copyright 2017 Samsung Electronics All Rights Reserved. + * + * Author: Jaroslaw Pelczar + * + * 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 DCM_DAEMON_DUMMY_BACKEND_DUMMYCRYPTOBACKEND_H_ +#define DCM_DAEMON_DUMMY_BACKEND_DUMMYCRYPTOBACKEND_H_ + +#include "abstractcryptobackend.h" +#include "cryptobackendroster.h" + +class dummy_crypto_backend final : public asbtract_crypto_backend { +public: + dummy_crypto_backend(); + virtual ~dummy_crypto_backend(); + + virtual std::shared_ptr create_client_context( + const std::string& serviceName, + const std::string& usage, + const std::string& key_type) override; + + virtual float will_handle_service(const std::string& serviceName, + const std::string& usage); + + static crypto_backend_registration dummy_crypto_backend_registration; +}; + +#endif /* DCM_DAEMON_DUMMY_BACKEND_DUMMYCRYPTOBACKEND_H_ */ diff --git a/dcm-daemon/dummy-backend/dummycryptobackendcontext.cpp b/dcm-daemon/dummy-backend/dummycryptobackendcontext.cpp new file mode 100644 index 0000000..6981fe5 --- /dev/null +++ b/dcm-daemon/dummy-backend/dummycryptobackendcontext.cpp @@ -0,0 +1,175 @@ +/****************************************************************** + * + * Copyright 2017 Samsung Electronics All Rights Reserved. + * + * Author: Jaroslaw Pelczar + * + * 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 "dummycryptobackendcontext.h" +#include +#include +#include +#include "logging.h" + +extern "C" { + extern size_t dummy_rootca_rsa_key_size; + extern char dummy_rootca_rsa_key[]; + extern size_t dummy_rootca_rsa_cert_size; + extern char dummy_rootca_rsa_cert[]; + extern size_t dummy_rootca_ecdsa_key_size; + extern char dummy_rootca_ecdsa_key[]; + extern size_t dummy_rootca_ecdsa_cert_size; + extern char dummy_rootca_ecdsa_cert[]; +} + +dummy_crypto_backend_context::dummy_crypto_backend_context(const std::string& keyType) { + BOOST_LOG_FUNCTION(); + if(keyType.empty() || keyType == "RSA") { + fKey = CRYPTO_KEY_TYPE_RSA; + } else if(keyType == "ECDSA") { + fKey = CRYPTO_KEY_TYPE_ECDSA; + } else { + throw std::invalid_argument("Unsupported key type"); + } + + mbedtls_entropy_init( &fEntropy ); + mbedtls_ctr_drbg_init( &fCtrDrbg ); + + int ret = mbedtls_ctr_drbg_seed( &fCtrDrbg, + mbedtls_entropy_func, + &fEntropy, + (const unsigned char *)this, + sizeof(dummy_crypto_backend_context) ); + + if(!ret) { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << "Can't seed entropy source"; + mbedtls_ctr_drbg_free( &fCtrDrbg ); + mbedtls_entropy_free( &fEntropy ); + throw std::runtime_error("seed failure"); + } +} + +dummy_crypto_backend_context::~dummy_crypto_backend_context() { + BOOST_LOG_FUNCTION(); + mbedtls_ctr_drbg_free( &fCtrDrbg ); + mbedtls_entropy_free( &fEntropy ); +} + +int dummy_crypto_backend_context::request_certificate_chain(std::string& mutable_chain) +{ + BOOST_LOG_FUNCTION(); + if(fKey == CRYPTO_KEY_TYPE_RSA) { + mutable_chain.assign(dummy_rootca_rsa_cert, dummy_rootca_rsa_cert_size); + } else { + mutable_chain.assign(dummy_rootca_ecdsa_cert, dummy_rootca_ecdsa_cert_size); + } + + return 0; +} + +int dummy_crypto_backend_context::sign_crypto_data(MessageDigestType digestType, + const std::string& dataToSign, + std::string& digestResult) +{ + BOOST_LOG_FUNCTION(); + int error; + + mbedtls_pk_context pk; + mbedtls_pk_init(&pk); + + if(fKey == CRYPTO_KEY_TYPE_RSA) { + error = mbedtls_pk_parse_key(&pk, + (const unsigned char *)dummy_rootca_rsa_key, + dummy_rootca_rsa_key_size + 1, // Include 0 byte for PEM + nullptr, 0); + + } else { + error = mbedtls_pk_parse_key(&pk, + (const unsigned char *)dummy_rootca_ecdsa_key, + dummy_rootca_ecdsa_key_size + 1, // Include 0 byte for PEM + nullptr, 0); + } + + if(error != 0) { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << "Can't parse private key"; + mbedtls_pk_free(&pk); + return error; + } + + size_t sig_len = 0; + digestResult.resize(MBEDTLS_MPI_MAX_SIZE); + + BOOST_LOG_SEV(dcm_logger::get(), log_severity::debug) << "Maximum digest size is " << digestResult.size(); + + error = mbedtls_pk_sign(&pk, + static_cast(digestType), + (const unsigned char *)dataToSign.c_str(), + dataToSign.size(), + (unsigned char *)digestResult.c_str(), + &sig_len, + &mbedtls_ctr_drbg_random, + &fCtrDrbg); + + if(error != 0) { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << "Signature generation failed"; + } else { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::debug) << "Signature size is " << sig_len; + digestResult.resize(sig_len); + } + + mbedtls_pk_free(&pk); + + return error; +} + +CryptoKeyType dummy_crypto_backend_context::dummy_crypto_backend_context::key_type() +{ + return fKey; +} + +unsigned int dummy_crypto_backend_context::key_length() +{ + BOOST_LOG_FUNCTION(); + unsigned int keyLength = 0; + + mbedtls_pk_context pk; + mbedtls_pk_init(&pk); + + if(fKey == CRYPTO_KEY_TYPE_RSA) { + int error = mbedtls_pk_parse_key(&pk, + (const unsigned char *)dummy_rootca_rsa_key, + dummy_rootca_rsa_key_size + 1, // Include 0 byte for PEM + nullptr, 0); + + assert(error == 0); + assert(mbedtls_pk_get_type(&pk) == MBEDTLS_PK_RSA); + (void)error; + } else { + int error = mbedtls_pk_parse_key(&pk, + (const unsigned char *)dummy_rootca_ecdsa_key, + dummy_rootca_ecdsa_key_size + 1, // Include 0 byte for PEM + nullptr, 0); + + assert(error == 0); + assert(mbedtls_pk_get_type(&pk) == MBEDTLS_PK_ECKEY); + (void)error; + } + + keyLength = mbedtls_pk_get_bitlen(&pk); + mbedtls_pk_free(&pk); + + return keyLength; +} diff --git a/dcm-daemon/dummy-backend/dummycryptobackendcontext.h b/dcm-daemon/dummy-backend/dummycryptobackendcontext.h new file mode 100644 index 0000000..0422a87 --- /dev/null +++ b/dcm-daemon/dummy-backend/dummycryptobackendcontext.h @@ -0,0 +1,48 @@ +/****************************************************************** + * + * Copyright 2017 Samsung Electronics All Rights Reserved. + * + * Author: Jaroslaw Pelczar + * + * 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 DCM_DAEMON_DUMMY_BACKEND_DUMMYCRYPTOBACKENDCONTEXT_H_ +#define DCM_DAEMON_DUMMY_BACKEND_DUMMYCRYPTOBACKENDCONTEXT_H_ + +#include "abstractcryptobackendcontext.h" +#include +#include + +class dummy_crypto_backend_context final : public abstract_crypto_backend_context { +public: + dummy_crypto_backend_context(const std::string& keyType); + virtual ~dummy_crypto_backend_context(); + + virtual int request_certificate_chain(std::string& mutable_chain) override; + + virtual int sign_crypto_data(MessageDigestType digestType, const std::string& dataToSign, + std::string& digestResult) override; + + virtual CryptoKeyType key_type() override; + + virtual unsigned int key_length() override; + +private: + CryptoKeyType fKey; + mbedtls_entropy_context fEntropy; + mbedtls_ctr_drbg_context fCtrDrbg; +}; + +#endif /* DCM_DAEMON_DUMMY_BACKEND_DUMMYCRYPTOBACKENDCONTEXT_H_ */ diff --git a/dcm-daemon/exception_translator.h b/dcm-daemon/exception_translator.h new file mode 100644 index 0000000..1395888 --- /dev/null +++ b/dcm-daemon/exception_translator.h @@ -0,0 +1,59 @@ +/****************************************************************** + * + * Copyright 2017 Samsung Electronics All Rights Reserved. + * + * Author: Jaroslaw Pelczar + * + * 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 DCM_DAEMON_EXCEPTION_TRANSLATOR_H_ +#define DCM_DAEMON_EXCEPTION_TRANSLATOR_H_ + +#include +#include +#include +#include + +template inline int run_with_exception_handler(T t) { + try { + t(); + } catch(boost::system::system_error& ex) { + return -ex.code().value(); + } catch(std::bad_alloc&) { + return -ENOMEM; + } catch(std::domain_error&) { + return -EDOM; + } catch(std::invalid_argument&) { + return -EINVAL; + } catch(std::length_error&) { + return -EINVAL; + } catch(std::out_of_range&) { + return -EINVAL; + } catch(std::range_error&) { + return -ERANGE; + } catch(std::overflow_error&) { + return -EOVERFLOW; + } catch(std::underflow_error&) { + return -EOVERFLOW; + } catch(std::exception&) { + return -EINVAL; + } catch(...) { + return -EFAULT; + } + + return 0; +} + +#endif /* DCM_DAEMON_EXCEPTION_TRANSLATOR_H_ */ diff --git a/dcm-daemon/logging.h b/dcm-daemon/logging.h new file mode 100644 index 0000000..3445945 --- /dev/null +++ b/dcm-daemon/logging.h @@ -0,0 +1,50 @@ +/****************************************************************** + * + * Copyright 2017 Samsung Electronics All Rights Reserved. + * + * Author: Jaroslaw Pelczar + * + * 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 DCM_DAEMON_LOGGING_H_ +#define DCM_DAEMON_LOGGING_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef USE_DLOG_LOGGING +#define LOG_TAG "dcm-server" +#include +#endif + +enum class log_severity { + debug, + normal, + warning, + error +}; + +// Global logger declaration +BOOST_LOG_INLINE_GLOBAL_LOGGER_DEFAULT(dcm_logger, boost::log::sources::severity_logger_mt) + +#endif /* DCM_DAEMON_LOGGING_H_ */ diff --git a/dcm-daemon/main.cpp b/dcm-daemon/main.cpp new file mode 100644 index 0000000..7012b3a --- /dev/null +++ b/dcm-daemon/main.cpp @@ -0,0 +1,204 @@ +/****************************************************************** + * + * Copyright 2017 Samsung Electronics All Rights Reserved. + * + * Author: Jaroslaw Pelczar + * + * 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 +#include + +#include +#include +#include +#include +#include + +#include "dcm_build_config.h" +#include "dcmserver.h" +#include "logging.h" +#include "serviceadapter.h" + +#include +#include + +#include + +namespace po = boost::program_options; + +struct OptionContext { + bool fShowHelp = false; + bool fShowVersion = false; + std::string fSocketName = DCM_UNIX_SOCKET_PATH; + + void parse_options(int argc, char ** argv) + { + po::options_description desc("Allowed options"); + desc.add_options() + ("help", "Print this help") + ("version", "Print version") + ; + + po::variables_map vm; + po::store(po::parse_command_line(argc, argv, desc), vm); + po::notify(vm); + + if(vm.count("help")) { + std::cout << desc << std::endl; + fShowHelp = true; + return; + } + + if(vm.count("version")) { + fShowVersion = true; + return; + } + } +}; + +BOOST_LOG_ATTRIBUTE_KEYWORD(_scope, "Scope", boost::log::attributes::named_scope::value_type) +BOOST_LOG_ATTRIBUTE_KEYWORD(_timestamp, "TimeStamp", boost::posix_time::ptime) +BOOST_LOG_ATTRIBUTE_KEYWORD(_severity, "Severity", log_severity) + +void init_logging() +{ + auto sink = boost::make_shared>(); + + dlog_custom_severity_mapping mapping("Severity"); + + mapping[log_severity::debug] = DLOG_DEBUG; + mapping[log_severity::error] = DLOG_ERROR; + mapping[log_severity::normal] = DLOG_INFO; + mapping[log_severity::warning] = DLOG_WARN; + + sink->locked_backend()->set_severity_mapper(mapping); + sink->locked_backend()->set_log_domain("dcm-server"); + +#if !defined(NDEBUG) + sink->set_filter(_severity >= log_severity::normal); +#endif + + sink->set_formatter(boost::log::expressions::stream + << boost::log::expressions::attr< unsigned int >("RecordID") // First an attribute "RecordID" is written to the log + << " [" // then this delimiter separates it from the rest of the line + << boost::log::expressions::if_(boost::log::expressions::has_attr("Tag")) + [ + boost::log::expressions::stream << boost::log::expressions::attr< std::string >("Tag") // then goes another attribute named "Tag" + // Note here we explicitly stated that its type + // should be std::string. We could omit it just + // like we did it with the "RecordID", but in this case + // library would have to detect the actual attribute value + // type in run time which has the following consequences: + // - On the one hand, the attribute would have been output + // even if it has another type (not std::string). + // - On the other, this detection does not come for free + // and will result in performance decrease. + // + // In general it's better you to specify explicitly which + // type should an attribute have wherever it is possible. + // You may specify an MPL sequence of types if the attribute + // may have more than one type. And you will have to specify + // it anyway if the library is not familiar with it (see + // boost/log/utility/type_dispatch/standard_types.hpp for the list + // of the supported out-of-the-box types). + << "] [" // yet another delimiter + ] + << boost::log::expressions::format_named_scope("Scope", boost::log::keywords::format = "%n", boost::log::keywords::iteration = boost::log::expressions::reverse) << "] " + << boost::log::expressions::smessage); // here goes the log record text + boost::log::core::get()->add_sink(sink); + + boost::log::add_common_attributes(); + boost::log::core::get()->add_thread_attribute("Scope", boost::log::attributes::named_scope()); +} + +int main(int argc, char ** argv) +{ + init_logging(); + + BOOST_LOG_FUNCTION(); + + OptionContext options; + + try { + options.parse_options(argc, argv); + } catch(std::exception& ex) { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << "Failed to parse options: " << ex.what(); + return EXIT_FAILURE; + } + + if(options.fShowHelp) { + return EXIT_SUCCESS; + } + + if(options.fShowVersion) { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::normal) << "Device Certificate Manager version " PROJECT_VERSION; + return EXIT_SUCCESS; + } + + service_adapter serviceAdapter(options.fSocketName); + + try { + boost::asio::io_service io_service; + + /* Catch signals */ + boost::asio::signal_set stop_signals(io_service, SIGINT, SIGTERM); + + stop_signals.async_wait([&io_service](const boost::system::error_code&, int sig) { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::normal) << "Stopped by signal " << sig; + io_service.stop(); + }); + + /* Change the file mode mask */ + (void)umask(0); + + /* Use root directory as working directory */ + int error = chdir("/"); + (void)error; // Don't care + + BOOST_LOG_SEV(dcm_logger::get(), log_severity::debug) << "Create platform socket"; + + auto server(std::make_shared(io_service, serviceAdapter.create_platform_socket_acceptor(io_service))); + + boost::asio::signal_set hup_signals(io_service, SIGHUP); + + hup_signals.async_wait([](const boost::system::error_code&, int) { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::normal) << "Received HUP signal"; + }); + + serviceAdapter.notify_start_complete(); + + BOOST_LOG_SEV(dcm_logger::get(), log_severity::debug) << "Start server"; + + server->start(); + io_service.run(); + } catch(std::bad_alloc& e) { + serviceAdapter.notify_start_failure(ENOMEM); + BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << "Server failed with OOM exception: " << e.what(); + return EXIT_FAILURE; + } catch(std::exception& e) { + serviceAdapter.notify_start_failure(EFAULT); + BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << "Server failed with exception: " << e.what(); + return EXIT_FAILURE; + } catch(...) { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << "Server failed with unknown exception"; + serviceAdapter.notify_start_failure(EFAULT); + return EXIT_FAILURE; + } + + BOOST_LOG_SEV(dcm_logger::get(), log_severity::normal) << "Server terminated"; + + return 0; +} diff --git a/dcm-daemon/see-backend/CMakeLists.txt b/dcm-daemon/see-backend/CMakeLists.txt new file mode 100644 index 0000000..42b718b --- /dev/null +++ b/dcm-daemon/see-backend/CMakeLists.txt @@ -0,0 +1,8 @@ +IF(ARTIK_SECURITY_FOUND) + add_library(see-backend + OBJECT + seebackend.cpp + seebackendcontext.cpp) + + add_dependencies(see-backend protobuf_generated) +ENDIF() diff --git a/dcm-daemon/see-backend/artik_security.h b/dcm-daemon/see-backend/artik_security.h new file mode 100644 index 0000000..bebd5d7 --- /dev/null +++ b/dcm-daemon/see-backend/artik_security.h @@ -0,0 +1,7 @@ +/* + * Workaround for broken header definitions + */ +#undef public +#define public +#include +#undef public diff --git a/dcm-daemon/see-backend/seebackend.cpp b/dcm-daemon/see-backend/seebackend.cpp new file mode 100644 index 0000000..b51615a --- /dev/null +++ b/dcm-daemon/see-backend/seebackend.cpp @@ -0,0 +1,103 @@ +/****************************************************************** + * + * Copyright 2017 Samsung Electronics All Rights Reserved. + * + * Author: Jaroslaw Pelczar + * + * 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 "seebackend.h" +#include "seebackendcontext.h" +#include "logging.h" +#include + +#include "artik_security.h" + +crypto_backend_registration see_backend::see_crypto_backend_registration; + +see_backend::see_backend() : + fSeeInitOK(false), + fDllResolver(std::string("libartik-security.so.0")) +{ + BOOST_LOG_FUNCTION(); + BOOST_LOG_SEV(dcm_logger::get(), log_severity::debug) << "Constructed SEE backend"; +} + +see_backend::~see_backend() { + BOOST_LOG_FUNCTION(); + if(fSeeInitOK) { + try { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::debug) << "Deinitializing SEE backend"; + fDllResolver.invoke(nullptr, "see_deinit"); + BOOST_LOG_SEV(dcm_logger::get(), log_severity::debug) << "Deinitialized SEE backend"; + } catch(...) { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::debug) << "Exception caught when deinitializing SEE backend"; + } + } +} + +void see_backend::initialize_see() { + if(fSeeInitOK) + return; + + BOOST_LOG_FUNCTION(); + BOOST_LOG_SEV(dcm_logger::get(), log_severity::debug) << "Initializing SEE backend"; + + std::unique_lock locker(fSEEMutex); + + try { + int error = fDllResolver.invoke(nullptr, "see_init", "TEST_ID", "TEST_PASSWORD"); + + if(error != 0) { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << "SEE framework init failure: " << error; + fSeeInitOK = false; + } else { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::debug) << "SEE framework initialized"; + fSeeInitOK = true; + } + } catch(...) { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << "Caught exception when initializing SEE backend"; + fSeeInitOK = false; + } +} + +std::shared_ptr see_backend::create_client_context( + const std::string& serviceName, + const std::string& usage, + const std::string& key_type) +{ + BOOST_LOG_FUNCTION(); + initialize_see(); + + if(!fSeeInitOK) + throw std::runtime_error("SEE not initialized"); + + return std::make_shared(std::static_pointer_cast(shared_from_this()), key_type); +} + +float see_backend::will_handle_service(const std::string&, + const std::string&) +{ + BOOST_LOG_FUNCTION(); + if(!fDllResolver.ensure_loaded()) + return -1.0f; + + initialize_see(); + + if(!fSeeInitOK) + return -1.0f; + + return 1.0f; +} diff --git a/dcm-daemon/see-backend/seebackend.h b/dcm-daemon/see-backend/seebackend.h new file mode 100644 index 0000000..919629d --- /dev/null +++ b/dcm-daemon/see-backend/seebackend.h @@ -0,0 +1,59 @@ +/****************************************************************** + * + * Copyright 2017 Samsung Electronics All Rights Reserved. + * + * Author: Jaroslaw Pelczar + * + * 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 DCM_DAEMON_SEE_BACKEND_SEEBACKEND_H_ +#define DCM_DAEMON_SEE_BACKEND_SEEBACKEND_H_ + +#include "abstractcryptobackend.h" +#include "cryptobackendroster.h" +#include +#include + +class see_backend: public asbtract_crypto_backend +{ +public: + see_backend(); + virtual ~see_backend(); + + virtual std::shared_ptr create_client_context( + const std::string& serviceName, + const std::string& usage, + const std::string& key_type) override; + + virtual float will_handle_service(const std::string& serviceName, + const std::string& usage); + + inline dll_resolver& get_dll_resolver() { + return fDllResolver; + } + + static crypto_backend_registration see_crypto_backend_registration; + +private: + void initialize_see(); + +private: + bool fSeeInitOK = false; + std::mutex fSEEMutex; + dll_resolver fDllResolver; +}; + +#endif /* DCM_DAEMON_SEE_BACKEND_SEEBACKEND_H_ */ diff --git a/dcm-daemon/see-backend/seebackendcontext.cpp b/dcm-daemon/see-backend/seebackendcontext.cpp new file mode 100644 index 0000000..2ebb4a8 --- /dev/null +++ b/dcm-daemon/see-backend/seebackendcontext.cpp @@ -0,0 +1,146 @@ +/****************************************************************** + * + * Copyright 2017 Samsung Electronics All Rights Reserved. + * + * Author: Jaroslaw Pelczar + * + * 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 "seebackendcontext.h" +#include "logging.h" +#include "artik_security.h" + +#include + +static int see_get_certificate_key = 0; +static int see_get_ecdsa_signature_key; + +see_backend_context::see_backend_context(std::shared_ptr backend, const std::string& keyType) : + fBackendPtr(backend) +{ + BOOST_LOG_FUNCTION(); + BOOST_LOG_SEV(dcm_logger::get(), log_severity::debug) << "Created new SEE with key " << keyType; + + if(keyType.empty() || keyType == "ECDSA") { + fKeyType = CRYPTO_KEY_TYPE_ECDSA; + } else { + throw std::invalid_argument("Unsupported key type"); + } + + BOOST_LOG_SEV(dcm_logger::get(), log_severity::debug) << "Created new SEE context at " << this; +} + +see_backend_context::~see_backend_context() +{ + BOOST_LOG_SEV(dcm_logger::get(), log_severity::debug) << "Deleting SEE context " << this; +} + +int see_backend_context::request_certificate_chain(std::string& mutable_chain) +{ + BOOST_LOG_FUNCTION(); + see_data cert; + + BOOST_LOG_SEV(dcm_logger::get(), log_severity::debug) << "SEE : Request certificate chain"; + + auto backend = fBackendPtr.lock(); + + if(!backend) { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << "Unable to acquire backend pointer"; + return -EINVAL; + } + + auto& resolver(backend->get_dll_resolver()); + + int error = resolver.invoke(&see_get_certificate_key, "see_get_certificate", "ARTIK/0", &cert); + + if(error != 0) { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::debug) << "Can't invoke get certificate function"; + return -EINVAL; + } + + BOOST_LOG_SEV(dcm_logger::get(), log_severity::debug) << "SEE: Got certificate with " << cert.length << " bytes"; + + try { + mutable_chain.assign((const char *)cert.data, cert.length); + } catch(...) { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << "SEE: Got exception when assigning data"; + free(cert.data); + throw; + } + + free(cert.data); + + BOOST_LOG_SEV(dcm_logger::get(), log_severity::debug) << "Requested certificate in " << this; + + return 0; +} + +int see_backend_context::sign_crypto_data(MessageDigestType digestType, const std::string& dataToSign, + std::string& digestResult) +{ + BOOST_LOG_FUNCTION(); + see_data hashed_data; + see_data signed_data; + + hashed_data.data = (void *)dataToSign.c_str(); + hashed_data.length = dataToSign.size(); + + BOOST_LOG_SEV(dcm_logger::get(), log_severity::debug) << "SEE: Sign " << hashed_data.length << " bytes"; + + auto backend = fBackendPtr.lock(); + + if(!backend) { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << "Unable to acquire backend pointer"; + return -EINVAL; + } + + auto& resolver(backend->get_dll_resolver()); + + int error = resolver.invoke( + &see_get_ecdsa_signature_key, + "see_get_ecdsa_signature", + ECDSA_SEC_P256R1, + "ARTIK/0", + hashed_data, + &signed_data); + + if(error != 0) { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << "Unable to generate ECDSA signature in " << this << " :" << error; + return MBEDTLS_ERR_PK_BAD_INPUT_DATA; + } + + BOOST_LOG_SEV(dcm_logger::get(), log_severity::debug) << "SEE: Generated ECDSA signature"; + + try { + digestResult.assign((const char *)signed_data.data, signed_data.length); + } catch(...) { + free(signed_data.data); + throw; + } + + free(signed_data.data); + + return 0; +} + +CryptoKeyType see_backend_context::key_type() +{ + return fKeyType; +} + +unsigned int see_backend_context::key_length() +{ + return 256; +} diff --git a/dcm-daemon/see-backend/seebackendcontext.h b/dcm-daemon/see-backend/seebackendcontext.h new file mode 100644 index 0000000..14d7ad8 --- /dev/null +++ b/dcm-daemon/see-backend/seebackendcontext.h @@ -0,0 +1,46 @@ +/****************************************************************** + * + * Copyright 2017 Samsung Electronics All Rights Reserved. + * + * Author: Jaroslaw Pelczar + * + * 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 DCM_DAEMON_SEE_BACKEND_SEEBACKENDCONTEXT_H_ +#define DCM_DAEMON_SEE_BACKEND_SEEBACKENDCONTEXT_H_ + +#include "abstractcryptobackendcontext.h" +#include "seebackend.h" + +class see_backend_context final : public abstract_crypto_backend_context { +public: + see_backend_context(std::shared_ptr backend, const std::string& keyType); + virtual ~see_backend_context(); + + virtual int request_certificate_chain(std::string& mutable_chain) override; + + virtual int sign_crypto_data(MessageDigestType digestType, const std::string& dataToSign, + std::string& digestResult) override; + + virtual CryptoKeyType key_type() override; + + virtual unsigned int key_length() override; + +private: + CryptoKeyType fKeyType; + std::weak_ptr fBackendPtr; +}; + +#endif /* DCM_DAEMON_SEE_BACKEND_SEEBACKENDCONTEXT_H_ */ diff --git a/dcm-daemon/serviceadapter.cpp b/dcm-daemon/serviceadapter.cpp new file mode 100644 index 0000000..c7df4aa --- /dev/null +++ b/dcm-daemon/serviceadapter.cpp @@ -0,0 +1,102 @@ +/****************************************************************** + * + * Copyright 2017 Samsung Electronics All Rights Reserved. + * + * Author: Jaroslaw Pelczar + * + * 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 "serviceadapter.h" +#include "logging.h" + +#include +#include + +#ifdef USE_SYSTEMD_API +#include +#endif + +service_adapter::service_adapter(const std::string& default_socket_path) : + fDefaultSocketPath(default_socket_path) +{ +} + +service_adapter::~service_adapter() +{ + if(fDefaultSocketPathUsed) { + boost::filesystem::remove(fDefaultSocketPath); + } +} + +boost::asio::local::stream_protocol::acceptor service_adapter::create_platform_socket_acceptor(boost::asio::io_service& io_service) +{ + BOOST_LOG_FUNCTION(); + +#ifdef USE_SYSTEMD_API + BOOST_LOG_SEV(dcm_logger::get(), log_severity::debug) << "Try to get socket from systemd"; + + int n = sd_listen_fds(0); + + BOOST_LOG_SEV(dcm_logger::get(), log_severity::debug) << n << " sockets provided by systemd"; + + if( n > 0 ) { + for(int fd = SD_LISTEN_FDS_START ; fd < SD_LISTEN_FDS_START + n ; ++fd) { + if(sd_is_socket_unix(fd, SOCK_STREAM, 1, fDefaultSocketPath.c_str(), 0)) { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::debug) << "Got UNIX domain socket with fd " << fd; + + return boost::asio::local::stream_protocol::acceptor( + io_service, + boost::asio::local::stream_protocol(), + fd); + } + } + } + + BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << "No systemd sockets found"; + + throw std::runtime_error("No socket created by systemd"); +#else + fDefaultSocketPathUsed = true; + boost::filesystem::remove(fDefaultSocketPath); + + return boost::asio::local::stream_protocol::acceptor( + io_service, + boost::asio::local::stream_protocol::endpoint(fDefaultSocketPath)); +#endif +} + +void service_adapter::notify_start_complete() +{ + BOOST_LOG_FUNCTION(); + BOOST_LOG_SEV(dcm_logger::get(), log_severity::normal) << "Notify start completed to systemd"; + +#ifdef USE_SYSTEMD_API + sd_listen_fds(1); + sd_notify(0, "READY=1"); + fStartCompleteNotified = true; +#endif +} + +void service_adapter::notify_start_failure(int error) +{ + BOOST_LOG_FUNCTION(); + BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << "Notify start failure"; + +#ifdef USE_SYSTEMD_API + if(!fStartCompleteNotified) { + sd_notifyf(0, "STATUS=Failed to start up: %s\nERRNO=%d", strerror(error), error); + } +#endif +} diff --git a/dcm-daemon/serviceadapter.h b/dcm-daemon/serviceadapter.h new file mode 100644 index 0000000..946b188 --- /dev/null +++ b/dcm-daemon/serviceadapter.h @@ -0,0 +1,43 @@ +/****************************************************************** + * + * Copyright 2017 Samsung Electronics All Rights Reserved. + * + * Author: Jaroslaw Pelczar + * + * 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 DCM_DAEMON_SERVICEADAPTER_H_ +#define DCM_DAEMON_SERVICEADAPTER_H_ + +#include +#include + +class service_adapter final : public boost::noncopyable { +public: + service_adapter(const std::string& default_socket_path); + ~service_adapter(); + + boost::asio::local::stream_protocol::acceptor create_platform_socket_acceptor(boost::asio::io_service& io_service); + + void notify_start_complete(); + void notify_start_failure(int error); + +private: + std::string fDefaultSocketPath; + bool fDefaultSocketPathUsed = false; + bool fStartCompleteNotified = false; +}; + +#endif /* DCM_DAEMON_SERVICEADAPTER_H_ */ diff --git a/packaging/device-certificate-manager-devel.manifest b/packaging/device-certificate-manager-devel.manifest new file mode 100644 index 0000000..5530a2e --- /dev/null +++ b/packaging/device-certificate-manager-devel.manifest @@ -0,0 +1,7 @@ + + + + + + + diff --git a/packaging/device-certificate-manager-tests.manifest.in b/packaging/device-certificate-manager-tests.manifest.in new file mode 100644 index 0000000..3b8c320 --- /dev/null +++ b/packaging/device-certificate-manager-tests.manifest.in @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/packaging/device-certificate-manager.manifest b/packaging/device-certificate-manager.manifest new file mode 100644 index 0000000..a76fdba --- /dev/null +++ b/packaging/device-certificate-manager.manifest @@ -0,0 +1,5 @@ + + + + + diff --git a/packaging/device-certificate-manager.spec b/packaging/device-certificate-manager.spec new file mode 100644 index 0000000..3660733 --- /dev/null +++ b/packaging/device-certificate-manager.spec @@ -0,0 +1,119 @@ +Name: device-certificate-manager +Summary: Device Certificate Manager daemon and libraries +Version: 0.1 +Release: 1 +Group: Security/Secure Storage +License: Apache-2.0 +Source0: %{name}-%{version}.tar.gz +Source1001: device-certificate-manager.manifest +Source1002: device-certificate-manager-devel.manifest +BuildRequires: cmake +BuildRequires: pkgconfig(dlog) +# BuildRequires: pkgconfig(artik-security) +BuildRequires: pkgconfig(libsystemd-daemon) +BuildRequires: pkgconfig(security-manager) +BuildRequires: pkgconfig(libsmack) +BuildRequires: pkgconfig(libtzplatform-config) +BuildRequires: pkgconfig(iotivity) +BuildRequires: pkgconfig(protobuf) +BuildRequires: boost-devel +BuildRequires: openssl +Summary: Device Certificate Manager +Group: Security/Libraries +Requires(post): /sbin/ldconfig +Requires(postun): /sbin/ldconfig +%{?systemd_requires} + +%global user_name key-manager +%global group_name key-manager +%global service_name device-certificate-manager +%global smack_domain_name System + +%description +Device Certificate Manager provides cryptography services +for the Iotivity framework. + +%package -n device-certificate-manager-devel +Summary: Device Certificate Manager (development) +Group: Security/Development +Requires: pkgconfig(iotivity) +Requires: device-certificate-manager = %{version}-%{release} + +%description -n device-certificate-manager-devel +Device Certificate Manager development headers and libraries + +%package -n device-certificate-manager-tests +Summary: Internal tests for Device Certificate Manager +Group: Security/Testing +Requires: device-certificate-manager = %{version}-%{release} +Requires(post): /sbin/ldconfig +Requires(postun): /sbin/ldconfig + +%description -n device-certificate-manager-tests +Internal tests for Device Certificate Manager + +%prep +%setup -q +cp -a %{SOURCE1001} . +cp -a %{SOURCE1002} . + +%build + +%cmake . -DVERSION=%{version} \ + -DCMAKE_BUILD_TYPE=%{?build_type:%build_type}%{!?build_type:Release} \ + -DSYSTEMD_UNIT_DIR=%{_unitdir} \ + -DSERVICE_NAME=%{service_name} \ + -DUSER_NAME=%{user_name} \ + -DGROUP_NAME=%{group_name} \ + -DSMACK_DOMAIN_NAME=%{smack_domain_name} + +make %{?jobs:-j%jobs} + +%install +%make_install +%install_service multi-user.target.wants device-certificate-manager.service +%install_service sockets.target.wants device-certificate-manager-control.socket + +%post + +systemctl daemon-reload +if [ $1 = 1 ]; then + # installation + systemctl start device-certificate-manager.service +fi + +%preun +if [ $1 = 0 ]; then + # unistall + systemctl stop device-certificate-manager.service +fi + +%postun +if [ $1 = 0 ]; then + # unistall + systemctl daemon-reload +fi + +%files +%manifest device-certificate-manager.manifest +%license LICENSE +%{_bindir}/device-certificate-managerd +%{_libdir}/libdevice-certificate-manager.so.1.0 +%{_unitdir}/multi-user.target.wants/device-certificate-manager.service +%{_unitdir}/device-certificate-manager.service +%{_unitdir}/device-certificate-manager.target +%{_unitdir}/sockets.target.wants/device-certificate-manager-control.socket +%{_unitdir}/device-certificate-manager-control.socket + +%files -n device-certificate-manager-devel +%manifest device-certificate-manager-devel.manifest +%license LICENSE +%{_libdir}/libdevice-certificate-manager.so +%{_includedir}/device-certificate-manager/*.h +%{_libdir}/pkgconfig/*.pc + +%files -n device-certificate-manager-tests +%manifest device-certificate-manager-tests.manifest +%license LICENSE +%{_bindir}/dcm_example_client +%{_bindir}/dcm_hw_api_test diff --git a/shared/boost_log_dlog_sink.h b/shared/boost_log_dlog_sink.h new file mode 100644 index 0000000..9ebd4f7 --- /dev/null +++ b/shared/boost_log_dlog_sink.h @@ -0,0 +1,108 @@ +/****************************************************************** + * + * Copyright 2017 Samsung Electronics All Rights Reserved. + * + * Author: Jaroslaw Pelczar + * + * 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 SHARED_BOOST_LOG_DLOG_SINK_H_ +#define SHARED_BOOST_LOG_DLOG_SINK_H_ + +#include +#include + +#ifdef USE_DLOG_LOGGING +#include +#else +typedef enum { + DLOG_UNKNOWN = 0, /**< Keep this always at the start */ + DLOG_DEFAULT, /**< Default */ + DLOG_VERBOSE, /**< Verbose */ + DLOG_DEBUG, /**< Debug */ + DLOG_INFO, /**< Info */ + DLOG_WARN, /**< Warning */ + DLOG_ERROR, /**< Error */ + DLOG_FATAL, /**< Fatal */ + DLOG_SILENT, /**< Silent */ + DLOG_PRIO_MAX /**< Keep this always at the end. */ +} log_priority; +#endif + +template class dlog_direct_severity_mapping : + public boost::log::sinks::basic_direct_mapping +{ + typedef boost::log::sinks::basic_direct_mapping base_type; +public: + explicit dlog_direct_severity_mapping(boost::log::attribute_name const& name) : + base_type(name) + { + } +}; + +template class dlog_custom_severity_mapping : + public boost::log::sinks::basic_custom_mapping +{ + typedef boost::log::sinks::basic_custom_mapping base_type; +public: + explicit dlog_custom_severity_mapping(boost::log::attribute_name const& name) : + base_type(name, DLOG_DEBUG) + { + } +}; + +class dlog_output_backend : + public boost::log::sinks::basic_formatted_sink_backend +{ + typedef boost::log::sinks::basic_formatted_sink_backend base_type; + typedef std::function< log_priority (boost::log::record_view const&) > severity_mapper_type; + +private: + std::string log_domain_; + severity_mapper_type level_mapper_; + + inline void send(log_priority level, string_type const& formatted_message) { +#ifdef USE_DLOG_LOGGING + dlog_print(level, log_domain_.c_str(), formatted_message.c_str()); +#else + fprintf(stderr, "%d/(%s): %s\n", level, log_domain_.c_str(), formatted_message.c_str()); +#endif + } + +public: + dlog_output_backend() + { + } + + void consume(boost::log::record_view const& rec, string_type const& formatted_message) { + if(!level_mapper_) { + send(DLOG_DEBUG, formatted_message); + } else { + send(level_mapper_(rec), formatted_message); + } + } + + void set_log_domain(const std::string& name) + { + log_domain_ = name; + } + + void set_severity_mapper(severity_mapper_type const& mapper) + { + level_mapper_ = mapper; + } +}; + +#endif /* SHARED_BOOST_LOG_DLOG_SINK_H_ */ diff --git a/shared/protobuf_asio.cpp b/shared/protobuf_asio.cpp new file mode 100644 index 0000000..9a4155a --- /dev/null +++ b/shared/protobuf_asio.cpp @@ -0,0 +1,97 @@ +/****************************************************************** + * + * Copyright 2017 Samsung Electronics All Rights Reserved. + * + * Author: Jaroslaw Pelczar + * + * 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 "protobuf_asio.h" +#include +#include + +protobuf_sync_message_serialization::protobuf_sync_message_serialization( + boost::asio::local::stream_protocol::socket& socket) : + fSocket(socket) +{ +} + +void protobuf_sync_message_serialization::encodeMessage(const google::protobuf::MessageLite& message) +{ + google::protobuf::io::CopyingOutputStreamAdaptor os(this); + google::protobuf::io::CodedOutputStream coded_os(&os); + + coded_os.WriteLittleEndian32(message.ByteSize()); + if(!message.SerializeToCodedStream(&coded_os)) { + throw std::invalid_argument("Message serialization error"); + } +} + +bool protobuf_sync_message_serialization::Write(const void* buffer, int size) +{ + try { + boost::asio::write(fSocket, boost::asio::buffer(buffer, size)); + } catch(...) { + return false; + } + return true; +} + +protobuf_sync_message_deserialization::protobuf_sync_message_deserialization( + boost::asio::local::stream_protocol::socket& socket) : + fSocket(socket) +{ +} + +void protobuf_sync_message_deserialization::decodeMessage(google::protobuf::MessageLite& message) +{ + google::protobuf::uint8 header[4]; + google::protobuf::uint32 length; + boost::asio::read(fSocket, boost::asio::buffer(header)); + google::protobuf::io::CodedInputStream::ReadLittleEndian32FromArray(header, &length); + + if(length < 1 || length > kDCMMessageMaxLength) { + throw std::length_error("Invalid message length"); + } + + std::unique_ptr local_array(new char[length]); + + boost::asio::read(fSocket, boost::asio::buffer(local_array.get(), length)); + + if(!message.ParseFromArray(local_array.get(), length)) { + throw std::invalid_argument("Invalid message format"); + } +} + +void protobuf_async_message_serialization::encodeMessage(const google::protobuf::MessageLite& message) +{ + int bufferSize = message.ByteSize(); + fBuffer.resize(bufferSize + sizeof(uint32_t)); + google::protobuf::io::CodedOutputStream::WriteLittleEndian32ToArray(bufferSize, + reinterpret_cast(&fBuffer[0])); + if(!message.SerializeToArray(&fBuffer[sizeof(uint32_t)], bufferSize)) { + throw std::invalid_argument("Message serialization error"); + } +} + +protobuf_async_message_deserialization::protobuf_async_message_deserialization() +{ + fBuffer.reserve(128); +} + +bool protobuf_async_message_deserialization::decode_received_message(google::protobuf::MessageLite& message) +{ + return message.ParseFromArray(&fBuffer[0], fBuffer.size()); +} diff --git a/shared/protobuf_asio.h b/shared/protobuf_asio.h new file mode 100644 index 0000000..9716357 --- /dev/null +++ b/shared/protobuf_asio.h @@ -0,0 +1,119 @@ +/****************************************************************** + * + * Copyright 2017 Samsung Electronics All Rights Reserved. + * + * Author: Jaroslaw Pelczar + * + * 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 SHARED_PROTOBUF_ASIO_H_ +#define SHARED_PROTOBUF_ASIO_H_ + +#include +#include +#include +#include +#include +#include + +static const size_t kDCMMessageMaxLength = 65536; + +class protobuf_sync_message_serialization final : + boost::noncopyable, + google::protobuf::io::CopyingOutputStream +{ + boost::asio::local::stream_protocol::socket& fSocket; +public: + protobuf_sync_message_serialization(boost::asio::local::stream_protocol::socket& socket); + + void encodeMessage(const google::protobuf::MessageLite& message); + +private: + virtual bool Write(const void* buffer, int size) override; +}; + +class protobuf_sync_message_deserialization final : + boost::noncopyable +{ + boost::asio::local::stream_protocol::socket& fSocket; +public: + protobuf_sync_message_deserialization(boost::asio::local::stream_protocol::socket& socket); + + void decodeMessage(google::protobuf::MessageLite& message); +}; + +class protobuf_async_message_serialization final : boost::noncopyable +{ + std::vector fBuffer; +public: + void encodeMessage(const google::protobuf::MessageLite& message); + + template void async_write(WriteStream& stream, WriteHandler&& handler) { + boost::asio::async_write(stream, boost::asio::buffer(fBuffer), handler); + } +}; + +class protobuf_async_message_deserialization final : public boost::noncopyable +{ + std::vector fBuffer; + google::protobuf::uint32 fMessageLength = 0; +public: + protobuf_async_message_deserialization(); + + template void read_message(ReadSocket& socket, + ReadHandler&& handler) { + fBuffer.resize(4); + fMessageLength = 0; + + boost::asio::async_read(socket, boost::asio::buffer(fBuffer), + [this, handler, &socket](const boost::system::error_code& error, std::size_t bytes_read) { + if(!error) { + assert(bytes_read == fBuffer.size()); + assert(fMessageLength == 0); + + google::protobuf::io::CodedInputStream::ReadLittleEndian32FromArray( + (const google::protobuf::uint8 *)&fBuffer[0], + &fMessageLength); + + if(fMessageLength >= 1 && fMessageLength < kDCMMessageMaxLength) { + try { + fBuffer.resize(fMessageLength); + } catch(...) { + handler( + boost::system::errc::make_error_code( + boost::system::posix::not_enough_memory), + 0); + return; + } + + boost::asio::async_read(socket, + boost::asio::buffer(fBuffer), + handler); + } else { + handler( + boost::system::errc::make_error_code( + boost::system::posix::message_size), + 0); + } + } else { + handler(error, 0); + } + }); + } + + bool decode_received_message(google::protobuf::MessageLite& message); +}; + +#endif /* SHARED_PROTOBUF_ASIO_H_ */ diff --git a/systemd/CMakeLists.txt b/systemd/CMakeLists.txt new file mode 100644 index 0000000..4c62027 --- /dev/null +++ b/systemd/CMakeLists.txt @@ -0,0 +1,17 @@ +configure_file(device-certificate-manager.service.in + ${CMAKE_CURRENT_BINARY_DIR}/device-certificate-manager.service + @ONLY) + +configure_file(device-certificate-manager-control.socket.in + ${CMAKE_CURRENT_BINARY_DIR}/device-certificate-manager-control.socket + @ONLY) + +IF(NOT ("${SYSTEMD_UNIT_DIR}" STREQUAL "")) + +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/device-certificate-manager.service + device-certificate-manager.target + ${CMAKE_CURRENT_BINARY_DIR}/device-certificate-manager-control.socket + DESTINATION + ${SYSTEMD_UNIT_DIR}) + +ENDIF() diff --git a/systemd/device-certificate-manager-control.socket.in b/systemd/device-certificate-manager-control.socket.in new file mode 100644 index 0000000..7ae61d5 --- /dev/null +++ b/systemd/device-certificate-manager-control.socket.in @@ -0,0 +1,13 @@ +[Socket] +ListenStream=@DCM_UNIX_SOCKET_PATH@ +SocketMode=0777 +SmackLabelIPIn=* +SmackLabelIPOut=@ +Service=device-certificate-manager.service + +[Unit] +Wants=device-certificate-manager.target +Before=device-certificate-manager.target + +[Install] +WantedBy=sockets.target diff --git a/systemd/device-certificate-manager.service.in b/systemd/device-certificate-manager.service.in new file mode 100644 index 0000000..bce74b8 --- /dev/null +++ b/systemd/device-certificate-manager.service.in @@ -0,0 +1,14 @@ +[Unit] +Description=Start the Device Certificate Manager +DefaultDependencies=no + +[Service] +User=@USER_NAME@ +Group=@GROUP_NAME@ +SmackProcessLabel=@SMACK_DOMAIN_NAME@ +Type=notify +ExecStart=@CMAKE_INSTALL_FULL_BINDIR@/device-certificate-managerd +Sockets=device-certificate-manager-control.socket + +[Install] +WantedBy=multi-user.target diff --git a/systemd/device-certificate-manager.target b/systemd/device-certificate-manager.target new file mode 100644 index 0000000..c51564b --- /dev/null +++ b/systemd/device-certificate-manager.target @@ -0,0 +1,3 @@ +[Unit] +Description=Device Certificate Manager sockets +DefaultDependencies=true diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..7214699 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,11 @@ +include_directories(../dcm-client) +include_directories(${CMAKE_BINARY_DIR}/dcm-client) + +add_executable(dcm_example_client example_client.cpp) +target_link_libraries(dcm_example_client device-certificate-manager) + +add_executable(dcm_hw_api_test hw_api_test.cpp) +target_link_libraries(dcm_hw_api_test device-certificate-manager ${MBEDTLS_LIB} ${MBEDCRYPTO_LIB}) + +install(TARGETS dcm_example_client dcm_hw_api_test + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) diff --git a/tests/example_client.cpp b/tests/example_client.cpp new file mode 100644 index 0000000..91183d7 --- /dev/null +++ b/tests/example_client.cpp @@ -0,0 +1,39 @@ +#include "dcm_client.h" +#include +#include + +int main(int argc, char ** argv) +{ + auto connection(dcm_client_connection::create()); + + if(!connection->create_context("example_client", "test_usage", "")) { + std::cerr << "Can't create context" << std::endl; + return -1; + } + + std::cout << "Using key type " << connection->key_type() << std::endl; + + std::vector cert; + connection->get_certificate_chain(cert); + + std::cout << "Cert is " << cert.size() << " bytes" << std::endl; + + cert.push_back('\0'); + + std::cout << "Received cert " << ((const char *)&cert[0]) << std::endl; + + std::cout << "Private key is " << connection->key_length() << " bits" << std::endl; + std::cout << "Private key is " << connection->key_type() << std::endl; + + std::vector signature; + if(connection->sign_data(MBEDTLS_MD_SHA256, "12345678901234567890123456789012", 32, signature) == 0) { + for(auto v : signature) { + std::cout << std::hex << std::setw(2) << (unsigned int)v << " "; + } + std::cout << std::endl; + } else { + std::cerr << "Signing failure" << std::endl; + } + + return 0; +} diff --git a/tests/hw_api_test.cpp b/tests/hw_api_test.cpp new file mode 100644 index 0000000..70e21ce --- /dev/null +++ b/tests/hw_api_test.cpp @@ -0,0 +1,119 @@ +#include "dcm_hw_interface.h" +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +int main() +{ + const char *pers = "hw_api_test"; + + mbedtls_entropy_context entropy; + mbedtls_ctr_drbg_context ctr_drbg; + + unsigned char result_sig[MBEDTLS_MPI_MAX_SIZE]; + size_t result_sig_len; + + mbedtls_entropy_init(&entropy); + mbedtls_ctr_drbg_init(&ctr_drbg); + + if( mbedtls_ctr_drbg_seed( &ctr_drbg, mbedtls_entropy_func, &entropy, + (const unsigned char *) pers, + strlen( pers ) ) ) + { + std::cerr << "Can't seed RNG" << std::endl; + mbedtls_ctr_drbg_free( &ctr_drbg ); + mbedtls_entropy_free( &entropy ); + return -1; + } + + std::cout << "Create new DCM key context" << std::endl; + + void * keyContext = DCM_HWGetKeyContext("a", "b", ""); + + if(!keyContext) { + std::cerr << "Can't create DCM key context" << std::endl; + mbedtls_ctr_drbg_free( &ctr_drbg ); + mbedtls_entropy_free( &entropy ); + return -1; + } + + mbedtls_pk_context pkey; + + std::cout << "Initialize PK context" << std::endl; + + mbedtls_pk_init(&pkey); + + if(DCM_HWSetupPkContext(&pkey, keyContext) != 0) { + std::cerr << "Can't setup key context" << std::endl; + DCM_HWFreeKeyContext(keyContext); + mbedtls_ctr_drbg_free( &ctr_drbg ); + mbedtls_entropy_free( &entropy ); + return -1; + } else { + std::cout << "Key context setup OK" << std::endl; + std::cout << "Key has " << mbedtls_pk_get_bitlen(&pkey) << " bits" << std::endl; + } + + unsigned char * certChain = nullptr; + size_t certChainLen = 0; + + if(DCM_HWGetOwnCertificateChain(keyContext, &certChain, &certChainLen)) { + std::cerr << "Can't request certificate chain" << std::endl; + } else { + std::cout << "Certificate received" << std::endl; + } + + unsigned char to_sign[32] = { + 1,2,3,4,5,6,7,8,9,0, + 1,2,3,4,5,6,7,8,9,0, + 1,2,3,4,5,6,7,8,9,0, + 11,11 + }; + + if(mbedtls_pk_sign(&pkey, + MBEDTLS_MD_SHA256, + to_sign, + sizeof(to_sign), + result_sig, + &result_sig_len, + mbedtls_ctr_drbg_random, &ctr_drbg) != 0) + { + std::cerr << "Can't sign data with key" << std::endl; + mbedtls_pk_free(&pkey); + DCM_HWFreeKeyContext(keyContext); + mbedtls_ctr_drbg_free( &ctr_drbg ); + mbedtls_entropy_free( &entropy ); + return -1; + } + + + std::cout << "Signature = "; + + for(size_t i = 0 ; i < result_sig_len ; ++i) { + std::cout << std::hex << std::setw(2) << (unsigned int)result_sig[i] << " "; + } + + std::cout << std::endl; + + std::cout << "Freeing PK context" << std::endl; + mbedtls_pk_free(&pkey); + + std::cout << "Deleting HW context" << std::endl; + DCM_HWFreeKeyContext(keyContext); + + std::cout << "Finished" << std::endl; + + mbedtls_ctr_drbg_free( &ctr_drbg ); + mbedtls_entropy_free( &entropy ); + + return 0; +} diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt new file mode 100644 index 0000000..064d1a4 --- /dev/null +++ b/tools/CMakeLists.txt @@ -0,0 +1,3 @@ +add_executable(helper_bin2c + bin2c.c) + \ No newline at end of file diff --git a/tools/bin2c.c b/tools/bin2c.c new file mode 100644 index 0000000..4d09136 --- /dev/null +++ b/tools/bin2c.c @@ -0,0 +1,34 @@ +#include +#include + +int main(int argc, char** argv) { + if(argc != 4) + return -1; + + FILE * infile = fopen(argv[1], "rb"); + FILE * outfile = fopen(argv[2], "wb"); + size_t i; + + fseek(infile,0,SEEK_END); + size_t size = ftell(infile); + fseek(infile,0,SEEK_SET); + + char * buffer = (char *)malloc(size); + + fread(buffer, 1, size, infile); + fclose(infile); + + fprintf(outfile, "#include \nsize_t %s_size = %zd;\nunsigned char %s[]= {\n", + argv[3], size, argv[3]); + + for(i = 0 ; i < size ; ++i) { + if(!(i % 64)) { + fprintf(outfile, "\n"); + } + + fprintf(outfile, "0x%02X,", (unsigned char)buffer[i]); + } + fprintf(outfile, "0\n};\n"); + fclose(outfile); + return 0; +} -- 2.7.4 From 03d561169ed337046b688815738b8fe8c2695c6b Mon Sep 17 00:00:00 2001 From: Jaroslaw Pelczar Date: Mon, 4 Dec 2017 07:10:33 +0100 Subject: [PATCH 02/16] Use artik security framework without external dependency Change-Id: If6533199a1cbfc1f4b6f36abacb96b3bfabaecc0 Signed-off-by: Jaroslaw Pelczar --- CMakeLists.txt | 9 ++------- cmake/CheckFrameworks.cmake | 5 +++-- dcm-client/CMakeLists.txt | 4 +++- dcm-daemon/CMakeLists.txt | 14 ++++++++++---- dcm-daemon/dcmsession.cpp | 1 + dcm-daemon/see-backend/CMakeLists.txt | 14 ++++++-------- dcm-daemon/see-backend/artik_security.h | 24 ++++++++++++++++++------ packaging/device-certificate-manager.spec | 1 - tests/CMakeLists.txt | 5 +++++ 9 files changed, 48 insertions(+), 29 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5027e44..bc49dfc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ CMAKE_MINIMUM_REQUIRED(VERSION 2.6) -##### Configure project version, especially when using +##### Configure project version when using ##### outdated tools like CMake v2 IF(POLICY CMP0048) @@ -32,12 +32,7 @@ find_package(Threads REQUIRED) INCLUDE(cmake/CheckFrameworks.cmake) INCLUDE(cmake/CStandard.cmake) -SET(ENABLE_DUMMY_BACKEND OFF) - -IF(NOT ARTIK_SECURITY_FOUND) - SET(ENABLE_DUMMY_BACKEND ON) - message(WARNING "Dummy backend enabled as no usable frameworks found") -ENDIF() +option(ENABLE_DUMMY_BACKEND "Enable dummy crypto backend" OFF) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/dcm_build_config.h.in ${CMAKE_CURRENT_BINARY_DIR}/dcm_build_config.h) diff --git a/cmake/CheckFrameworks.cmake b/cmake/CheckFrameworks.cmake index ff961e0..cb5909d 100644 --- a/cmake/CheckFrameworks.cmake +++ b/cmake/CheckFrameworks.cmake @@ -6,6 +6,8 @@ INCLUDE(CheckIncludeFiles) CHECK_INCLUDE_FILE("pkix_interface.h" HAVE_PKIX_INTERFACE) +FIND_PACKAGE(Boost 1.54 COMPONENTS unit_test_framework) + FIND_PACKAGE(Boost 1.54 REQUIRED COMPONENTS @@ -14,8 +16,7 @@ FIND_PACKAGE(Boost 1.54 program_options log thread - system - ) + system) FIND_PACKAGE(PkgConfig REQUIRED) diff --git a/dcm-client/CMakeLists.txt b/dcm-client/CMakeLists.txt index cb61bc4..ab08b48 100644 --- a/dcm-client/CMakeLists.txt +++ b/dcm-client/CMakeLists.txt @@ -51,12 +51,14 @@ GENERATE_EXPORT_HEADER(device-certificate-manager ApplyCxx11Standard(device-certificate-manager) target_link_libraries(device-certificate-manager - ${Boost_LIBRARIES} + ${Boost_SYSTEM_LIBRARY} ${CMAKE_THREAD_LIBS_INIT} ${PROTOBUF_LIBRARIES} ${MBEDTLS_LIB} ${MBEDCRYPTO_LIB}) +set_property(TARGET device-certificate-manager APPEND PROPERTY LINK_FLAGS "-Wl,--no-undefined") + IF(DLOG_FOUND) target_link_libraries(device-certificate-manager ${DLOG_LIBRARIES}) ENDIF() diff --git a/dcm-daemon/CMakeLists.txt b/dcm-daemon/CMakeLists.txt index bfddd42..17a3593 100644 --- a/dcm-daemon/CMakeLists.txt +++ b/dcm-daemon/CMakeLists.txt @@ -38,6 +38,7 @@ ENDIF(SYSTEMD_FOUND) IF(ARTIK_SECURITY_FOUND) include_directories(${ARTIK_SECURITY_INCLUDE_DIRS}) add_definitions(${ARTIK_SECURITY_CFLAGS_OTHER}) + add_definitions(-DUSE_ARTIK_SECURITY_HEADERS=1) ENDIF(ARTIK_SECURITY_FOUND) IF(SMACK_FOUND) @@ -61,9 +62,9 @@ IF(ENABLE_DUMMY_BACKEND) SET(DUMMY_BACKEND_OBJECTS $) ENDIF(ENABLE_DUMMY_BACKEND) -IF(ARTIK_SECURITY_FOUND) +IF(SECURITY_MANAGER_FOUND) SET(SEE_BACKEND_OBJECTS $) -ENDIF(ARTIK_SECURITY_FOUND) +ENDIF(SECURITY_MANAGER_FOUND) ###### Main executable ####### @@ -89,8 +90,13 @@ ApplyCxx11Standard(device-certificate-managerd) ###### Framework linking ####### -target_link_libraries(device-certificate-managerd ${Boost_LIBRARIES} - ${CMAKE_THREAD_LIBS_INIT} +target_link_libraries(device-certificate-managerd + ${Boost_LOG_LIBRARY} + ${Boost_THREAD_LIBRARY} + ${Boost_FILESYSTEM_LIBRARY} + ${Boost_PROGRAM_OPTIONS_LIBRARY} + ${Boost_SYSTEM_LIBRARY} + ${CMAKE_THREAD_LIBS_INIT} device-certificate-manager dl) diff --git a/dcm-daemon/dcmsession.cpp b/dcm-daemon/dcmsession.cpp index 068c76e..615e762 100644 --- a/dcm-daemon/dcmsession.cpp +++ b/dcm-daemon/dcmsession.cpp @@ -333,6 +333,7 @@ void dcm_session::handle_context_association(const AssociateKeyContext& message) auto backend = crypto_backend_roster::instance().choose_backend(message.service(), message.usage()); if(!backend) { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << "No crypto usable backend available"; throw std::invalid_argument("Unable to find crypto backend"); } diff --git a/dcm-daemon/see-backend/CMakeLists.txt b/dcm-daemon/see-backend/CMakeLists.txt index 42b718b..dac67cb 100644 --- a/dcm-daemon/see-backend/CMakeLists.txt +++ b/dcm-daemon/see-backend/CMakeLists.txt @@ -1,8 +1,6 @@ -IF(ARTIK_SECURITY_FOUND) - add_library(see-backend - OBJECT - seebackend.cpp - seebackendcontext.cpp) - - add_dependencies(see-backend protobuf_generated) -ENDIF() +add_library(see-backend + OBJECT + seebackend.cpp + seebackendcontext.cpp) + +add_dependencies(see-backend protobuf_generated) diff --git a/dcm-daemon/see-backend/artik_security.h b/dcm-daemon/see-backend/artik_security.h index bebd5d7..d5a6975 100644 --- a/dcm-daemon/see-backend/artik_security.h +++ b/dcm-daemon/see-backend/artik_security.h @@ -1,7 +1,19 @@ -/* - * Workaround for broken header definitions - */ -#undef public -#define public +#ifdef USE_ARTIK_SECURITY_HEADERS #include -#undef public +#else + +#ifndef _ARTIK_SECURITY_H_ +#define _ARTIK_SECURITY_H_ + +typedef struct { + void *data; + unsigned int length; +} see_data; + +typedef enum { + ECDSA_SEC_P256R1 = 3 /**< nist curve for P256r1 */ +} see_ecdsa_curve; + +#endif + +#endif diff --git a/packaging/device-certificate-manager.spec b/packaging/device-certificate-manager.spec index 3660733..deafc29 100644 --- a/packaging/device-certificate-manager.spec +++ b/packaging/device-certificate-manager.spec @@ -9,7 +9,6 @@ Source1001: device-certificate-manager.manifest Source1002: device-certificate-manager-devel.manifest BuildRequires: cmake BuildRequires: pkgconfig(dlog) -# BuildRequires: pkgconfig(artik-security) BuildRequires: pkgconfig(libsystemd-daemon) BuildRequires: pkgconfig(security-manager) BuildRequires: pkgconfig(libsmack) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 7214699..205c1a5 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -9,3 +9,8 @@ target_link_libraries(dcm_hw_api_test device-certificate-manager ${MBEDTLS_LIB} install(TARGETS dcm_example_client dcm_hw_api_test RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + +IF(Boost_UNIT_TEST_FRAMEWORK_FOUND) +ELSE() + message(STATUS "Boost::test library not found. Disabling unit test build") +ENDIF() -- 2.7.4 From 1e8c860fd58f4fa4e55964af01b17c799ead1776 Mon Sep 17 00:00:00 2001 From: Jaroslaw Pelczar Date: Mon, 4 Dec 2017 08:01:23 +0100 Subject: [PATCH 03/16] dcm-client: add linker version script Change-Id: I3536085c5c2c4786666b322210918e569864d860 Signed-off-by: Jaroslaw Pelczar --- dcm-client/CMakeLists.txt | 2 +- dcm-client/version_script.lds | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 dcm-client/version_script.lds diff --git a/dcm-client/CMakeLists.txt b/dcm-client/CMakeLists.txt index ab08b48..b318c00 100644 --- a/dcm-client/CMakeLists.txt +++ b/dcm-client/CMakeLists.txt @@ -57,7 +57,7 @@ target_link_libraries(device-certificate-manager ${MBEDTLS_LIB} ${MBEDCRYPTO_LIB}) -set_property(TARGET device-certificate-manager APPEND PROPERTY LINK_FLAGS "-Wl,--no-undefined") +set_property(TARGET device-certificate-manager APPEND PROPERTY LINK_FLAGS "-Wl,--no-undefined -Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/version_script.lds") IF(DLOG_FOUND) target_link_libraries(device-certificate-manager ${DLOG_LIBRARIES}) diff --git a/dcm-client/version_script.lds b/dcm-client/version_script.lds new file mode 100644 index 0000000..854fe23 --- /dev/null +++ b/dcm-client/version_script.lds @@ -0,0 +1,13 @@ +DCMCLIENT_1.0 { + global: + DCM_HWGetKeyContext; + DCM_HWFreeKeyContext; + DCM_HWGetOwnCertificateChain; + DCM_HWSetupPkContext; + extern "C++" { + dcm_client_connection::*; + }; + + local: + *; +}; -- 2.7.4 From e863bf7eafa2c0bcf4c8ae56c624369254199309 Mon Sep 17 00:00:00 2001 From: Jaroslaw Pelczar Date: Mon, 4 Dec 2017 10:07:23 +0100 Subject: [PATCH 04/16] Added certificate reversing for broken libSEE API Change-Id: I3ce62b70675feb4fa05a81ec357bea0c58a92bca Signed-off-by: Jaroslaw Pelczar --- CMakeLists.txt | 12 +++++- cmake/CheckFrameworks.cmake | 7 ++++ dcm-daemon/CMakeLists.txt | 15 +++++--- dcm-daemon/dummy-backend/dummycryptobackend.cpp | 3 +- dcm-daemon/main.cpp | 2 +- dcm-daemon/mbedtls_wrapper.h | 49 +++++++++++++++++++++++++ dcm-daemon/see-backend/seebackendcontext.cpp | 37 +++++++++++++++++++ dcm-daemon/see-backend/seebackendcontext.h | 3 ++ tests/hw_api_test.cpp | 19 ++++++++++ 9 files changed, 138 insertions(+), 9 deletions(-) create mode 100644 dcm-daemon/mbedtls_wrapper.h diff --git a/CMakeLists.txt b/CMakeLists.txt index bc49dfc..68f1c4f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,6 +33,11 @@ INCLUDE(cmake/CheckFrameworks.cmake) INCLUDE(cmake/CStandard.cmake) option(ENABLE_DUMMY_BACKEND "Enable dummy crypto backend" OFF) +option(ENABLE_SYSTEMD_SUPPORT "Enable support for systemd" ON) + +IF(ENABLE_SYSTEMD_SUPPORT AND NOT SYSTEMD_FOUND) + message(FATAL_ERROR "systemd support required but systemd not found") +ENDIF() configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/dcm_build_config.h.in ${CMAKE_CURRENT_BINARY_DIR}/dcm_build_config.h) @@ -43,7 +48,12 @@ configure_file(packaging/device-certificate-manager-tests.manifest.in include_directories(${CMAKE_CURRENT_BINARY_DIR}) include_directories(shared) -SET(DCM_UNIX_SOCKET_PATH "/run/device-certificate-manager.socket") +IF(ENABLE_SYSTEMD_SUPPORT) + SET(DCM_UNIX_SOCKET_PATH "/run/device-certificate-manager.socket") +ELSE() + SET(DCM_UNIX_SOCKET_PATH "/tmp/device-certificate-manager.socket") +ENDIF() + add_definitions(-DDCM_UNIX_SOCKET_PATH="${DCM_UNIX_SOCKET_PATH}") add_subdirectory(dcm-daemon) diff --git a/cmake/CheckFrameworks.cmake b/cmake/CheckFrameworks.cmake index cb5909d..34ed41b 100644 --- a/cmake/CheckFrameworks.cmake +++ b/cmake/CheckFrameworks.cmake @@ -41,6 +41,9 @@ find_library(MBEDTLS_LIB find_library(MBEDCRYPTO_LIB mbedcrypto) + +find_library(MBEDX509_LIB + mbedx509) IF(MBEDTLS_LIB-NOTFOUND) message(FATAL_ERROR "mbedtls not found ...") @@ -50,6 +53,10 @@ IF(MBEDCRYPTO_LIB-NOTFOUND) message(FATAL_ERROR "mbedcrypto not found ...") ENDIF() +IF(MBEDX509_LIB-NOTFOUND) + message(FATAL_ERROR "mbedx509 not found ...") +ENDIF() + CHECK_INCLUDE_FILES("mbedtls/rsa.h;mbedtls/ecdsa.h" MBEDTLS_HEADERS_OK) IF(NOT MBEDTLS_HEADERS_OK) diff --git a/dcm-daemon/CMakeLists.txt b/dcm-daemon/CMakeLists.txt index 17a3593..90f64a4 100644 --- a/dcm-daemon/CMakeLists.txt +++ b/dcm-daemon/CMakeLists.txt @@ -28,12 +28,12 @@ IF(SECURITY_MANAGER_FOUND) add_definitions(-DUSE_SECURITY_MANAGER=1) ENDIF(SECURITY_MANAGER_FOUND) -IF(SYSTEMD_FOUND) +IF(ENABLE_SYSTEMD_SUPPORT) include_directories(${SYSTEMD_INCLUDE_DIRS}) link_directories(${SYSTEMD_LIBRARY_DIRS}) add_definitions(${SYSTEMD_CFLAGS_OTHER}) add_definitions(-DUSE_SYSTEMD_API=1) -ENDIF(SYSTEMD_FOUND) +ENDIF(ENABLE_SYSTEMD_SUPPORT) IF(ARTIK_SECURITY_FOUND) include_directories(${ARTIK_SECURITY_INCLUDE_DIRS}) @@ -96,7 +96,10 @@ target_link_libraries(device-certificate-managerd ${Boost_FILESYSTEM_LIBRARY} ${Boost_PROGRAM_OPTIONS_LIBRARY} ${Boost_SYSTEM_LIBRARY} - ${CMAKE_THREAD_LIBS_INIT} + ${CMAKE_THREAD_LIBS_INIT} + ${MBEDTLS_LIB} + ${MBEDCRYPTO_LIB} + ${MBEDX509_LIB} device-certificate-manager dl) @@ -108,13 +111,13 @@ IF(SECURITY_MANAGER_FOUND) target_link_libraries(device-certificate-managerd ${SECURITY_MANAGER_LIBRARIES}) ENDIF(SECURITY_MANAGER_FOUND) -IF(SYSTEMD_FOUND) +IF(ENABLE_SYSTEMD_SUPPORT) target_link_libraries(device-certificate-managerd ${SYSTEMD_LIBRARIES}) -ENDIF(SYSTEMD_FOUND) +ENDIF(ENABLE_SYSTEMD_SUPPORT) IF(SMACK_FOUND) target_link_libraries(device-certificate-managerd ${SMACK_LIBRARIES}) -ENDIF(SMACK_FOUND) +ENDIF(SMACK_FOUND) ###### Installation ####### diff --git a/dcm-daemon/dummy-backend/dummycryptobackend.cpp b/dcm-daemon/dummy-backend/dummycryptobackend.cpp index 059e37b..03c44a5 100644 --- a/dcm-daemon/dummy-backend/dummycryptobackend.cpp +++ b/dcm-daemon/dummy-backend/dummycryptobackend.cpp @@ -26,10 +26,11 @@ crypto_backend_registration dummy_crypto_backend::dummy_crypto_backend_registration; dummy_crypto_backend::dummy_crypto_backend() { - + BOOST_LOG_FUNCTION(); } dummy_crypto_backend::~dummy_crypto_backend() { + BOOST_LOG_FUNCTION(); } std::shared_ptr dummy_crypto_backend::create_client_context( diff --git a/dcm-daemon/main.cpp b/dcm-daemon/main.cpp index 7012b3a..03d75ac 100644 --- a/dcm-daemon/main.cpp +++ b/dcm-daemon/main.cpp @@ -87,7 +87,7 @@ void init_logging() sink->locked_backend()->set_severity_mapper(mapping); sink->locked_backend()->set_log_domain("dcm-server"); -#if !defined(NDEBUG) +#if defined(NDEBUG) && !defined(DEBUG) sink->set_filter(_severity >= log_severity::normal); #endif diff --git a/dcm-daemon/mbedtls_wrapper.h b/dcm-daemon/mbedtls_wrapper.h new file mode 100644 index 0000000..03d67b6 --- /dev/null +++ b/dcm-daemon/mbedtls_wrapper.h @@ -0,0 +1,49 @@ +/****************************************************************** + * + * Copyright 2017 Samsung Electronics All Rights Reserved. + * + * Author: Jaroslaw Pelczar + * + * 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 DCM_DAEMON_MBEDTLS_WRAPPER_H_ +#define DCM_DAEMON_MBEDTLS_WRAPPER_H_ + +#include +#include +#include +#include + +struct mbedtls_x509_crt_wrapper : public mbedtls_x509_crt, public boost::noncopyable { + mbedtls_x509_crt_wrapper() { + mbedtls_x509_crt_init(this); + } + + ~mbedtls_x509_crt_wrapper() { + mbedtls_x509_crt_free(this); + } + + int parse(const std::string& pem) { + return mbedtls_x509_crt_parse(this, reinterpret_cast(pem.c_str()), pem.size() + 1); + } +}; + +static inline std::string mbedtls_error_to_string(int error) { + char buffer[256]; + mbedtls_strerror(error, buffer, sizeof(buffer)); + return std::string(buffer); +} + +#endif /* DCM_DAEMON_MBEDTLS_WRAPPER_H_ */ diff --git a/dcm-daemon/see-backend/seebackendcontext.cpp b/dcm-daemon/see-backend/seebackendcontext.cpp index 2ebb4a8..4c06068 100644 --- a/dcm-daemon/see-backend/seebackendcontext.cpp +++ b/dcm-daemon/see-backend/seebackendcontext.cpp @@ -47,6 +47,37 @@ see_backend_context::~see_backend_context() BOOST_LOG_SEV(dcm_logger::get(), log_severity::debug) << "Deleting SEE context " << this; } +static const std::string sPEMHeader("-----BEGIN CERTIFICATE-----\n"); +static const std::string sPEMFooter("-----END CERTIFICATE-----\n"); + +void see_backend_context::fix_certificate_chain(std::string& cert_chain) { + BOOST_LOG_FUNCTION(); + + std::list certs; + + BOOST_LOG_SEV(dcm_logger::get(), log_severity::debug) << "Parse certificate chain"; + + auto it = cert_chain.find(sPEMHeader); + while(it != std::string::npos) { + auto footer_it = cert_chain.find(sPEMFooter, it + sPEMHeader.length() - 1); + + if(footer_it == std::string::npos) { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << "Invalid format of certificate list returned by backend: no footer"; + throw std::runtime_error("Can't find certificate footer"); + } + + std::string cert_pem(cert_chain.substr(it, footer_it + sPEMFooter.size() - it)); + + certs.emplace_front(std::move(cert_pem)); + it = cert_chain.find(sPEMHeader, footer_it + sPEMFooter.length() - 1); + } + + cert_chain.resize(0); + + for(const auto& cert: certs) + cert_chain.append(cert); +} + int see_backend_context::request_certificate_chain(std::string& mutable_chain) { BOOST_LOG_FUNCTION(); @@ -74,6 +105,12 @@ int see_backend_context::request_certificate_chain(std::string& mutable_chain) try { mutable_chain.assign((const char *)cert.data, cert.length); + + /* + * libSEE bug workaround - we need to reverse certificate order + * as library returns them in incorrect sequence + */ + fix_certificate_chain(mutable_chain); } catch(...) { BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << "SEE: Got exception when assigning data"; free(cert.data); diff --git a/dcm-daemon/see-backend/seebackendcontext.h b/dcm-daemon/see-backend/seebackendcontext.h index 14d7ad8..cb894b3 100644 --- a/dcm-daemon/see-backend/seebackendcontext.h +++ b/dcm-daemon/see-backend/seebackendcontext.h @@ -39,6 +39,9 @@ public: virtual unsigned int key_length() override; private: + void fix_certificate_chain(std::string& cert_chain); + +private: CryptoKeyType fKeyType; std::weak_ptr fBackendPtr; }; diff --git a/tests/hw_api_test.cpp b/tests/hw_api_test.cpp index 70e21ce..19e929f 100644 --- a/tests/hw_api_test.cpp +++ b/tests/hw_api_test.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include @@ -70,6 +71,24 @@ int main() std::cerr << "Can't request certificate chain" << std::endl; } else { std::cout << "Certificate received" << std::endl; + + mbedtls_x509_crt chain; + mbedtls_x509_crt_init(&chain); + + int error = mbedtls_x509_crt_parse(&chain, certChain, certChainLen); + + if(error != 0) { + std::cerr << "Can't parse certificate chain !!!" << std::endl; + DCM_HWFreeKeyContext(keyContext); + mbedtls_x509_crt_free(&chain); + mbedtls_ctr_drbg_free( &ctr_drbg ); + mbedtls_entropy_free( &entropy ); + return -1; + } + + std::cout << "Certificate chain parsed successfuly" << std::endl; + + mbedtls_x509_crt_free(&chain); } unsigned char to_sign[32] = { -- 2.7.4 From c5ab5c0f1488a97dd60a044c14bf78501e95723b Mon Sep 17 00:00:00 2001 From: Jaroslaw Pelczar Date: Mon, 4 Dec 2017 11:03:10 +0100 Subject: [PATCH 05/16] hw_api_test: add hash signature verification Change-Id: I56fcd9f41cdaad50728f0fb9226b06e4235f0fb3 Signed-off-by: Jaroslaw Pelczar --- tests/hw_api_test.cpp | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/tests/hw_api_test.cpp b/tests/hw_api_test.cpp index 19e929f..a2f564b 100644 --- a/tests/hw_api_test.cpp +++ b/tests/hw_api_test.cpp @@ -67,14 +67,14 @@ int main() unsigned char * certChain = nullptr; size_t certChainLen = 0; + mbedtls_x509_crt chain; + mbedtls_x509_crt_init(&chain); + if(DCM_HWGetOwnCertificateChain(keyContext, &certChain, &certChainLen)) { std::cerr << "Can't request certificate chain" << std::endl; } else { std::cout << "Certificate received" << std::endl; - mbedtls_x509_crt chain; - mbedtls_x509_crt_init(&chain); - int error = mbedtls_x509_crt_parse(&chain, certChain, certChainLen); if(error != 0) { @@ -109,12 +109,12 @@ int main() std::cerr << "Can't sign data with key" << std::endl; mbedtls_pk_free(&pkey); DCM_HWFreeKeyContext(keyContext); + mbedtls_x509_crt_free(&chain); mbedtls_ctr_drbg_free( &ctr_drbg ); mbedtls_entropy_free( &entropy ); return -1; } - std::cout << "Signature = "; for(size_t i = 0 ; i < result_sig_len ; ++i) { @@ -123,6 +123,24 @@ int main() std::cout << std::endl; + // Verify signature + + std::cout << "Verifying signature ..." << std::endl; + + if(mbedtls_pk_verify(&chain.pk, + MBEDTLS_MD_SHA256, + to_sign, + sizeof(to_sign), + result_sig, + result_sig_len) != 0) + { + std::cout << "Signature verification failure" << std::endl; + } else { + std::cout << "Signature verification succeeded" << std::endl; + } + + mbedtls_x509_crt_free(&chain); + std::cout << "Freeing PK context" << std::endl; mbedtls_pk_free(&pkey); -- 2.7.4 From 082c27a997504762605b229c9f45244f8ba72005 Mon Sep 17 00:00:00 2001 From: Jaroslaw Pelczar Date: Mon, 4 Dec 2017 11:20:32 +0100 Subject: [PATCH 06/16] hw_api_test: show more errors and update signature verification Change-Id: I4ba040beff40e16db60e5982525cac33d223ae6b Signed-off-by: Jaroslaw Pelczar --- tests/hw_api_test.cpp | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/tests/hw_api_test.cpp b/tests/hw_api_test.cpp index a2f564b..e6680e1 100644 --- a/tests/hw_api_test.cpp +++ b/tests/hw_api_test.cpp @@ -13,6 +13,12 @@ #include +static inline std::string mbedtls_error_to_string(int error) { + char buffer[256]; + mbedtls_strerror(error, buffer, sizeof(buffer)); + return std::string(buffer); +} + int main() { const char *pers = "hw_api_test"; @@ -20,17 +26,18 @@ int main() mbedtls_entropy_context entropy; mbedtls_ctr_drbg_context ctr_drbg; + int error; unsigned char result_sig[MBEDTLS_MPI_MAX_SIZE]; size_t result_sig_len; mbedtls_entropy_init(&entropy); mbedtls_ctr_drbg_init(&ctr_drbg); - if( mbedtls_ctr_drbg_seed( &ctr_drbg, mbedtls_entropy_func, &entropy, + if( (error = mbedtls_ctr_drbg_seed( &ctr_drbg, mbedtls_entropy_func, &entropy, (const unsigned char *) pers, - strlen( pers ) ) ) + strlen( pers ) )) != 0 ) { - std::cerr << "Can't seed RNG" << std::endl; + std::cerr << "Can't seed RNG: " << mbedtls_error_to_string(error) << std::endl; mbedtls_ctr_drbg_free( &ctr_drbg ); mbedtls_entropy_free( &entropy ); return -1; @@ -38,7 +45,7 @@ int main() std::cout << "Create new DCM key context" << std::endl; - void * keyContext = DCM_HWGetKeyContext("a", "b", ""); + void * keyContext = DCM_HWGetKeyContext("a", "b", "ECDSA"); if(!keyContext) { std::cerr << "Can't create DCM key context" << std::endl; @@ -75,10 +82,10 @@ int main() } else { std::cout << "Certificate received" << std::endl; - int error = mbedtls_x509_crt_parse(&chain, certChain, certChainLen); + error = mbedtls_x509_crt_parse(&chain, certChain, certChainLen); if(error != 0) { - std::cerr << "Can't parse certificate chain !!!" << std::endl; + std::cerr << "Can't parse certificate chain: " << mbedtls_error_to_string(error) << std::endl; DCM_HWFreeKeyContext(keyContext); mbedtls_x509_crt_free(&chain); mbedtls_ctr_drbg_free( &ctr_drbg ); @@ -98,15 +105,15 @@ int main() 11,11 }; - if(mbedtls_pk_sign(&pkey, + if((error = mbedtls_pk_sign(&pkey, MBEDTLS_MD_SHA256, to_sign, sizeof(to_sign), result_sig, &result_sig_len, - mbedtls_ctr_drbg_random, &ctr_drbg) != 0) + mbedtls_ctr_drbg_random, &ctr_drbg)) != 0) { - std::cerr << "Can't sign data with key" << std::endl; + std::cerr << "Can't sign data with key: " << mbedtls_error_to_string(error) << std::endl; mbedtls_pk_free(&pkey); DCM_HWFreeKeyContext(keyContext); mbedtls_x509_crt_free(&chain); @@ -127,14 +134,16 @@ int main() std::cout << "Verifying signature ..." << std::endl; - if(mbedtls_pk_verify(&chain.pk, + if((error = mbedtls_pk_verify_ext(chain.sig_pk, + chain.sig_opts, + &chain.pk, MBEDTLS_MD_SHA256, to_sign, sizeof(to_sign), result_sig, - result_sig_len) != 0) + result_sig_len)) != 0) { - std::cout << "Signature verification failure" << std::endl; + std::cout << "Signature verification failure : " << mbedtls_error_to_string(error) << std::endl; } else { std::cout << "Signature verification succeeded" << std::endl; } -- 2.7.4 From 6d47a9e0c6ac5a681811084babc18c30c4da6e67 Mon Sep 17 00:00:00 2001 From: Jaroslaw Pelczar Date: Mon, 4 Dec 2017 11:59:29 +0100 Subject: [PATCH 07/16] see backend: create mbedtls compatible ASN.1 ECDSA signature Change-Id: I0dd2133f1c5d687221f9d5290713cc15a0e97744 Signed-off-by: Jaroslaw Pelczar --- dcm-daemon/logging.h | 5 ++ dcm-daemon/main.cpp | 2 +- dcm-daemon/mbedtls_wrapper.h | 26 ++++++ dcm-daemon/see-backend/seebackendcontext.cpp | 115 +++++++++++++++++++++++++-- tests/hw_api_test.cpp | 2 - 5 files changed, 140 insertions(+), 10 deletions(-) diff --git a/dcm-daemon/logging.h b/dcm-daemon/logging.h index 3445945..1ccf0a1 100644 --- a/dcm-daemon/logging.h +++ b/dcm-daemon/logging.h @@ -47,4 +47,9 @@ enum class log_severity { // Global logger declaration BOOST_LOG_INLINE_GLOBAL_LOGGER_DEFAULT(dcm_logger, boost::log::sources::severity_logger_mt) +#if defined(NDEBUG) && !defined(DEBUG) +#else +#define ENABLE_DEBUG_LODDING 1 +#endif + #endif /* DCM_DAEMON_LOGGING_H_ */ diff --git a/dcm-daemon/main.cpp b/dcm-daemon/main.cpp index 03d75ac..e1f8fd2 100644 --- a/dcm-daemon/main.cpp +++ b/dcm-daemon/main.cpp @@ -87,7 +87,7 @@ void init_logging() sink->locked_backend()->set_severity_mapper(mapping); sink->locked_backend()->set_log_domain("dcm-server"); -#if defined(NDEBUG) && !defined(DEBUG) +#ifndef ENABLE_DEBUG_LODDING sink->set_filter(_severity >= log_severity::normal); #endif diff --git a/dcm-daemon/mbedtls_wrapper.h b/dcm-daemon/mbedtls_wrapper.h index 03d67b6..ed2ebe3 100644 --- a/dcm-daemon/mbedtls_wrapper.h +++ b/dcm-daemon/mbedtls_wrapper.h @@ -23,6 +23,7 @@ #include #include +#include #include #include @@ -46,4 +47,29 @@ static inline std::string mbedtls_error_to_string(int error) { return std::string(buffer); } +struct mbedtls_mpi_wrapper : public mbedtls_mpi, public boost::noncopyable { + mbedtls_mpi_wrapper() { + mbedtls_mpi_init(this); + } + + ~mbedtls_mpi_wrapper() { + mbedtls_mpi_free(this); + } + + int read_binary(const void * data, size_t length) { + return mbedtls_mpi_read_binary(this, (const unsigned char *)data, length); + } + + int get_raw(const void * data, size_t length) { + const int ciL = sizeof(mbedtls_mpi_uint); + const int biL = ciL * 8; + int error = mbedtls_mpi_grow(this, (length * 8 + biL - 1) / biL); + if(error != 0) + return error; + mbedtls_mpi_lset(this, 0); + memcpy(this->p, data, length); + return 0; + } +}; + #endif /* DCM_DAEMON_MBEDTLS_WRAPPER_H_ */ diff --git a/dcm-daemon/see-backend/seebackendcontext.cpp b/dcm-daemon/see-backend/seebackendcontext.cpp index 4c06068..df2d468 100644 --- a/dcm-daemon/see-backend/seebackendcontext.cpp +++ b/dcm-daemon/see-backend/seebackendcontext.cpp @@ -21,9 +21,13 @@ #include "seebackendcontext.h" #include "logging.h" #include "artik_security.h" +#include "mbedtls_wrapper.h" #include +#include +#include + static int see_get_certificate_key = 0; static int see_get_ecdsa_signature_key; @@ -83,7 +87,9 @@ int see_backend_context::request_certificate_chain(std::string& mutable_chain) BOOST_LOG_FUNCTION(); see_data cert; +#ifdef ENABLE_DEBUG_LODDING BOOST_LOG_SEV(dcm_logger::get(), log_severity::debug) << "SEE : Request certificate chain"; +#endif auto backend = fBackendPtr.lock(); @@ -101,7 +107,9 @@ int see_backend_context::request_certificate_chain(std::string& mutable_chain) return -EINVAL; } +#ifdef ENABLE_DEBUG_LODDING BOOST_LOG_SEV(dcm_logger::get(), log_severity::debug) << "SEE: Got certificate with " << cert.length << " bytes"; +#endif try { mutable_chain.assign((const char *)cert.data, cert.length); @@ -119,17 +127,40 @@ int see_backend_context::request_certificate_chain(std::string& mutable_chain) free(cert.data); +#ifdef ENABLE_DEBUG_LODDING BOOST_LOG_SEV(dcm_logger::get(), log_severity::debug) << "Requested certificate in " << this; +#endif return 0; } +static int ecdsa_signature_to_asn1( const mbedtls_mpi *r, const mbedtls_mpi *s, + unsigned char *sig, size_t *slen ) +{ + int ret; + unsigned char buf[MBEDTLS_ECDSA_MAX_LEN]; + unsigned char *p = buf + sizeof( buf ); + size_t len = 0; + + MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_mpi( &p, buf, s ) ); + MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_mpi( &p, buf, r ) ); + + MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( &p, buf, len ) ); + MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_tag( &p, buf, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE ) ); + + memcpy( sig, p, len ); + *slen = len; + + return 0; +} + int see_backend_context::sign_crypto_data(MessageDigestType digestType, const std::string& dataToSign, std::string& digestResult) { BOOST_LOG_FUNCTION(); - see_data hashed_data; - see_data signed_data; + see_data hashed_data = { }; + see_data signed_data = { }; hashed_data.data = (void *)dataToSign.c_str(); hashed_data.length = dataToSign.size(); @@ -145,6 +176,17 @@ int see_backend_context::sign_crypto_data(MessageDigestType digestType, const st auto& resolver(backend->get_dll_resolver()); +#ifdef ENABLE_DEBUG_LODDING + try { + std::string hex; + boost::algorithm::hex((const unsigned char *)hashed_data.data, + (const unsigned char *)hashed_data.data + hashed_data.length, + std::back_inserter(hex)); + BOOST_LOG_SEV(dcm_logger::get(), log_severity::debug) << "Hashed data is " << hex; + } catch(...) { + } +#endif + int error = resolver.invoke( &see_get_ecdsa_signature_key, "see_get_ecdsa_signature", @@ -154,21 +196,80 @@ int see_backend_context::sign_crypto_data(MessageDigestType digestType, const st &signed_data); if(error != 0) { - BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << "Unable to generate ECDSA signature in " << this << " :" << error; + BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << + "Unable to generate ECDSA signature in " << + this << + " (" << error << ") : " << + mbedtls_error_to_string(error); + return MBEDTLS_ERR_PK_BAD_INPUT_DATA; } - BOOST_LOG_SEV(dcm_logger::get(), log_severity::debug) << "SEE: Generated ECDSA signature"; +#ifdef ENABLE_DEBUG_LODDING + BOOST_LOG_SEV(dcm_logger::get(), log_severity::debug) << "Signature length is " << signed_data.length; try { - digestResult.assign((const char *)signed_data.data, signed_data.length); + std::string hex; + boost::algorithm::hex((const unsigned char *)signed_data.data, + (const unsigned char *)signed_data.data + signed_data.length, + std::back_inserter(hex)); + BOOST_LOG_SEV(dcm_logger::get(), log_severity::debug) << "Hex signature is " << hex; } catch(...) { + } +#endif + + if(signed_data.length != 64) { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << + "Incorrect length of signed data " << + signed_data.length << + " but should be 64"; + free(signed_data.data); - throw; + return MBEDTLS_ERR_PK_BAD_INPUT_DATA; } - free(signed_data.data); +#ifdef ENABLE_DEBUG_LODDING + BOOST_LOG_SEV(dcm_logger::get(), log_severity::debug) << "SEE: Generated ECDSA signature"; +#endif + + size_t sig_len = 0; + unsigned char sig[MBEDTLS_ECDSA_MAX_LEN]; + mbedtls_mpi_wrapper r, s; + + error = r.read_binary((const unsigned char *)signed_data.data, 32); + + if(error == 0) { + error = s.read_binary(((const unsigned char *)signed_data.data) + 32, 32); + } + + free(signed_data.data); + + if(error != 0) { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << + "Unable to unserialize binary numbers : " << + mbedtls_error_to_string(error); + return error; + } + + error = ecdsa_signature_to_asn1(&r, &s, sig, &sig_len); + + if(error != 0) { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << + "Unable to convert ECDSA signature : " << + mbedtls_error_to_string(error); + return error; + } + +#ifdef ENABLE_DEBUG_LODDING + try { + std::string hex; + boost::algorithm::hex(sig, sig + sig_len, std::back_inserter(hex)); + BOOST_LOG_SEV(dcm_logger::get(), log_severity::debug) << "Final signature is " << hex; + } catch(...) { + } +#endif + digestResult.assign((const char *)sig, sig_len); return 0; } diff --git a/tests/hw_api_test.cpp b/tests/hw_api_test.cpp index e6680e1..298d699 100644 --- a/tests/hw_api_test.cpp +++ b/tests/hw_api_test.cpp @@ -94,8 +94,6 @@ int main() } std::cout << "Certificate chain parsed successfuly" << std::endl; - - mbedtls_x509_crt_free(&chain); } unsigned char to_sign[32] = { -- 2.7.4 From ccf92ef27ba84804c680b3bd4b7ec366aed63f1e Mon Sep 17 00:00:00 2001 From: Jaroslaw Pelczar Date: Tue, 5 Dec 2017 06:05:46 +0100 Subject: [PATCH 08/16] Move mbedtls_wrapper.h to shared subdirectory Change-Id: I7de8872bb943429ae38618a20f4e7f85248a75d1 Signed-off-by: Jaroslaw Pelczar --- {dcm-daemon => shared}/mbedtls_wrapper.h | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {dcm-daemon => shared}/mbedtls_wrapper.h (100%) diff --git a/dcm-daemon/mbedtls_wrapper.h b/shared/mbedtls_wrapper.h similarity index 100% rename from dcm-daemon/mbedtls_wrapper.h rename to shared/mbedtls_wrapper.h -- 2.7.4 From a1201c8f831ae2a838cf77c5a4c1d28e62eaddf2 Mon Sep 17 00:00:00 2001 From: Jaroslaw Pelczar Date: Tue, 5 Dec 2017 06:29:49 +0100 Subject: [PATCH 09/16] hw_api_test: refactor to use C++ wrappers for mbedtls Change-Id: I8b593f4a8ca3de9f4ac938cb65667bafccf71d06 Signed-off-by: Jaroslaw Pelczar --- shared/mbedtls_wrapper.h | 52 +++++++++++++++++++++++++++++++++++++++--------- tests/hw_api_test.cpp | 50 +++++++++------------------------------------- 2 files changed, 52 insertions(+), 50 deletions(-) diff --git a/shared/mbedtls_wrapper.h b/shared/mbedtls_wrapper.h index ed2ebe3..c72fdfc 100644 --- a/shared/mbedtls_wrapper.h +++ b/shared/mbedtls_wrapper.h @@ -24,6 +24,8 @@ #include #include #include +#include +#include #include #include @@ -39,6 +41,10 @@ struct mbedtls_x509_crt_wrapper : public mbedtls_x509_crt, public boost::noncopy int parse(const std::string& pem) { return mbedtls_x509_crt_parse(this, reinterpret_cast(pem.c_str()), pem.size() + 1); } + + int parse(const unsigned char * pem, size_t size) { + return mbedtls_x509_crt_parse(this, pem, size); + } }; static inline std::string mbedtls_error_to_string(int error) { @@ -59,16 +65,44 @@ struct mbedtls_mpi_wrapper : public mbedtls_mpi, public boost::noncopyable { int read_binary(const void * data, size_t length) { return mbedtls_mpi_read_binary(this, (const unsigned char *)data, length); } +}; + +struct mbedtls_entropy_context_wrapper : public mbedtls_entropy_context, public boost::noncopyable +{ + mbedtls_entropy_context_wrapper() { + mbedtls_entropy_init(this); + } + + ~mbedtls_entropy_context_wrapper() { + mbedtls_entropy_free(this); + } +}; + +struct mbedtls_ctr_drbg_context_wrapper : public mbedtls_ctr_drbg_context, public boost::noncopyable +{ + mbedtls_ctr_drbg_context_wrapper() { + mbedtls_ctr_drbg_init(this); + } + + ~mbedtls_ctr_drbg_context_wrapper() { + mbedtls_ctr_drbg_free(this); + } + + int seed(int (*f_entropy)(void *, unsigned char *, size_t), void *p_entropy, + const unsigned char *custom, size_t len) + { + return mbedtls_ctr_drbg_seed(this, f_entropy, p_entropy, custom, len); + } +}; + +struct mbedtls_pk_context_wrapper : public mbedtls_pk_context, public boost::noncopyable +{ + mbedtls_pk_context_wrapper() { + mbedtls_pk_init(this); + } - int get_raw(const void * data, size_t length) { - const int ciL = sizeof(mbedtls_mpi_uint); - const int biL = ciL * 8; - int error = mbedtls_mpi_grow(this, (length * 8 + biL - 1) / biL); - if(error != 0) - return error; - mbedtls_mpi_lset(this, 0); - memcpy(this->p, data, length); - return 0; + ~mbedtls_pk_context_wrapper() { + mbedtls_pk_free(this); } }; diff --git a/tests/hw_api_test.cpp b/tests/hw_api_test.cpp index 298d699..d5ceac6 100644 --- a/tests/hw_api_test.cpp +++ b/tests/hw_api_test.cpp @@ -11,35 +11,26 @@ #include #include -#include +#include -static inline std::string mbedtls_error_to_string(int error) { - char buffer[256]; - mbedtls_strerror(error, buffer, sizeof(buffer)); - return std::string(buffer); -} +#include int main() { const char *pers = "hw_api_test"; - mbedtls_entropy_context entropy; - mbedtls_ctr_drbg_context ctr_drbg; + mbedtls_entropy_context_wrapper entropy; + mbedtls_ctr_drbg_context_wrapper ctr_drbg; int error; unsigned char result_sig[MBEDTLS_MPI_MAX_SIZE]; size_t result_sig_len; - mbedtls_entropy_init(&entropy); - mbedtls_ctr_drbg_init(&ctr_drbg); - - if( (error = mbedtls_ctr_drbg_seed( &ctr_drbg, mbedtls_entropy_func, &entropy, + if( (error = ctr_drbg.seed(mbedtls_entropy_func, &entropy, (const unsigned char *) pers, - strlen( pers ) )) != 0 ) + strlen( pers ))) != 0 ) { std::cerr << "Can't seed RNG: " << mbedtls_error_to_string(error) << std::endl; - mbedtls_ctr_drbg_free( &ctr_drbg ); - mbedtls_entropy_free( &entropy ); return -1; } @@ -49,22 +40,16 @@ int main() if(!keyContext) { std::cerr << "Can't create DCM key context" << std::endl; - mbedtls_ctr_drbg_free( &ctr_drbg ); - mbedtls_entropy_free( &entropy ); return -1; } - mbedtls_pk_context pkey; - std::cout << "Initialize PK context" << std::endl; - mbedtls_pk_init(&pkey); + mbedtls_pk_context_wrapper pkey; if(DCM_HWSetupPkContext(&pkey, keyContext) != 0) { std::cerr << "Can't setup key context" << std::endl; DCM_HWFreeKeyContext(keyContext); - mbedtls_ctr_drbg_free( &ctr_drbg ); - mbedtls_entropy_free( &entropy ); return -1; } else { std::cout << "Key context setup OK" << std::endl; @@ -74,22 +59,18 @@ int main() unsigned char * certChain = nullptr; size_t certChainLen = 0; - mbedtls_x509_crt chain; - mbedtls_x509_crt_init(&chain); + mbedtls_x509_crt_wrapper chain; if(DCM_HWGetOwnCertificateChain(keyContext, &certChain, &certChainLen)) { std::cerr << "Can't request certificate chain" << std::endl; } else { std::cout << "Certificate received" << std::endl; - error = mbedtls_x509_crt_parse(&chain, certChain, certChainLen); + error = chain.parse(certChain, certChainLen); if(error != 0) { std::cerr << "Can't parse certificate chain: " << mbedtls_error_to_string(error) << std::endl; DCM_HWFreeKeyContext(keyContext); - mbedtls_x509_crt_free(&chain); - mbedtls_ctr_drbg_free( &ctr_drbg ); - mbedtls_entropy_free( &entropy ); return -1; } @@ -112,11 +93,7 @@ int main() mbedtls_ctr_drbg_random, &ctr_drbg)) != 0) { std::cerr << "Can't sign data with key: " << mbedtls_error_to_string(error) << std::endl; - mbedtls_pk_free(&pkey); DCM_HWFreeKeyContext(keyContext); - mbedtls_x509_crt_free(&chain); - mbedtls_ctr_drbg_free( &ctr_drbg ); - mbedtls_entropy_free( &entropy ); return -1; } @@ -129,7 +106,6 @@ int main() std::cout << std::endl; // Verify signature - std::cout << "Verifying signature ..." << std::endl; if((error = mbedtls_pk_verify_ext(chain.sig_pk, @@ -146,18 +122,10 @@ int main() std::cout << "Signature verification succeeded" << std::endl; } - mbedtls_x509_crt_free(&chain); - - std::cout << "Freeing PK context" << std::endl; - mbedtls_pk_free(&pkey); - std::cout << "Deleting HW context" << std::endl; DCM_HWFreeKeyContext(keyContext); std::cout << "Finished" << std::endl; - mbedtls_ctr_drbg_free( &ctr_drbg ); - mbedtls_entropy_free( &entropy ); - return 0; } -- 2.7.4 From 5ced502eef6f0e7b6c97475629d352f605d62413 Mon Sep 17 00:00:00 2001 From: Jaroslaw Pelczar Date: Tue, 5 Dec 2017 06:32:09 +0100 Subject: [PATCH 10/16] Handle valid case for hash_size==0 If hash size is 0 then MD algorithm must not be NONE, in this case hash_size is extracted from expected length for MD algorithm. If MD algorithm is not NONE, then hash_size is validated against the algorithm's expected input length. Change-Id: Ib95d8138ce6a21c364a5d7f88910fde15e32a026 Signed-off-by: Jaroslaw Pelczar --- dcm-client/dcmclient.cpp | 42 +++++++++++++++++++++++++++++++++--------- dcm-daemon/dcmsession.cpp | 38 +++++++++++++++++++++++--------------- 2 files changed, 56 insertions(+), 24 deletions(-) diff --git a/dcm-client/dcmclient.cpp b/dcm-client/dcmclient.cpp index 22f9bb9..8f82f03 100644 --- a/dcm-client/dcmclient.cpp +++ b/dcm-client/dcmclient.cpp @@ -297,25 +297,49 @@ int dcm_client_connection_impl::sign_data(mbedtls_md_type_t digestType, const vo return MBEDTLS_ERR_PK_BAD_INPUT_DATA; } - const mbedtls_md_info_t * md_info = mbedtls_md_info_from_type(digestType); + /* + * If hash_size == 0 then hash type must be known + */ + if(hash_size == 0) { + if(digestType == MBEDTLS_MD_NONE) { +#ifdef USE_DLOG_LOGGING + LOGD("%s: Digest type is NONE and hash size is 0", __FUNCTION__); +#endif + return MBEDTLS_ERR_PK_BAD_INPUT_DATA; + } - if(!md_info) { + const mbedtls_md_info_t * md_info = mbedtls_md_info_from_type(digestType); + + if(!md_info) { #ifdef USE_DLOG_LOGGING - LOGD("%s: Can't find hash data for digest type %d", __FUNCTION__, digestType); + LOGD("%s: Can't find hash data for digest type %d", __FUNCTION__, digestType); #endif - return MBEDTLS_ERR_PK_BAD_INPUT_DATA; - } + return MBEDTLS_ERR_PK_BAD_INPUT_DATA; + } - if(hash_size == 0) { #ifdef USE_DLOG_LOGGING LOGD("%s: Overriding hash size to %zd bytes", __FUNCTION__, hash_size); #endif hash_size = mbedtls_md_get_size(md_info); - } else if(hash_size != mbedtls_md_get_size(md_info)) { + } else if(hash_size != 0 && digestType != MBEDTLS_MD_NONE) { + /* + * If hash_size != 0 then hash type can be specified + */ + const mbedtls_md_info_t * md_info = mbedtls_md_info_from_type(digestType); + + if(!md_info) { #ifdef USE_DLOG_LOGGING - LOGE("%s: Hash size mismatch. Expected %zd but got %zd", __FUNCTION__, hash_size, (size_t)mbedtls_md_get_size(md_info)); + LOGD("%s: Can't find hash data for digest type %d", __FUNCTION__, digestType); #endif - return MBEDTLS_ERR_PK_BAD_INPUT_DATA; + return MBEDTLS_ERR_PK_BAD_INPUT_DATA; + } + + if(hash_size != mbedtls_md_get_size(md_info)) { +#ifdef USE_DLOG_LOGGING + LOGE("%s: Hash size mismatch. Expected %zd but got %zd", __FUNCTION__, hash_size, (size_t)mbedtls_md_get_size(md_info)); +#endif + return MBEDTLS_ERR_PK_BAD_INPUT_DATA; + } } try { diff --git a/dcm-daemon/dcmsession.cpp b/dcm-daemon/dcmsession.cpp index 615e762..9bdd07d 100644 --- a/dcm-daemon/dcmsession.cpp +++ b/dcm-daemon/dcmsession.cpp @@ -402,25 +402,33 @@ void dcm_session::handle_sign_request(const SignRequest& message) return; } - const mbedtls_md_info_t * md_info = mbedtls_md_info_from_type( - static_cast( - message.digest_type())); - - if(!md_info) { - BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << "Can't find crypto algorithm specified by caller"; + if(message.data_to_sign().size() == 0) { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << "Data to sign is empty and hash type is NONE"; signingResponse->set_result(MBEDTLS_ERR_PK_BAD_INPUT_DATA); - reply(msg); return; } - if(message.data_to_sign().size() != mbedtls_md_get_size(md_info)) { - BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << - "Input hash length mismatch. It is " << - message.data_to_sign().size() << " but should be " << - mbedtls_md_get_size(md_info); - signingResponse->set_result(MBEDTLS_ERR_PK_BAD_INPUT_DATA); - reply(msg); - return; + mbedtls_md_type_t mdType = static_cast(message.digest_type()); + + if(mdType != MBEDTLS_MD_NONE) { + const mbedtls_md_info_t * md_info = mbedtls_md_info_from_type(mdType); + + if(!md_info) { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << "Can't find MD algorithm specified by caller"; + signingResponse->set_result(MBEDTLS_ERR_PK_BAD_INPUT_DATA); + reply(msg); + return; + } + + if(message.data_to_sign().size() != mbedtls_md_get_size(md_info)) { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << + "Input hash length mismatch. It is " << + message.data_to_sign().size() << " but should be " << + mbedtls_md_get_size(md_info); + signingResponse->set_result(MBEDTLS_ERR_PK_BAD_INPUT_DATA); + reply(msg); + return; + } } signingResponse->set_result( -- 2.7.4 From eb65907cd0e054840f2490a38cafc4c836537df4 Mon Sep 17 00:00:00 2001 From: Jaroslaw Pelczar Date: Tue, 5 Dec 2017 06:37:32 +0100 Subject: [PATCH 11/16] hw_api_test: use random test data Also print data using boost::algorithm::hex Change-Id: I57981550c4101e593916851073fa73f753f1a505 Signed-off-by: Jaroslaw Pelczar --- tests/hw_api_test.cpp | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/tests/hw_api_test.cpp b/tests/hw_api_test.cpp index d5ceac6..2340a55 100644 --- a/tests/hw_api_test.cpp +++ b/tests/hw_api_test.cpp @@ -15,6 +15,8 @@ #include +#include + int main() { const char *pers = "hw_api_test"; @@ -77,12 +79,13 @@ int main() std::cout << "Certificate chain parsed successfuly" << std::endl; } - unsigned char to_sign[32] = { - 1,2,3,4,5,6,7,8,9,0, - 1,2,3,4,5,6,7,8,9,0, - 1,2,3,4,5,6,7,8,9,0, - 11,11 - }; + unsigned char to_sign[32]; + + mbedtls_ctr_drbg_random(&ctr_drbg, to_sign, sizeof(to_sign)); + + std::cout << "Data to sign = "; + boost::algorithm::hex(std::begin(to_sign), std::end(to_sign), std::ostream_iterator(std::cout)); + std::cout << std::endl; if((error = mbedtls_pk_sign(&pkey, MBEDTLS_MD_SHA256, @@ -99,9 +102,7 @@ int main() std::cout << "Signature = "; - for(size_t i = 0 ; i < result_sig_len ; ++i) { - std::cout << std::hex << std::setw(2) << (unsigned int)result_sig[i] << " "; - } + boost::algorithm::hex(result_sig, result_sig + result_sig_len, std::ostream_iterator(std::cout)); std::cout << std::endl; -- 2.7.4 From 3693510e95883ad1ee30666a981b123f146d8fad Mon Sep 17 00:00:00 2001 From: Jaroslaw Pelczar Date: Tue, 5 Dec 2017 06:43:59 +0100 Subject: [PATCH 12/16] [cmake] Add missing include directory Change-Id: I0d6be92ff596bee9eab05a0286b924335276ae33 Signed-off-by: Jaroslaw Pelczar --- tests/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 205c1a5..3122c2a 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,11 +1,11 @@ -include_directories(../dcm-client) +include_directories(../dcm-client ../shared) include_directories(${CMAKE_BINARY_DIR}/dcm-client) add_executable(dcm_example_client example_client.cpp) target_link_libraries(dcm_example_client device-certificate-manager) add_executable(dcm_hw_api_test hw_api_test.cpp) -target_link_libraries(dcm_hw_api_test device-certificate-manager ${MBEDTLS_LIB} ${MBEDCRYPTO_LIB}) +target_link_libraries(dcm_hw_api_test device-certificate-manager ${MBEDTLS_LIB} ${MBEDCRYPTO_LIB} ${MBEDX509_LIB}) install(TARGETS dcm_example_client dcm_hw_api_test RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) -- 2.7.4 From 5ff30f836286030b8ee46b848a152fecd6ece6af Mon Sep 17 00:00:00 2001 From: Jaroslaw Pelczar Date: Tue, 5 Dec 2017 07:18:30 +0100 Subject: [PATCH 13/16] dcm-client: remove debug level logs, leave only error logs Change-Id: I65f9f68274c5407ed6f51df2db81ca8298233d9e Signed-off-by: Jaroslaw Pelczar --- dcm-client/dcm_hw_interface.cpp | 30 +++++----------------- dcm-client/dcmclient.cpp | 57 +++++------------------------------------ 2 files changed, 13 insertions(+), 74 deletions(-) diff --git a/dcm-client/dcm_hw_interface.cpp b/dcm-client/dcm_hw_interface.cpp index 3e2ad58..332141a 100644 --- a/dcm-client/dcm_hw_interface.cpp +++ b/dcm-client/dcm_hw_interface.cpp @@ -43,10 +43,6 @@ static std::map> sEDCSAContex static std::mutex sEDCSAContextsMutex; void* DCM_HWGetKeyContext(const char* service, const char* usage, const char* keytype) { -#ifdef USE_DLOG_LOGGING - LOGD("Create new context for"); -#endif - try { std::unique_ptr context(new dcm_key_context_internal()); @@ -66,11 +62,12 @@ void* DCM_HWGetKeyContext(const char* service, const char* usage, const char* ke return nullptr; } + return context.release(); + } catch(std::exception& ex) { #ifdef USE_DLOG_LOGGING - LOGD("Created context %p", context.get()); + LOGE("Context creation failure: %s", ex.what()); #endif - - return context.release(); + return nullptr; } catch(...) { #ifdef USE_DLOG_LOGGING LOGE("Context creation failure"); @@ -85,10 +82,6 @@ int DCM_HWFreeKeyContext(void* keyContext) return HWIF_ERR_INVALID_PARAM; } -#ifdef USE_DLOG_LOGGING - LOGD("Delete context %p", keyContext); -#endif - delete reinterpret_cast(keyContext); return HWIF_SUCCESS; @@ -104,15 +97,7 @@ int DCM_HWGetOwnCertificateChain(const void* keyContext, return HWIF_ERR_INVALID_PARAM; } -#ifdef USE_DLOG_LOGGING - LOGD("Request certificate chain for session %p", keyContext); -#endif - if(!context->cached_cert_chain.empty()) { -#ifdef USE_DLOG_LOGGING - LOGD("Use cached certificate chain"); -#endif - *cert_chain = &context->cached_cert_chain[0]; *cert_chain_len = context->cached_cert_chain.size(); return HWIF_SUCCESS; @@ -149,6 +134,9 @@ static int pk_rsa_alt_sign_func( void *ctx, } if(mode != MBEDTLS_RSA_PRIVATE) { +#ifdef USE_DLOG_LOGGING + LOGE("Signing mode must be RSA_PRIVATE"); +#endif return MBEDTLS_ERR_PK_BAD_INPUT_DATA; } @@ -305,10 +293,6 @@ int DCM_HWSetupPkContext(mbedtls_pk_context* ctx, void* key_context) return HWIF_ERR_INVALID_PARAM; } -#ifdef USE_DLOG_LOGGING - LOGD("Setup PK context %p with key context %p", ctx, key_context); -#endif - const auto& key_type(context->connection->key_type()); if(key_type == "RSA") { diff --git a/dcm-client/dcmclient.cpp b/dcm-client/dcmclient.cpp index 8f82f03..82acda3 100644 --- a/dcm-client/dcmclient.cpp +++ b/dcm-client/dcmclient.cpp @@ -26,6 +26,7 @@ #include #include #include + #ifdef USE_DLOG_LOGGING #define LOG_TAG "dcm-client" #include @@ -56,16 +57,10 @@ dcm_client_connection_impl::~dcm_client_connection_impl() dcm_client_connection::dcm_client_connection() { -#ifdef USE_DLOG_LOGGING - LOGD("dcm_client_connection: Allocated new client connection at %p", this); -#endif } dcm_client_connection::~dcm_client_connection() { -#ifdef USE_DLOG_LOGGING - LOGD("dcm_client_connection: Deallocated client connection at %p", this); -#endif } std::shared_ptr dcm_client_connection::create() @@ -75,17 +70,8 @@ std::shared_ptr dcm_client_connection::create() void dcm_client_connection_impl::sendReceive(RequestMessage& request, ResponseMessage& response) { -#ifdef USE_DLOG_LOGGING - LOGD("Send request to server in connection %p of type %d", this, request.request_oneof_case()); -#endif - protobuf_sync_message_serialization(*fSocket).encodeMessage(request); - protobuf_sync_message_deserialization(*fSocket).decodeMessage(response); - -#ifdef USE_DLOG_LOGGING - LOGD("Received response from server in connection %p of type %d", this, response.reply_oneof_case()); -#endif } bool dcm_client_connection_impl::create_context(const std::string& serviceName, @@ -103,9 +89,6 @@ bool dcm_client_connection_impl::create_context(const std::string& serviceName, } if(!fSocket) { -#ifdef USE_DLOG_LOGGING - LOGD("%s: Ensure that socket is connected for session %p", __FUNCTION__, this); -#endif try { ensureSocketConnected(); } catch(std::exception& ex) { @@ -121,10 +104,6 @@ bool dcm_client_connection_impl::create_context(const std::string& serviceName, } } -#ifdef USE_DLOG_LOGGING - LOGD("%s: Connection established. Requesting cookie", __FUNCTION__); -#endif - try { RequestMessage request; ResponseMessage response; @@ -156,15 +135,6 @@ bool dcm_client_connection_impl::create_context(const std::string& serviceName, fCookie = assoc_message.context_cookie(); fKeyType = assoc_message.key_type(); fKeyLength = assoc_message.key_length(); - -#ifdef USE_DLOG_LOGGING - LOGD("%s: Received cookie %" PRIx64 " with key type %s and length %zd for session %p", - __FUNCTION__, - fCookie, - this->key_type().c_str(), - fKeyLength, - this); -#endif } catch(std::exception& ex) { #ifdef USE_DLOG_LOGGING LOGE("%s: Caught exception \"%s\" when establishing cookie for session %p", __FUNCTION__, ex.what(), this); @@ -200,10 +170,6 @@ int dcm_client_connection_impl::get_certificate_chain(std::vector& chai return HWIF_ERR_INVALID_PARAM; } -#ifdef USE_DLOG_LOGGING - LOGD("%s: Requesting certificate chain for session %p", __FUNCTION__, this); -#endif - try { RequestMessage request; ResponseMessage response; @@ -248,10 +214,6 @@ int dcm_client_connection_impl::get_certificate_chain(std::vector& chai // Pad with zero chain.push_back(0); } - -#ifdef USE_DLOG_LOGGING - LOGD("%s: Received %zd bytes of certificate for session %p", __FUNCTION__, cert_resp.cert_chain().size(), this); -#endif } catch(std::bad_alloc&) { #ifdef USE_DLOG_LOGGING LOGE("%s: Out of memory when requesting certificate for session %p", __FUNCTION__, this); @@ -292,7 +254,7 @@ int dcm_client_connection_impl::sign_data(mbedtls_md_type_t digestType, const vo if(!fCookie) { #ifdef USE_DLOG_LOGGING - LOGD("%s: Trying to request data signing in object %p but there is no connection", __FUNCTION__); + LOGE("%s: Trying to request data signing in object %p but there is no connection", __FUNCTION__); #endif return MBEDTLS_ERR_PK_BAD_INPUT_DATA; } @@ -303,7 +265,7 @@ int dcm_client_connection_impl::sign_data(mbedtls_md_type_t digestType, const vo if(hash_size == 0) { if(digestType == MBEDTLS_MD_NONE) { #ifdef USE_DLOG_LOGGING - LOGD("%s: Digest type is NONE and hash size is 0", __FUNCTION__); + LOGE("%s: Digest type is NONE and hash size is 0", __FUNCTION__); #endif return MBEDTLS_ERR_PK_BAD_INPUT_DATA; } @@ -312,14 +274,11 @@ int dcm_client_connection_impl::sign_data(mbedtls_md_type_t digestType, const vo if(!md_info) { #ifdef USE_DLOG_LOGGING - LOGD("%s: Can't find hash data for digest type %d", __FUNCTION__, digestType); + LOGE("%s: Can't find hash data for digest type %d", __FUNCTION__, digestType); #endif return MBEDTLS_ERR_PK_BAD_INPUT_DATA; } -#ifdef USE_DLOG_LOGGING - LOGD("%s: Overriding hash size to %zd bytes", __FUNCTION__, hash_size); -#endif hash_size = mbedtls_md_get_size(md_info); } else if(hash_size != 0 && digestType != MBEDTLS_MD_NONE) { /* @@ -329,7 +288,7 @@ int dcm_client_connection_impl::sign_data(mbedtls_md_type_t digestType, const vo if(!md_info) { #ifdef USE_DLOG_LOGGING - LOGD("%s: Can't find hash data for digest type %d", __FUNCTION__, digestType); + LOGE("%s: Can't find hash data for digest type %d", __FUNCTION__, digestType); #endif return MBEDTLS_ERR_PK_BAD_INPUT_DATA; } @@ -356,7 +315,7 @@ int dcm_client_connection_impl::sign_data(mbedtls_md_type_t digestType, const vo if(!response.has_sign_data()) { #ifdef USE_DLOG_LOGGING - LOGD("%s: Response for hash signature has no signature data", __FUNCTION__); + LOGE("%s: Response for hash signature has no signature data", __FUNCTION__); #endif return MBEDTLS_ERR_PK_BAD_INPUT_DATA; } @@ -381,10 +340,6 @@ int dcm_client_connection_impl::sign_data(mbedtls_md_type_t digestType, const vo digest.resize(signature.size()); memcpy(&digest[0], signature.c_str(), signature.size()); - -#ifdef USE_DLOG_LOGGING - LOGD("%s: Received %zd bytes of signed object for session %p", __FUNCTION__, signature.size(), this); -#endif } catch(std::bad_alloc&) { #ifdef USE_DLOG_LOGGING LOGE("%s: Out of memory when processing sign request for session %p", __FUNCTION__, this); -- 2.7.4 From 833d132197b38a9ada15d491b6b61beeb78597be Mon Sep 17 00:00:00 2001 From: Jaroslaw Pelczar Date: Tue, 5 Dec 2017 08:35:42 +0100 Subject: [PATCH 14/16] Use Cynara for access control Change-Id: I42e7aebfd0369301877e023ad5721547098b20df Signed-off-by: Jaroslaw Pelczar --- cmake/CheckFrameworks.cmake | 6 +- dcm-daemon/CMakeLists.txt | 34 ++-- dcm-daemon/cryptobackendroster.h | 3 + dcm-daemon/dcmsession.cpp | 194 ++++++++------------- dcm-daemon/dcmsession.h | 2 +- dcm-daemon/main.cpp | 37 +++- .../device-certificate-manager-tests.manifest.in | 15 +- packaging/device-certificate-manager.spec | 5 +- 8 files changed, 137 insertions(+), 159 deletions(-) diff --git a/cmake/CheckFrameworks.cmake b/cmake/CheckFrameworks.cmake index 34ed41b..ef52463 100644 --- a/cmake/CheckFrameworks.cmake +++ b/cmake/CheckFrameworks.cmake @@ -28,8 +28,6 @@ ENDIF() PKG_CHECK_MODULES(DLOG dlog) -PKG_CHECK_MODULES(SECURITY_MANAGER security-manager) - CHECK_FUNCTION_EXISTS(fork HAVE_FORK) find_package(Protobuf REQUIRED) @@ -65,4 +63,6 @@ ENDIF() PKG_CHECK_MODULES(SYSTEMD libsystemd) -PKG_CHECK_MODULES(SMACK libsmack) +#### Cynara #### + +PKG_CHECK_MODULES(CYNARA cynara-client cynara-creds-socket cynara-session) diff --git a/dcm-daemon/CMakeLists.txt b/dcm-daemon/CMakeLists.txt index 90f64a4..2c50e1c 100644 --- a/dcm-daemon/CMakeLists.txt +++ b/dcm-daemon/CMakeLists.txt @@ -21,13 +21,6 @@ IF(DLOG_FOUND) add_definitions(-DUSE_DLOG_LOGGING=1) ENDIF(DLOG_FOUND) -IF(SECURITY_MANAGER_FOUND) - include_directories(${SECURITY_MANAGER_INCLUDE_DIRS}) - link_directories(${SECURITY_MANAGER_LIBRARY_DIRS}) - add_definitions(${SECURITY_MANAGER_CFLAGS_OTHER}) - add_definitions(-DUSE_SECURITY_MANAGER=1) -ENDIF(SECURITY_MANAGER_FOUND) - IF(ENABLE_SYSTEMD_SUPPORT) include_directories(${SYSTEMD_INCLUDE_DIRS}) link_directories(${SYSTEMD_LIBRARY_DIRS}) @@ -41,12 +34,12 @@ IF(ARTIK_SECURITY_FOUND) add_definitions(-DUSE_ARTIK_SECURITY_HEADERS=1) ENDIF(ARTIK_SECURITY_FOUND) -IF(SMACK_FOUND) - include_directories(${SMACK_INCLUDE_DIRS}) - link_directories(${SMACK_LIBRARY_DIRS}) - add_definitions(${SMACK_CFLAGS_OTHER}) - add_definitions(-DUSE_SMACK=1) -ENDIF(SMACK_FOUND) +IF(CYNARA_FOUND) + include_directories(${CYNARA_INCLUDE_DIRS}) + link_directories(${CYNARA_LIBRARY_DIRS}) + add_definitions(${CYNARA_CFLAGS_OTHER}) + add_definitions(-DUSE_CYNARA=1) +ENDIF(CYNARA_FOUND) include_directories(${Boost_INCLUDE_DIRS}) link_directories(${Boost_LIBRARY_DIRS}) @@ -60,11 +53,10 @@ add_subdirectory(see-backend) IF(ENABLE_DUMMY_BACKEND) SET(DUMMY_BACKEND_OBJECTS $) + add_definitions(-DENABLE_DUMMY_BACKEND=1) ENDIF(ENABLE_DUMMY_BACKEND) -IF(SECURITY_MANAGER_FOUND) - SET(SEE_BACKEND_OBJECTS $) -ENDIF(SECURITY_MANAGER_FOUND) +SET(SEE_BACKEND_OBJECTS $) ###### Main executable ####### @@ -107,17 +99,13 @@ IF(DLOG_FOUND) target_link_libraries(device-certificate-managerd ${DLOG_LIBRARIES}) ENDIF(DLOG_FOUND) -IF(SECURITY_MANAGER_FOUND) - target_link_libraries(device-certificate-managerd ${SECURITY_MANAGER_LIBRARIES}) -ENDIF(SECURITY_MANAGER_FOUND) - IF(ENABLE_SYSTEMD_SUPPORT) target_link_libraries(device-certificate-managerd ${SYSTEMD_LIBRARIES}) ENDIF(ENABLE_SYSTEMD_SUPPORT) -IF(SMACK_FOUND) - target_link_libraries(device-certificate-managerd ${SMACK_LIBRARIES}) -ENDIF(SMACK_FOUND) +IF(CYNARA_FOUND) + target_link_libraries(device-certificate-managerd ${CYNARA_LIBRARIES}) +ENDIF(CYNARA_FOUND) ###### Installation ####### diff --git a/dcm-daemon/cryptobackendroster.h b/dcm-daemon/cryptobackendroster.h index 2514412..3117136 100644 --- a/dcm-daemon/cryptobackendroster.h +++ b/dcm-daemon/cryptobackendroster.h @@ -43,6 +43,9 @@ template struct crypto_backend_registration { crypto_backend_registration() { crypto_backend_roster::instance().register_backend(std::make_shared()); } + + void ensure_inited() { + } }; #endif /* DCM_DAEMON_CRYPTOBACKENDROSTER_H_ */ diff --git a/dcm-daemon/dcmsession.cpp b/dcm-daemon/dcmsession.cpp index 9bdd07d..ad09839 100644 --- a/dcm-daemon/dcmsession.cpp +++ b/dcm-daemon/dcmsession.cpp @@ -31,21 +31,22 @@ #include #include -#ifdef USE_SMACK -#include +#ifdef USE_CYNARA +#include +#include +#include #endif -#ifdef USE_SECURITY_MANAGER -#include -#endif - -#ifdef USE_SMACK -static char const *const OWNER_ID_SYSTEM = "/System"; -#endif +#ifdef USE_CYNARA +extern cynara * gGlobalCynaraInstance; -#if defined(USE_SECURITY_MANAGER) && defined(USE_SMACK) -static std::map sPackageIdMapping; -static std::mutex sPackageIdMappingMutex; +static inline std::string cynara_error_to_string(int error) { + char buffer[256]; + int ret = cynara_strerror(error, buffer, sizeof(buffer)); + if(ret == CYNARA_API_SUCCESS) + return std::string(buffer); + return std::string("Can't translate error"); +} #endif dcm_session::dcm_session(boost::asio::io_service& io_service, const std::shared_ptr& server) : @@ -61,136 +62,79 @@ dcm_session::~dcm_session() BOOST_LOG_SEV(dcm_logger::get(), log_severity::debug) << "Destroy session object " << this; } -#if defined(USE_SMACK) && defined(USE_SECURITY_MANAGER) -static int assignToString(std::vector &vec, socklen_t len, std::string &res) -{ - if (vec.size() <= len) - return -1; - vec[len] = 0; // old implementation getsockopt returns cstring without 0 - if (vec[len - 1] == 0) --len;// new implementation of getsockopt returns cstring size+1 - res.assign(vec.data(), len); - return 0; -} - -static int getCredentialsFromSocket(int sock, std::string &res) -{ - std::vector result(SMACK_LABEL_LEN + 1); - socklen_t length = SMACK_LABEL_LEN; - - if (0 == getsockopt(sock, SOL_SOCKET, SO_PEERSEC, result.data(), &length)) { - return assignToString(result, length, res); - } - - if (errno != ERANGE) { - BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << "getsockopt failed"; - return -1; +struct string_free_deleter { + void operator()(char * p) const { + free(p); } +}; - result.resize(length + 1); - - if (0 > getsockopt(sock, SOL_SOCKET, SO_PEERSEC, result.data(), &length)) { - BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << "getsockopt failed with errno: " << errno; - return -1; - } - - return assignToString(result, length, res); -} - -static int getPkgIdFromSocket(int sock, std::string &pkgId) +bool dcm_session::verify_privileges(int handle) { - char *pkg = nullptr; +#ifdef USE_CYNARA + BOOST_LOG_FUNCTION(); - int ret = security_manager_identify_app_from_socket(sock, &pkg, nullptr); + int ret = 0; + char * tmp_str; + pid_t pid = 0; - if (ret == SECURITY_MANAGER_ERROR_NO_SUCH_OBJECT) { - BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << "Owner of socket is not connected with pkgid. " - "This case must be special-labled client. e.g. User, System"; - return 1; - } + std::unique_ptr user; + std::unique_ptr client; + std::unique_ptr client_session; - if (ret != SECURITY_MANAGER_SUCCESS) { - BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << "security_manager_identify_app_from_socket failed with error: " - << ret; - return -1; + /* Get user info */ + tmp_str = nullptr; + ret = cynara_creds_socket_get_user(handle, USER_METHOD_DEFAULT, &tmp_str); + if(ret != CYNARA_API_SUCCESS) { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << "Can't get user from socket : " << ret << " - " << cynara_error_to_string(ret); + return false; } + user.reset(tmp_str); - try { - pkgId = pkg; - } catch(...) { - free(pkg); - throw; + /* Get client info */ + tmp_str = nullptr; + ret = cynara_creds_socket_get_client(handle, CLIENT_METHOD_DEFAULT, &tmp_str); + if(ret != CYNARA_API_SUCCESS) { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << "Can't get client from socket : " << ret << " - " << cynara_error_to_string(ret); + return false; } + client.reset(tmp_str); - free(pkg); - BOOST_LOG_SEV(dcm_logger::get(), log_severity::debug) << "Socket: " << sock << " Was translated to owner id: " << pkgId; - return 0; -} - -static void mapToDomainLabel(std::string &label) -{ - static const std::string subdomainSep = "::"; - static const auto systemLabelLen = strlen(OWNER_ID_SYSTEM); - if (label.length() > systemLabelLen + subdomainSep.length() && - label.compare(0, systemLabelLen, OWNER_ID_SYSTEM) == 0 && - label.compare(systemLabelLen, subdomainSep.length(), subdomainSep) == 0) { - label = OWNER_ID_SYSTEM; + /* Get client PID from socket */ + ret = cynara_creds_socket_get_pid(handle, &pid); + if(ret != CYNARA_API_SUCCESS) { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << "Can't get PID from socket : " << ret << " - " << cynara_error_to_string(ret); + return false; } -} -#endif - -bool dcm_session::get_client_id(int handle, std::string& result) -{ - BOOST_LOG_FUNCTION(); - -#if defined(USE_SMACK) && defined(USE_SECURITY_MANAGER) - try { - std::string smackLabel; - int error = getCredentialsFromSocket(handle, smackLabel); - - if(error < 0) { - BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << "Socket access failure. Disconnecting"; - return false; - } - - BOOST_LOG_SEV(dcm_logger::get(), log_severity::debug) << "Client credential is " << smackLabel; - - std::unique_lock locker(sPackageIdMappingMutex); - - auto it = sPackageIdMapping.find(smackLabel); - if(it != sPackageIdMapping.end()) { - result = it->second; - return true; - } - - std::string pkgId; - int retCode = getPkgIdFromSocket(handle, pkgId); - - if (retCode < 0) { - return false; - } - - if (retCode == 1) { - BOOST_LOG_SEV(dcm_logger::get(), log_severity::debug) << "Special smack label case. label: " << smackLabel; - pkgId = "/" + smackLabel; - } + client_session.reset(cynara_session_from_pid(pid)); + if(!client_session) { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << "Can't get session identifier from PID"; + return false; + } - mapToDomainLabel(pkgId); + BOOST_LOG_SEV(dcm_logger::get(), log_severity::debug) << "Got new session from " << pid << " with user " << + user.get() << ", client ID " << client.get() << " and session ID " << client_session.get(); - BOOST_LOG_SEV(dcm_logger::get(), log_severity::debug) << smackLabel << " mapped to " << pkgId; + ret = cynara_check(gGlobalCynaraInstance, + client.get(), + client_session.get(), + user.get(), + "http://tizen.org/privilege/internet"); - result = pkgId; - sPackageIdMapping.emplace(std::move(smackLabel), std::move(pkgId)); + if(ret != CYNARA_API_ACCESS_ALLOWED) { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << + "Application access denied - no internet permission for " << + pid << + " - " << + cynara_error_to_string(ret); - return true; - } catch(std::exception& ex) { - BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << "Caught exception when translating socket: " << ex.what(); - return false; - } catch(...) { - BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << "Caught unknown exception when translating socket"; return false; } + + BOOST_LOG_SEV(dcm_logger::get(), log_severity::debug) << "Access granted for " << pid; +#else + (void)handle; #endif return true; @@ -204,8 +148,10 @@ void dcm_session::start() BOOST_LOG_SEV(dcm_logger::get(), log_severity::debug) << "Accepted connection with socket " << fSocket.native_handle(); std::string label; - if(get_client_id(handle, label)) { + if(verify_privileges(handle)) { do_receive(); + } else { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << "Client privilege check failure. Disconnect"; } } diff --git a/dcm-daemon/dcmsession.h b/dcm-daemon/dcmsession.h index a2458cf..7124247 100644 --- a/dcm-daemon/dcmsession.h +++ b/dcm-daemon/dcmsession.h @@ -51,7 +51,7 @@ private: void decode_message() noexcept; void reply(const ResponseMessage& resp) noexcept; - bool get_client_id(int handle, std::string& result); + bool verify_privileges(int handle); void handle_context_association(const AssociateKeyContext& message); void handle_cert_chain(const RequestCertificateChain& message); diff --git a/dcm-daemon/main.cpp b/dcm-daemon/main.cpp index e1f8fd2..e4ec56e 100644 --- a/dcm-daemon/main.cpp +++ b/dcm-daemon/main.cpp @@ -37,6 +37,10 @@ #include +#ifdef USE_CYNARA +#include +#endif + namespace po = boost::program_options; struct OptionContext { @@ -124,8 +128,14 @@ void init_logging() boost::log::core::get()->add_thread_attribute("Scope", boost::log::attributes::named_scope()); } +#ifdef USE_CYNARA +cynara * gGlobalCynaraInstance; +#endif + int main(int argc, char ** argv) { + int error = 0; + init_logging(); BOOST_LOG_FUNCTION(); @@ -150,6 +160,26 @@ int main(int argc, char ** argv) service_adapter serviceAdapter(options.fSocketName); +#ifdef USE_CYNARA + cynara_configuration * cynara_conf = nullptr; + error = cynara_configuration_create(&cynara_conf); + if(error != CYNARA_API_SUCCESS) { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << "Can't initialize Cynara configuration: " << error; + serviceAdapter.notify_start_failure(error); + return EXIT_FAILURE; + } + + error = cynara_initialize(&gGlobalCynaraInstance, cynara_conf); + + cynara_configuration_destroy(cynara_conf); + + if(error != CYNARA_API_SUCCESS) { + BOOST_LOG_SEV(dcm_logger::get(), log_severity::error) << "Can't initialize Cynara instance: " << error; + serviceAdapter.notify_start_failure(error); + return EXIT_FAILURE; + } +#endif + try { boost::asio::io_service io_service; @@ -165,7 +195,7 @@ int main(int argc, char ** argv) (void)umask(0); /* Use root directory as working directory */ - int error = chdir("/"); + error = chdir("/"); (void)error; // Don't care BOOST_LOG_SEV(dcm_logger::get(), log_severity::debug) << "Create platform socket"; @@ -198,6 +228,11 @@ int main(int argc, char ** argv) return EXIT_FAILURE; } +#ifdef USE_CYNARA + cynara_finish(gGlobalCynaraInstance); + gGlobalCynaraInstance = nullptr; +#endif + BOOST_LOG_SEV(dcm_logger::get(), log_severity::normal) << "Server terminated"; return 0; diff --git a/packaging/device-certificate-manager-tests.manifest.in b/packaging/device-certificate-manager-tests.manifest.in index 3b8c320..9bd7968 100644 --- a/packaging/device-certificate-manager-tests.manifest.in +++ b/packaging/device-certificate-manager-tests.manifest.in @@ -1,9 +1,14 @@ + + + + + + + + + - + - - - - diff --git a/packaging/device-certificate-manager.spec b/packaging/device-certificate-manager.spec index deafc29..0689971 100644 --- a/packaging/device-certificate-manager.spec +++ b/packaging/device-certificate-manager.spec @@ -10,11 +10,12 @@ Source1002: device-certificate-manager-devel.manifest BuildRequires: cmake BuildRequires: pkgconfig(dlog) BuildRequires: pkgconfig(libsystemd-daemon) -BuildRequires: pkgconfig(security-manager) -BuildRequires: pkgconfig(libsmack) BuildRequires: pkgconfig(libtzplatform-config) BuildRequires: pkgconfig(iotivity) BuildRequires: pkgconfig(protobuf) +BuildRequires: pkgconfig(cynara-client) +BuildRequires: pkgconfig(cynara-creds-socket) +BuildRequires: pkgconfig(cynara-session) BuildRequires: boost-devel BuildRequires: openssl Summary: Device Certificate Manager -- 2.7.4 From 89dffe9d87874e045c3b15708a7e496c7709938c Mon Sep 17 00:00:00 2001 From: Dongsun Lee Date: Mon, 11 Dec 2017 16:10:14 +0900 Subject: [PATCH 15/16] change user/group from key-manager to security_fw Change-Id: I8854d29a2ce01f3ba0460492b904ed0f05228744 Signed-off-by: Dongsun Lee --- packaging/device-certificate-manager.spec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packaging/device-certificate-manager.spec b/packaging/device-certificate-manager.spec index 0689971..1d722f2 100644 --- a/packaging/device-certificate-manager.spec +++ b/packaging/device-certificate-manager.spec @@ -24,8 +24,8 @@ Requires(post): /sbin/ldconfig Requires(postun): /sbin/ldconfig %{?systemd_requires} -%global user_name key-manager -%global group_name key-manager +%global user_name security_fw +%global group_name security_fw %global service_name device-certificate-manager %global smack_domain_name System -- 2.7.4 From 126cdd48eec3612156064d68126cbb7bb5b191ea Mon Sep 17 00:00:00 2001 From: Jaroslaw Pelczar Date: Thu, 14 Dec 2017 07:30:47 +0100 Subject: [PATCH 16/16] Implement x509 certificate rewriter This class will rewrite broken x509 chains into correct order. Change-Id: I58b7a312f39443d7740fcda2bef94b089ca24090 Signed-off-by: Jaroslaw Pelczar --- dcm-daemon/CMakeLists.txt | 1 + dcm-daemon/cert_utils.cpp | 261 ++++++++++++++++++++++++++++++++++++++++++++++ dcm-daemon/cert_utils.h | 50 +++++++++ 3 files changed, 312 insertions(+) create mode 100644 dcm-daemon/cert_utils.cpp create mode 100644 dcm-daemon/cert_utils.h diff --git a/dcm-daemon/CMakeLists.txt b/dcm-daemon/CMakeLists.txt index 2c50e1c..2d8a157 100644 --- a/dcm-daemon/CMakeLists.txt +++ b/dcm-daemon/CMakeLists.txt @@ -70,6 +70,7 @@ add_executable(device-certificate-managerd abstractcryptobackendcontext.cpp cryptobackendroster.cpp dllresolver.cpp + cert_utils.cpp ${PROTO_SRCS} ${PROTO_HDRS} ${DUMMY_BACKEND_OBJECTS} diff --git a/dcm-daemon/cert_utils.cpp b/dcm-daemon/cert_utils.cpp new file mode 100644 index 0000000..2714904 --- /dev/null +++ b/dcm-daemon/cert_utils.cpp @@ -0,0 +1,261 @@ +/****************************************************************** + * + * Copyright 2017 Samsung Electronics All Rights Reserved. + * + * Author: Jaroslaw Pelczar + * + * 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 "cert_utils.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "logging.h" + +int x509_crt_rewriter::parse(const unsigned char * buffer, size_t length) +{ + BOOST_LOG_FUNCTION(); + return mbedtls_x509_crt_parse(fChain, buffer, length); +} + +/* + * Like memcmp, but case-insensitive and always returns -1 if different + */ +static int x509_memcasecmp( const void *s1, const void *s2, size_t len ) +{ + size_t i; + unsigned char diff; + const unsigned char *n1 = (const unsigned char *)s1, *n2 = (const unsigned char *)s2; + + for( i = 0; i < len; i++ ) + { + diff = n1[i] ^ n2[i]; + + if( diff == 0 ) + continue; + + if( diff == 32 && + ( ( n1[i] >= 'a' && n1[i] <= 'z' ) || + ( n1[i] >= 'A' && n1[i] <= 'Z' ) ) ) + { + continue; + } + + return( -1 ); + } + + return( 0 ); +} + +/* + * Compare two X.509 strings, case-insensitive, and allowing for some encoding + * variations (but not all). + * + * Return 0 if equal, -1 otherwise. + */ +static int x509_string_cmp( const mbedtls_x509_buf *a, const mbedtls_x509_buf *b ) +{ + if( a->tag == b->tag && + a->len == b->len && + memcmp( a->p, b->p, b->len ) == 0 ) + { + return( 0 ); + } + + if( ( a->tag == MBEDTLS_ASN1_UTF8_STRING || a->tag == MBEDTLS_ASN1_PRINTABLE_STRING ) && + ( b->tag == MBEDTLS_ASN1_UTF8_STRING || b->tag == MBEDTLS_ASN1_PRINTABLE_STRING ) && + a->len == b->len && + x509_memcasecmp( a->p, b->p, b->len ) == 0 ) + { + return( 0 ); + } + + return( -1 ); +} + +/* + * Compare two X.509 Names (aka rdnSequence). + * + * See RFC 5280 section 7.1, though we don't implement the whole algorithm: + * we sometimes return unequal when the full algorithm would return equal, + * but never the other way. (In particular, we don't do Unicode normalisation + * or space folding.) + * + * Return 0 if equal, -1 otherwise. + */ +static int x509_name_cmp( const mbedtls_x509_name *a, const mbedtls_x509_name *b ) +{ + /* Avoid recursion, it might not be optimised by the compiler */ + while( a != NULL || b != NULL ) + { + if( a == NULL || b == NULL ) + return( -1 ); + + /* type */ + if( a->oid.tag != b->oid.tag || + a->oid.len != b->oid.len || + memcmp( a->oid.p, b->oid.p, b->oid.len ) != 0 ) + { + return( -1 ); + } + + /* value */ + if( x509_string_cmp( &a->val, &b->val ) != 0 ) + return( -1 ); + + /* structure of the list of sets */ + if( a->next_merged != b->next_merged ) + return( -1 ); + + a = a->next; + b = b->next; + } + + /* a == NULL == b */ + return( 0 ); +} + +void x509_crt_rewriter::sort_chain() +{ + BOOST_LOG_FUNCTION(); + + // Only 1 certificate - don't bother + if(!fChain->next) { + fChainSize = fChain->raw.len; + fNumCerts = 1; + return; + } + + std::vector list; + std::multimap subject_of; + std::set visited; + + // Build list of all certificates + for(auto * cert = fChain ; cert ; cert = cert->next) { + list.push_back(cert); + } + + // Create graph vertices to map issuer to subject + for(size_t i = 0 ; i < list.size() ; ++i) { + mbedtls_x509_crt * issuer = nullptr; + // Find issuer - ignore ourselves as we always want root CA issuer to be nullptr + for(size_t j = 0 ; j < list.size() ; ++j) { + if(i != j && x509_name_cmp(&list[i]->issuer, &list[j]->subject) == 0) { + issuer = list[j]; + break; + } + } + + // In case there are multiple subjects for one issuer, the chain + // must be horribly broken + subject_of.emplace(issuer, list[i]); + } + + // BFS algorithm queue + std::list queue; + // Final output chain + std::list final_chain; + + // Find root certificates - they will not have any issuer + auto root_range = subject_of.equal_range(nullptr); + + // Perform BFS for each root + for(auto it = root_range.first ; it != root_range.second ; ++it) { + mbedtls_x509_crt * root_cert = it->second; + + if(visited.find(root_cert) == visited.end()) { + visited.insert(root_cert); + queue.push_back(root_cert); + + while(!queue.empty()) { + auto s = queue.front(); + // Write out chain from root back to leaf + final_chain.push_back(s); + queue.pop_front(); + + auto range = subject_of.equal_range(s); + for(auto it = range.first ; it != range.second ; ++it) { + mbedtls_x509_crt * cert = it->second; + if(visited.find(cert) == visited.end()) { + visited.insert(cert); + queue.push_back(cert); + } + } + } + } + } + + fChain = nullptr; + fChainSize = 0; + fNumCerts = list.size(); + + // Rebuild certificate linked list + for(auto it = final_chain.begin() ; it != final_chain.end() ; ++it) { + auto cert(*it); + cert->next = fChain; + fChain = cert; + fChainSize += cert->raw.len; + } +} + +#define PEM_BEGIN_CRT "-----BEGIN CERTIFICATE-----\n" +#define PEM_END_CRT "-----END CERTIFICATE-----\n" + +std::string x509_crt_rewriter::emit_pem() +{ + BOOST_LOG_FUNCTION(); + std::string buffer; + + if(fChainSize == 0) + throw std::runtime_error("State failure"); + + // Always rewrite the chain as PEM + + buffer.resize(fChainSize * 4 + fNumCerts * (sizeof(PEM_BEGIN_CRT) + sizeof(PEM_END_CRT))); + + unsigned char * out_buffer = (unsigned char *)buffer.c_str(); + size_t out_capacity = buffer.size(); + size_t total_size = 0; + size_t this_len; + + for(auto cert = fChain ; cert ; cert = cert->next) { + int error = mbedtls_pem_write_buffer(PEM_BEGIN_CRT, + PEM_END_CRT, + cert->raw.p, + cert->raw.len, + out_buffer + total_size, + out_capacity - total_size, + &this_len); + + if(error != 0) { + throw std::runtime_error("Certificate write failure"); + } + + // Account for final 0 byte + total_size += this_len - 1; + } + + buffer.resize(total_size); + buffer.push_back(0); + + return buffer; +} diff --git a/dcm-daemon/cert_utils.h b/dcm-daemon/cert_utils.h new file mode 100644 index 0000000..7a5a64f --- /dev/null +++ b/dcm-daemon/cert_utils.h @@ -0,0 +1,50 @@ +/****************************************************************** + * + * Copyright 2017 Samsung Electronics All Rights Reserved. + * + * Author: Jaroslaw Pelczar + * + * 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 DCM_DAEMON_CERT_UTILS_H_ +#define DCM_DAEMON_CERT_UTILS_H_ + +#include +#include + +struct x509_crt_rewriter { +private: + mbedtls_x509_crt * fChain; + size_t fChainSize = 0; + size_t fNumCerts = 0; + +public: + x509_crt_rewriter() : + fChain(new mbedtls_x509_crt()) + { + mbedtls_x509_crt_init(fChain); + } + + ~x509_crt_rewriter() { + mbedtls_x509_crt_free(fChain); + delete fChain; + } + + int parse(const unsigned char * buffer, size_t length); + void sort_chain(); + std::string emit_pem(); +}; + +#endif /* DCM_DAEMON_CERT_UTILS_H_ */ -- 2.7.4