PROJECT(trust-anchor)
+INCLUDE(FindPkgConfig)
+INCLUDE(GNUInstallDirs)
+
+IF(NOT CMAKE_BUILD_TYPE)
+ SET(CMAKE_BUILD_TYPE "DEBUG")
+ENDIF(NOT CMAKE_BUILD_TYPE)
+
+STRING(REGEX MATCH "([^.]*)" API_VERSION "${LIB_VERSION}")
+SET(TANCHOR_INCLUDE ${PROJECT_SOURCE_DIR}/api)
+SET(TANCHOR_SRC ${PROJECT_SOURCE_DIR}/src)
+SET(TARGET_TANCHOR_LIB ${LIB_NAME})
+
+ADD_DEFINITIONS("-DTANCHOR_USR_DIR=\"${TANCHOR_USR}\"")
+ADD_DEFINITIONS("-DTANCHOR_GLOBAL_DIR=\"${TANCHOR_GLOBAL}\"")
+ADD_DEFINITIONS("-DTANCHOR_BUNDLE=\"${TANCHOR_BUNDLE}\"")
+ADD_DEFINITIONS("-DTZ_SYS_CA_CERTS=\"${TZ_SYS_CA_CERTS}\"")
+ADD_DEFINITIONS("-DTZ_SYS_CA_BUNDLE=\"${TZ_SYS_CA_BUNDLE}\"")
+
+IF("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.7)
+ SET(CXX_STD "c++0x")
+else()
+ SET(CXX_STD "c++11")
+endif()
+
+ADD_DEFINITIONS("-fPIC")
+ADD_DEFINITIONS("-Werror")
+ADD_DEFINITIONS("-Wall")
+ADD_DEFINITIONS("-Wextra")
+ADD_DEFINITIONS("-pedantic")
+ADD_DEFINITIONS("-pedantic-errors")
+
+SET(CMAKE_C_FLAGS_PROFILING "-g -O0 -pg")
+SET(CMAKE_CXX_FLAGS_PROFILING "-g -O0 -pg -std=${CXX_STD} -fno-rtti")
+SET(CMAKE_C_FLAGS_DEBUG "-g -O0 -ggdb")
+SET(CMAKE_CXX_FLAGS_DEBUG "-g -O0 -ggdb -std=${CXX_STD} -fno-rtti")
+SET(CMAKE_C_FLAGS_RELEASE "-g -O2 -DNDEBUG")
+SET(CMAKE_CXX_FLAGS_RELEASE "-g -O2 -DNDEBUG -std=${CXX_STD} -fno-rtti")
+SET(CMAKE_C_FLAGS_CCOV "-g -O0 --coverage")
+SET(CMAKE_CXX_FLAGS_CCOV "-g -O0 --coverage -std=${CXX_STD} -fno-rtti")
+
+SET(CMAKE_SHARED_LINKER_FLAGS "-Wl,--as-needed")
+SET(CMAKE_EXE_LINKER_FLAGS "-Wl,--as-needed")
+
+IF(NOT DEFINED LIB_INSTALL_DIR)
+ SET(LIB_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}")
+ENDIF(NOT DEFINED LIB_INSTALL_DIR)
+
IF(NOT DEFINED INCLUDE_INSTALL_DIR)
SET(INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}")
ENDIF(NOT DEFINED INCLUDE_INSTALL_DIR)
-SET(TANCHOR_INCLUDE ${PROJECT_SOURCE_DIR}/api/tanchor)
-
-CONFIGURE_FILE(packaging/trust-anchor.manifest trust-anchor.manifest)
+CONFIGURE_FILE(packaging/trust-anchor.manifest.in trust-anchor.manifest @ONLY)
ADD_SUBDIRECTORY(api)
+ADD_SUBDIRECTORY(lib)
+ADD_SUBDIRECTORY(src)
# @author Sangwan kwon (sangwan.kwon@samsung.com)
#
-INSTALL(DIRECTORY ${TANCHOR_INCLUDE}
+INSTALL(DIRECTORY ${TANCHOR_INCLUDE}/tanchor
DESTINATION ${INCLUDE_INSTALL_DIR})
--- /dev/null
+# Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved
+#
+# 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
+#
+# @file CMakeLists.txt
+# @author Sangwan kwon (sangwan.kwon@samsung.com)
+# @brief Configure pkgconfig files
+#
+
+CONFIGURE_FILE(${LIB_NAME}.pc.in ${LIB_NAME}.pc @ONLY)
+
+INSTALL(FILES ${LIB_NAME}.pc
+ DESTINATION ${LIB_INSTALL_DIR}/pkgconfig)
--- /dev/null
+# Package Information for pkg-config
+
+prefix=@CMAKE_INSTALL_PREFIX@
+exec_prefix=%{prefix}
+libdir=@LIB_INSTALL_DIR@
+includedir=@INCLUDE_INSTALL_DIR@
+
+Name: @LIB_NAME@
+Description: @PACKAGE_DESCRIPTION@
+Version: @LIB_VERSION@
+Libs: -L${libdir} -l@LIB_NAME@
+Cflags: -I${includedir}/@LIB_NAME@
+++ /dev/null
-<manifest>
- <request>
- <domain name="_" />
- </request>
-</manifest>
--- /dev/null
+<manifest>
+ <request>
+ <domain name="_" />
+ </request>
+ <assign>
+ <filesystem path="@TANCHOR_BASE@" label="@SMACK_LABEL@" type="transmutable" />
+ </assign>
+</manifest>
Group: Security/Certificate Management
BuildRequires: gcc
BuildRequires: cmake
+BuildRequires: coreutils
+BuildRequires: pkgconfig(klay)
+BuildRequires: pkgconfig(openssl)
+BuildRequires: pkgconfig(libtzplatform-config)
+BuildRequires: ca-certificates
+BuildRequires: ca-certificates-devel
+Requires(post): /sbin/ldconfig
+Requires(postun): /sbin/ldconfig
+
+%global lib_name tanchor
+%global user_name security_fw
+%global group_name security_fw
+%global smack_label System
+
+%global tanchor_base %{TZ_SYS_DATA}
+%global tanchor_res %{tanchor_base}/res
+%global tanchor_usr %{tanchor_base}/usr
+%global tanchor_global %{tanchor_base}/global
+%global tanchor_bundle %{tanchor_base}/ca-bundle.pem
%description
The package provides trust-anchor which the application can assign
%files
%manifest %{name}.manifest
%license LICENSE
+%{_libdir}/lib%{lib_name}.so.0
+%{_libdir}/lib%{lib_name}.so.%{version}
+%dir %attr(-, %{user_name}, %{group_name}) %{tanchor_usr}
+%dir %attr(-, %{user_name}, %{group_name}) %{tanchor_global}
+%attr(-, %{user_name}, %{group_name}) %{tanchor_bundle}
%prep
%setup -q
%build
%{!?build_type:%define build_type "RELEASE"}
-%cmake . -DCMAKE_BUILD_TYPE=%{build_type}
+%cmake . -DCMAKE_BUILD_TYPE=%{build_type} \
+ -DLIB_NAME=%{lib_name} \
+ -DLIB_VERSION=%{version} \
+ -DUSER_NAME=%{user_name} \
+ -DGROUP_NAME=%{group_name} \
+ -DSMACK_LABEL=%{smack_label} \
+ -DTANCHOR_BASE=%{tanchor_base} \
+ -DTANCHOR_RES=%{tanchor_res} \
+ -DTANCHOR_USR=%{tanchor_usr} \
+ -DTANCHOR_GLOBAL=%{tanchor_global} \
+ -DTANCHOR_BUNDLE=%{tanchor_bundle} \
+ -DTZ_SYS_CA_CERTS=%{TZ_SYS_CA_CERTS} \
+ -DTZ_SYS_CA_BUNDLE=%{TZ_SYS_CA_BUNDLE}
make %{?_smp_mflags}
%install
%make_install
+mkdir -p %{buildroot}%{tanchor_res}
+mkdir -p %{buildroot}%{tanchor_usr}
+mkdir -p %{buildroot}%{tanchor_global}
+
+touch %{buildroot}%{tanchor_bundle}
+
+%post -p /sbin/ldconfig
+
+%postun -p /sbin/ldconfig
+
## Devel Package ###############################################################
%package devel
Summary: Trust Anchor API (development files)
The package provides Trust Anchor API development files.
%files devel
-%{_includedir}/tanchor/error.h
-%{_includedir}/tanchor/trust-anchor.h
-%{_includedir}/tanchor/trust-anchor.hxx
+%{_includedir}/%{lib_name}/error.h
+%{_includedir}/%{lib_name}/trust-anchor.h
+%{_includedir}/%{lib_name}/trust-anchor.hxx
+%{_libdir}/lib%{lib_name}.so
+%{_libdir}/pkgconfig/%{lib_name}.pc
--- /dev/null
+# Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved
+#
+# 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
+#
+# @file CMakeLists.txt
+# @author Sangwan Kwon (sangwan.kwon@samsung.com)
+# @breif Make trust anchor library
+#
+PKG_CHECK_MODULES(${TARGET_TANCHOR_LIB}_DEP REQUIRED klay
+ openssl)
+
+SET(${TARGET_TANCHOR_LIB}_SRCS ${TANCHOR_SRC}/init-lib.cpp
+ ${TANCHOR_SRC}/exception.cpp
+ ${TANCHOR_SRC}/api.cpp
+ ${TANCHOR_SRC}/certificate.cpp
+ ${TANCHOR_SRC}/trust-anchor.cpp)
+
+INCLUDE_DIRECTORIES(SYSTEM ${TANCHOR_INCLUDE}
+ ${TANCHOR_SRC}
+ ${${TARGET_TANCHOR_LIB}_DEP_INCLUDE_DIRS})
+
+ADD_LIBRARY(${TARGET_TANCHOR_LIB} SHARED ${${TARGET_TANCHOR_LIB}_SRCS})
+
+# TODO(sangwan.kwon) visibility needed to be hidden
+SET_TARGET_PROPERTIES(${TARGET_TANCHOR_LIB}
+ PROPERTIES COMPILE_FLAGS "-D_GNU_SOURCE -fPIC -fvisibility=default"
+ SOVERSION ${API_VERSION}
+ VERSION ${LIB_VERSION}
+)
+
+TARGET_LINK_LIBRARIES(${TARGET_TANCHOR_LIB}
+ ${${TARGET_TANCHOR_LIB}_DEP_LIBRARIES}
+)
+
+INSTALL(TARGETS ${TARGET_TANCHOR_LIB}
+ DESTINATION ${LIB_INSTALL_DIR})
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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
+ */
+/*
+ * @file api.cpp
+ * @author Sangwan Kwon (sangwan.kwon@samsung.com)
+ * @version 0.1
+ * @brief Implementation of trust anchor CAPI
+ */
+#include "tanchor/trust-anchor.h"
+#include "tanchor/trust-anchor.hxx"
+
+using namespace tanchor;
+
+int trust_anchor_global_install(const char *package_id,
+ const char *app_certificates_path,
+ bool with_system_certificates)
+{
+ TrustAnchor ta(package_id, app_certificates_path);
+ return ta.install(with_system_certificates);
+}
+
+int trust_anchor_usr_install(const char *package_id,
+ const char *app_certificates_path,
+ uid_t uid,
+ bool with_system_certificates)
+{
+ TrustAnchor ta(package_id, app_certificates_path, uid);
+ return ta.install(with_system_certificates);
+}
+
+int trust_anchor_global_launch(const char *package_id,
+ const char *app_certificates_path,
+ bool with_system_certificates)
+{
+ TrustAnchor ta(package_id, app_certificates_path);
+ return ta.launch(with_system_certificates);
+}
+
+int trust_anchor_usr_launch(const char *package_id,
+ const char *app_certificates_path,
+ uid_t uid,
+ bool with_system_certificates)
+{
+ TrustAnchor ta(package_id, app_certificates_path, uid);
+ return ta.launch(with_system_certificates);
+}
+
+int trust_anchor_global_uninstall(const char *package_id,
+ const char *app_certificates_path)
+{
+ TrustAnchor ta(package_id, app_certificates_path);
+ return ta.uninstall();
+}
+
+int trust_anchor_usr_uninstall(const char *package_id,
+ const char *app_certificates_path,
+ uid_t uid)
+{
+ TrustAnchor ta(package_id, app_certificates_path, uid);
+ return ta.uninstall();
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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
+ */
+/*
+ * @file certificate.cpp
+ * @author Sangwan Kwon (sangwan.kwon@samsung.com)
+ * @version 0.1
+ * @brief
+ */
+#include "certificate.hxx"
+
+#include <cstdio>
+#include <vector>
+#include <stdexcept>
+
+#include <openssl/pem.h>
+
+namespace tanchor {
+
+namespace {
+
+using X509Ptr = std::unique_ptr<X509, decltype(&::X509_free)>;
+
+const std::string START_CERT = "-----BEGIN CERTIFICATE-----";
+const std::string END_CERT = "-----END CERTIFICATE-----";
+const std::string START_TRUSTED = "-----BEGIN TRUSTED CERTIFICATE-----";
+const std::string END_TRUSTED = "-----END TRUSTED CERTIFICATE-----";
+
+const int HASH_LENGTH = 8;
+
+} // namespace anonymous
+
+Certificate::Certificate(const std::string &path) :
+ m_fp(FilePtr(fopen(path.c_str(), "rb"), ::fclose))
+{
+ if (this->m_fp == nullptr)
+ throw std::invalid_argument("Faild to open certificate.");
+}
+
+std::string Certificate::getSubjectNameHash() const
+{
+ X509Ptr x509(::PEM_read_X509(this->m_fp.get(), NULL, NULL, NULL),
+ ::X509_free);
+ if (x509 == nullptr) {
+ ::rewind(this->m_fp.get());
+ x509 = X509Ptr(::PEM_read_X509_AUX(this->m_fp.get(), NULL, NULL, NULL),
+ ::X509_free);
+ }
+
+ if (x509 == nullptr)
+ throw std::logic_error("Failed to read certificate.");
+
+ std::vector<char> buf(HASH_LENGTH + 1);
+ snprintf(buf.data(), buf.size(),
+ "%08lx", ::X509_subject_name_hash(x509.get()));
+
+ return std::string(buf.data(), HASH_LENGTH);
+}
+
+std::string Certificate::getCertificateData() const
+{
+ std::fseek(this->m_fp.get(), 0L, SEEK_END);
+ unsigned int fsize = std::ftell(this->m_fp.get());
+ std::rewind(this->m_fp.get());
+
+ std::string content(fsize, 0);
+ if (fsize != std::fread(static_cast<void*>(&content[0]), 1, fsize,
+ this->m_fp.get()))
+ throw std::logic_error("Failed to read certificate from fp.");
+
+ return this->parseData(content);
+}
+
+std::string Certificate::parseData(const std::string &data) const
+{
+ if (data.empty())
+ throw std::logic_error("There is no data to parse.");
+
+ size_t from = data.find(START_CERT);
+ size_t to = data.find(END_CERT);
+ size_t tailLen = END_CERT.length();
+
+ if (from == std::string::npos || to == std::string::npos || from > to) {
+ from = data.find(START_TRUSTED);
+ to = data.find(END_TRUSTED);
+ tailLen = END_TRUSTED.length();
+ }
+
+ if (from == std::string::npos || to == std::string::npos || from > to)
+ throw std::logic_error("Failed to parse certificate.");
+
+ return std::string(data, from, to - from + tailLen);
+}
+
+} // namespace tanchor
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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
+ */
+/*
+ * @file certificate.hxx
+ * @author Sangwan Kwon (sangwan.kwon@samsung.com)
+ * @version 0.1
+ * @brief
+ */
+#pragma once
+
+#include <string>
+#include <memory>
+#include <cstdio>
+
+namespace tanchor {
+
+using FilePtr = std::unique_ptr<FILE, decltype(&::fclose)>;
+
+class Certificate {
+public:
+ explicit Certificate(const std::string &path);
+ virtual ~Certificate(void) = default;
+
+ Certificate(const Certificate &) = delete;
+ Certificate(Certificate &&) = delete;
+ Certificate &operator=(const Certificate &) = delete;
+ Certificate &operator=(Certificate &&) = delete;
+
+ std::string getSubjectNameHash() const;
+ std::string getCertificateData() const;
+
+private:
+ std::string parseData(const std::string &data) const;
+
+ FilePtr m_fp;
+};
+
+} // namespace tanchor
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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
+ */
+/*
+ * @file exception.cpp
+ * @author Sangwan Kwon (sangwan.kwon@samsung.com)
+ * @version 0.1
+ * @brief Exception guard and custom exceptions
+ */
+#include "exception.hxx"
+
+#include <exception>
+
+#include <klay/exception.h>
+#include <klay/audit/logger.h>
+
+namespace tanchor {
+
+int exceptionGuard(const std::function<int()> &func)
+{
+ // TODO add custom error code
+ try {
+ return func();
+ } catch (runtime::Exception &e) {
+ ERROR(e.what());
+ return -1;
+ } catch (const std::invalid_argument &e) {
+ ERROR("Invalid argument: " << e.what());
+ return -1;
+ } catch (const std::exception &e) {
+ ERROR(e.what());
+ return -1;
+ } catch (...) {
+ ERROR("Unknown exception occurred.");
+ return -1;
+ }
+}
+
+} // namespace tanchor
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+/*
+ * @file exception.h
+ * @author Sangwan Kwon (sangwan.kwon@samsung.com)
+ * @version 0.1
+ * @brief Exception guard and custom exceptions
+ */
+#pragma once
+
+#include <functional>
+
+#define EXCEPTION_GUARD_START return tanchor::exceptionGuard([&]() {
+#define EXCEPTION_GUARD_END });
+
+namespace tanchor {
+
+int exceptionGuard(const std::function<int()> &);
+
+} // namespace tanchor
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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
+ */
+/*
+ * @file init-lib.cpp
+ * @author Sangwan Kwon (sangwan.kwon@samsung.com)
+ * @version 0.1
+ * @brief Init global configuration for library
+ */
+
+#include <klay/audit/logger.h>
+#include <klay/audit/dlog-sink.h>
+
+#include <memory>
+
+namespace tanchor {
+namespace {
+
+class InitLib {
+public:
+ InitLib()
+ {
+ audit::Logger::setBackend(new audit::DlogLogSink());
+ audit::Logger::setTag("CERT_SVC");
+ };
+ ~InitLib() = default;
+};
+
+static std::unique_ptr<InitLib> init(new(std::nothrow)(InitLib));
+
+} // namespace anonymous
+} // namespace tanchor
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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
+ */
+/*
+ * @file trust-anchor.cpp
+ * @author Sangwan Kwon (sangwan.kwon@samsung.com)
+ * @version 0.1
+ * @brief Implementation of trust anchor
+ */
+#include "tanchor/trust-anchor.hxx"
+
+#include <climits>
+#include <cerrno>
+#include <ctime>
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+
+#include <set>
+#include <vector>
+
+#include <klay/filesystem.h>
+#include <klay/audit/logger.h>
+
+#include "certificate.hxx"
+#include "exception.hxx"
+
+namespace tanchor {
+
+namespace {
+
+const std::string BASE_USR_PATH(TANCHOR_USR_DIR);
+const std::string BASE_GLOBAL_PATH(TANCHOR_GLOBAL_DIR);
+const std::string TANCHOR_BUNDLE_PATH(TANCHOR_BUNDLE);
+const std::string SYS_CERTS_PATH(TZ_SYS_CA_CERTS);
+const std::string SYS_BUNDLE_PATH(TZ_SYS_CA_BUNDLE);
+const std::string MOUNT_POINT_CERTS(TZ_SYS_CA_CERTS);
+const std::string MOUNT_POINT_BUNDLE(TZ_SYS_CA_BUNDLE);
+const std::string BUNDLE_NAME("ca-bundle.pem");
+const std::string NEW_LINE("\n");
+
+} // namespace anonymous
+
+class TrustAnchor::Impl {
+public:
+ explicit Impl(const std::string &packageId,
+ const std::string &certsDir,
+ uid_t uid) noexcept;
+ explicit Impl(const std::string &packageId,
+ const std::string &certsDir) noexcept;
+ virtual ~Impl(void) = default;
+
+ int install(bool withSystemCerts) noexcept;
+ int uninstall(bool isRollback = false) noexcept;
+ int launch(bool withSystemCerts);
+
+private:
+ void preInstall(void) const;
+ void linkTo(const std::string &src, const std::string &dst) const;
+ void makeCustomBundle(bool withSystemCerts);
+ std::string readLink(const std::string &path) const;
+ std::string getUniqueHashName(const std::string &hashName) const;
+ bool isSystemCertsModified(void) const;
+
+ std::string m_packageId;
+ std::string m_appCertsPath;
+ uid_t m_uid;
+
+ std::string m_customBasePath;
+ std::string m_customCertsPath;
+ std::string m_customBundlePath;
+
+ std::set<std::string> m_customCertNameSet;
+ std::vector<std::string> m_customCertsData;
+};
+
+TrustAnchor::Impl::Impl(const std::string &packageId,
+ const std::string &certsDir,
+ uid_t uid) noexcept :
+ m_packageId(packageId),
+ m_appCertsPath(certsDir),
+ m_uid(uid),
+ m_customBasePath(BASE_USR_PATH + "/" +
+ std::to_string(static_cast<int>(uid)) + "/" +
+ packageId),
+ m_customCertsPath(m_customBasePath + "/certs"),
+ m_customBundlePath(m_customBasePath + "/bundle"),
+ m_customCertNameSet(),
+ m_customCertsData() {}
+
+TrustAnchor::Impl::Impl(const std::string &packageId,
+ const std::string &certsDir) noexcept :
+ m_packageId(packageId),
+ m_appCertsPath(certsDir),
+ m_uid(-1),
+ m_customBasePath(BASE_GLOBAL_PATH + "/" + packageId),
+ m_customCertsPath(m_customBasePath + "/certs"),
+ m_customBundlePath(m_customBasePath + "/bundle"),
+ m_customCertNameSet(),
+ m_customCertsData() {}
+
+std::string TrustAnchor::Impl::readLink(const std::string &path) const
+{
+ std::vector<char> buf(PATH_MAX);
+ ssize_t count = readlink(path.c_str(), buf.data(), buf.size());
+ return std::string(buf.data(), (count > 0) ? count : 0);
+}
+
+void TrustAnchor::Impl::linkTo(const std::string &src,
+ const std::string &dst) const
+{
+ errno = 0;
+ int ret = ::symlink(src.c_str(), dst.c_str());
+ if (ret != 0)
+ throw std::logic_error("Fail to link " + src + " -> " + dst +
+ "[" + std::to_string(errno) + "]");
+}
+
+void TrustAnchor::Impl::preInstall(void) const
+{
+ runtime::File customBaseDir(this->m_customBasePath);
+ if (customBaseDir.exists()) {
+ WARN("App custom directory is already exist. remove it!");
+ customBaseDir.remove(true);
+ }
+ customBaseDir.makeDirectory(true);
+
+ runtime::File customCertsDir(this->m_customCertsPath);
+ customCertsDir.makeDirectory();
+
+ runtime::File customBundleDir(this->m_customBundlePath);
+ customBundleDir.makeDirectory();
+
+ runtime::File appCertsDir(this->m_appCertsPath);
+ if (!appCertsDir.exists() || !appCertsDir.isDirectory())
+ throw std::invalid_argument("App custom certs path is wrong. : " +
+ m_appCertsPath);
+
+ DEBUG("Success to pre-install stage.");
+}
+
+int TrustAnchor::Impl::install(bool withSystemCerts) noexcept
+{
+ EXCEPTION_GUARD_START
+
+ this->preInstall();
+
+ if (withSystemCerts) {
+ // link system certificates to the custom directory
+ runtime::DirectoryIterator iter(SYS_CERTS_PATH), end;
+ while (iter != end) {
+ linkTo(readLink(iter->getPath()),
+ this->m_customCertsPath + "/" + iter->getName());
+ this->m_customCertNameSet.emplace(iter->getName());
+ ++iter;
+ }
+ DEBUG("Success to migrate system certificates.");
+ }
+
+ // link app certificates to the custom directory as subjectNameHash
+ runtime::DirectoryIterator iter(this->m_appCertsPath), end;
+ while (iter != end) {
+ Certificate cert(iter->getPath());
+ std::string hashName = this->getUniqueHashName(cert.getSubjectNameHash());
+ linkTo(iter->getPath(),
+ this->m_customCertsPath + "/" + hashName);
+ this->m_customCertNameSet.emplace(std::move(hashName));
+
+ this->m_customCertsData.emplace_back(cert.getCertificateData());
+ ++iter;
+ }
+
+ this->makeCustomBundle(withSystemCerts);
+
+ INFO("Success to install[" << this->m_packageId <<
+ "] to " << this->m_customBasePath);
+ return 0;
+
+ EXCEPTION_GUARD_END
+}
+
+int TrustAnchor::Impl::uninstall(bool isRollback) noexcept
+{
+ EXCEPTION_GUARD_START
+
+ runtime::File customBaseDir(this->m_customBasePath);
+ if (!customBaseDir.exists() && !isRollback)
+ throw std::invalid_argument("There is no installed anchor previous.");
+
+ if (customBaseDir.exists())
+ customBaseDir.remove(true);
+
+ INFO("Success to uninstall. : " << this->m_packageId);
+ return 0;
+
+ EXCEPTION_GUARD_END
+}
+
+bool TrustAnchor::Impl::isSystemCertsModified(void) const
+{
+ struct stat systemAttr, customAttr;
+
+ stat(SYS_BUNDLE_PATH.c_str(), &systemAttr);
+ DEBUG("System bundle mtime : " << ::ctime(&systemAttr.st_mtime));
+
+ auto customBundle = this->m_customBundlePath + "/" + BUNDLE_NAME;
+ stat(customBundle.c_str(), &customAttr);
+ DEBUG("Custom bundle mtime : " << ::ctime(&customAttr.st_mtime));
+
+ return systemAttr.st_mtime > customAttr.st_mtime;
+}
+
+int TrustAnchor::Impl::launch(bool withSystemCerts)
+{
+ EXCEPTION_GUARD_START
+
+ if (withSystemCerts && this->isSystemCertsModified())
+ this->makeCustomBundle(true);
+
+ errno = 0;
+ // disassociate from the parent namespace
+ if (::unshare(CLONE_NEWNS))
+ throw std::logic_error("Failed to unshare namespace > " +
+ std::to_string(errno));
+
+ // convert it to a slave for preventing propagation
+ if (::mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL))
+ throw std::logic_error("Failed to disconnect root fs.");
+
+ if (::mount(this->m_customCertsPath.c_str(),
+ MOUNT_POINT_CERTS.c_str(),
+ NULL,
+ MS_BIND,
+ NULL))
+ throw std::logic_error("Failed to mount certs.");
+
+ auto bundle = this->m_customBundlePath + "/" + BUNDLE_NAME;
+ if (::mount(bundle.c_str(),
+ MOUNT_POINT_BUNDLE.c_str(),
+ NULL,
+ MS_BIND,
+ NULL))
+ throw std::logic_error("Failed to mount bundle.");
+
+ INFO("Success to launch. : " << this->m_packageId);
+ return 0;
+
+ EXCEPTION_GUARD_END
+}
+
+std::string TrustAnchor::Impl::getUniqueHashName(
+ const std::string &hashName) const
+{
+ int sameFileNameCnt = 0;
+ std::string uniqueName;
+ do {
+ uniqueName = hashName + "." + std::to_string(sameFileNameCnt++);
+ } while (this->m_customCertNameSet.find(uniqueName) !=
+ this->m_customCertNameSet.end());
+
+ return uniqueName;
+}
+
+void TrustAnchor::Impl::makeCustomBundle(bool withSystemCerts)
+{
+ runtime::File customBundle(this->m_customBundlePath + "/" +
+ BUNDLE_NAME);
+ if (customBundle.exists()) {
+ WARN("App custom bundle is already exist. remove it!");
+ customBundle.remove();
+ }
+
+ DEBUG("Start to migrate previous bundle.");
+ if (withSystemCerts) {
+ runtime::File sysBundle(SYS_BUNDLE_PATH);
+ if (!sysBundle.exists())
+ throw std::logic_error("There is no system bundle file.");
+ sysBundle.copyTo(this->m_customBundlePath);
+ } else {
+ runtime::File tanchorBundle(TANCHOR_BUNDLE_PATH);
+ if (!tanchorBundle.exists())
+ throw std::logic_error("There is no tanchor bundle file.");
+ tanchorBundle.copyTo(this->m_customBundlePath);
+ }
+ DEBUG("Finish migrating previous bundle.");
+
+ if (this->m_customCertsData.empty()) {
+ DEBUG("System certificates is changed after TrustAnchor installation.");
+ runtime::DirectoryIterator iter(this->m_appCertsPath), end;
+ while (iter != end) {
+ Certificate cert(iter->getPath());
+ this->m_customCertsData.emplace_back(cert.getCertificateData());
+ ++iter;
+ }
+ }
+
+ DEBUG("Start to add app's certificate to bundle.");
+ customBundle.open(O_RDWR | O_APPEND);
+ for (const auto &cert : this->m_customCertsData) {
+ customBundle.write(cert.c_str(), cert.length());
+ customBundle.write(NEW_LINE.c_str(), NEW_LINE.length());
+ }
+
+ INFO("Success to make app custom bundle.");
+}
+
+TrustAnchor::TrustAnchor(const std::string &packageId,
+ const std::string &certsDir,
+ uid_t uid) noexcept :
+ m_pImpl(new Impl(packageId, certsDir, uid)) {}
+
+TrustAnchor::TrustAnchor(const std::string &packageId,
+ const std::string &certsDir) noexcept :
+ m_pImpl(new Impl(packageId, certsDir)) {}
+
+TrustAnchor::~TrustAnchor(void) = default;
+
+int TrustAnchor::install(bool withSystemCerts) noexcept
+{
+ if (this->m_pImpl == nullptr)
+ return -1;
+
+ int ret = this->m_pImpl->install(withSystemCerts);
+
+ if (ret != 0) {
+ ERROR("Failed to intall ACTA. Remove custom directory for rollback.");
+ this->m_pImpl->uninstall(true);
+ }
+
+ return ret;
+}
+
+int TrustAnchor::uninstall(void) noexcept
+{
+ if (this->m_pImpl == nullptr)
+ return -1;
+
+ return this->m_pImpl->uninstall();
+}
+
+int TrustAnchor::launch(bool withSystemCerts) noexcept
+{
+ if (this->m_pImpl == nullptr)
+ return -1;
+
+ return this->m_pImpl->launch(withSystemCerts);
+}
+
+} // namespace tanchor