Add making bundle logic on install stage 36/115036/4
authorsangwan.kwon <sangwan.kwon@samsung.com>
Wed, 15 Feb 2017 11:16:45 +0000 (20:16 +0900)
committersangwan.kwon <sangwan.kwon@samsung.com>
Thu, 16 Feb 2017 08:13:03 +0000 (17:13 +0900)
* Refine Certificate class as non-static for reducing file I/O.

Change-Id: I63832eb9fc595715d828d3549db708bb2fab7e3f
Signed-off-by: sangwan.kwon <sangwan.kwon@samsung.com>
src/transec/AppCustomTrustAnchor.cpp
src/transec/Certificate.cpp
src/transec/Certificate.h
tests/transec/test-certificate.cpp
tests/transec/test-resource.h

index 4e32afe..d71a945 100644 (file)
 
 #include <unistd.h>
 #include <cerrno>
+#include <fcntl.h>
 
 #include <set>
+#include <vector>
 
 #include <klay/filesystem.h>
 #include <klay/audit/logger.h>
 
 namespace transec {
 
+namespace {
+
+const std::string BASE_PATH(CERTSVC_TRANSEC_DIR);
+const std::string BASE_CERTS_PATH(BASE_PATH + "/certs");
+const std::string BASE_BUNDLE_PATH(BASE_PATH + "/bundle");
+const std::string SYS_CERTS_PATH(TZ_SYS_CA_CERTS);
+const std::string SYS_BUNDLE_PATH(TZ_SYS_CA_BUNDLE);
+const std::string BUNDLE_NAME("ca-bundle.pem");
+const std::string NEW_LINE("\n");
+
+} // namespace anonymous
+
 class AppCustomTrustAnchor::Impl {
 public:
        explicit Impl(const std::string &packageId,
@@ -44,21 +58,21 @@ public:
        virtual ~Impl(void) = default;
 
        int install(bool withSystemCerts) noexcept;
-       int uninstall(void) noexcept;
+       int uninstall(bool isRollback = false) noexcept;
        int launch(bool withSystemCerts);
 
 private:
-       void linkTo(const std::string &src, const std::string &dst);
-       void makeCustomBundle(void);
-       std::string getHashName(const std::string &filePath);
+       void preInstall(void) const;
+       void linkTo(const std::string &src, const std::string &dst) const;
+       void makeCustomBundle(const std::vector<std::string> &certs) const;
+       std::string getUniqueHashName(const std::string &hashName) const;
 
        std::string m_packageId;
        std::string m_appCertsPath;
        uid_t m_uid;
 
-       std::string m_basePath;
-       std::string m_sysCertsPath;
        std::string m_customCertsPath;
+       std::string m_customBundlePath;
 
        std::set<std::string> m_customCertNameSet;
 };
@@ -69,22 +83,25 @@ AppCustomTrustAnchor::Impl::Impl(const std::string &packageId,
        m_packageId(packageId),
        m_appCertsPath(certsDir),
        m_uid(uid),
-       m_basePath(std::string(CERTSVC_TRANSEC_DIR)),
-       m_sysCertsPath(std::string(TZ_SYS_CA_CERTS)),
-       m_customCertsPath(this->m_basePath + "/" +
+       m_customCertsPath(BASE_CERTS_PATH + "/usr/" +
                                          std::to_string(static_cast<int>(uid)) + "/" +
-                                         packageId) {}
+                                         packageId),
+       m_customBundlePath(BASE_BUNDLE_PATH + "/usr/" +
+                                          std::to_string(static_cast<int>(uid)) + "/" +
+                                          packageId),
+       m_customCertNameSet() {}
 
 AppCustomTrustAnchor::Impl::Impl(const std::string &packageId,
                                                                 const std::string &certsDir) noexcept :
        m_packageId(packageId),
        m_appCertsPath(certsDir),
-       m_basePath(std::string(CERTSVC_TRANSEC_DIR)),
-       m_sysCertsPath(std::string(TZ_SYS_CA_CERTS)),
-       m_customCertsPath(this->m_basePath + "/" + packageId) {}
+       m_uid(-1),
+       m_customCertsPath(BASE_CERTS_PATH + "/global/" + packageId),
+       m_customBundlePath(BASE_BUNDLE_PATH + "/global/" + packageId),
+       m_customCertNameSet() {}
 
 void AppCustomTrustAnchor::Impl::linkTo(const std::string &src,
-                                                                               const std::string &dst)
+                                                                               const std::string &dst) const
 {
        errno = 0;
        int ret = ::symlink(src.c_str(), dst.c_str());
@@ -93,45 +110,70 @@ void AppCustomTrustAnchor::Impl::linkTo(const std::string &src,
                                                           "[" + std::to_string(errno) + "]");
 }
 
+void AppCustomTrustAnchor::Impl::preInstall(void) const
+{
+       runtime::File customCertsDir(this->m_customCertsPath);
+       if (customCertsDir.exists()) {
+               WARN("App custom certs directory is already exist. remove it!");
+               customCertsDir.remove(true);
+       }
+       customCertsDir.makeDirectory(true);
+
+       runtime::File customBundleDir(this->m_customBundlePath);
+       if (customBundleDir.exists()) {
+               WARN("App custom bundle directory is already exist. remove it!");
+               customBundleDir.remove(true);
+       }
+       customBundleDir.makeDirectory(true);
+
+       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 AppCustomTrustAnchor::Impl::install(bool withSystemCerts) noexcept
 {
        EXCEPTION_GUARD_START
 
-       // make the package's custom directory
-       runtime::File customDir(this->m_customCertsPath);
-       if (customDir.exists()) {
-               WARN("App custom certs directory is already exist. remove it!");
-               customDir.remove(true);
-       }
-       customDir.makeDirectory(true);
+       this->preInstall();
 
-       // link system certificates to the custom directory
        if (withSystemCerts) {
-               runtime::DirectoryIterator iter(this->m_sysCertsPath), end;
+               // link system certificates to the custom directory
+               runtime::DirectoryIterator iter(SYS_CERTS_PATH), end;
                while (iter != end) {
                        linkTo(iter->getPath(),
                                   this->m_customCertsPath + "/" + iter->getName());
                        this->m_customCertNameSet.emplace(iter->getName());
                        ++iter;
                }
-       }
 
-       // link app certificates to the custom directory
-       runtime::File appCertsDir(this->m_appCertsPath);
-       if (!appCertsDir.exists() || !appCertsDir.isDirectory())
-               throw std::invalid_argument("App custom certs path is wrong. : " +
-                                                                       m_appCertsPath);
+               // copy system bundle to the custom path
+               runtime::File sysBundle(SYS_BUNDLE_PATH);
+               if (!sysBundle.exists())
+                       throw std::logic_error("There is no system bundle file.");
+               sysBundle.copyTo(this->m_customBundlePath);
 
+               DEBUG("Success to migrate system certificates and bundle.");
+       }
+
+       // link app certificates to the custom directory as subjectNameHash
        runtime::DirectoryIterator iter(this->m_appCertsPath), end;
+       std::vector<std::string> customCertData;
        while (iter != end) {
-               std::string hashName = this->getHashName(iter->getPath());
+               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));
+
+               customCertData.emplace_back(cert.getCertificateData());
                ++iter;
        }
 
-       this->makeCustomBundle();
+       this->makeCustomBundle(customCertData);
 
        INFO("Success to install[" << this->m_packageId <<
                 "] to " << this->m_customCertsPath);
@@ -140,15 +182,23 @@ int AppCustomTrustAnchor::Impl::install(bool withSystemCerts) noexcept
        EXCEPTION_GUARD_END
 }
 
-int AppCustomTrustAnchor::Impl::uninstall(void) noexcept
+int AppCustomTrustAnchor::Impl::uninstall(bool isRollback) noexcept
 {
        EXCEPTION_GUARD_START
 
-       runtime::File customDir(this->m_customCertsPath);
-       if (!customDir.exists())
-               throw std::invalid_argument("There is no installed directory previous.");
+       runtime::File customCertsDir(this->m_customCertsPath);
+       if (!customCertsDir.exists() && !isRollback)
+               throw std::invalid_argument("There is no installed certs previous.");
+
+       runtime::File customBundleDir(this->m_customBundlePath);
+       if (!customBundleDir.exists() && !isRollback)
+               throw std::invalid_argument("There is no installed bundle previous.");
+
+       if (customCertsDir.exists())
+               customCertsDir.remove(true);
 
-       customDir.remove(true);
+       if (!customBundleDir.exists())
+               customBundleDir.remove(true);
 
        INFO("Success to uninstall. : " << this->m_packageId);
        return 0;
@@ -164,21 +214,34 @@ int AppCustomTrustAnchor::Impl::launch(bool withSystemCerts)
                return -1;
 }
 
-std::string AppCustomTrustAnchor::Impl::getHashName(const std::string &filePath)
+std::string AppCustomTrustAnchor::Impl::getUniqueHashName(
+       const std::string &hashName) const
 {
-       auto hashName = Certificate::getSubjectNameHash(filePath);
        int sameFileNameCnt = 0;
        std::string uniqueName;
        do {
                uniqueName = hashName + "." + std::to_string(sameFileNameCnt++);
-       } while (this->m_customCertNameSet.find(uniqueName) != this->m_customCertNameSet.end());
+       } while (this->m_customCertNameSet.find(uniqueName) !=
+                        this->m_customCertNameSet.end());
 
        return uniqueName;
 }
 
-void AppCustomTrustAnchor::Impl::makeCustomBundle(void)
+void AppCustomTrustAnchor::Impl::makeCustomBundle(
+       const std::vector<std::string> &certs) const
 {
-       // TODO (openssl) make custom bundle file
+       runtime::File customBundle(this->m_customBundlePath + "/" +
+                                                          BUNDLE_NAME);
+       if (!customBundle.exists()) {
+               DEBUG("Make bundle only used by app certificates.");
+               customBundle.create(755);
+       }
+
+       customBundle.open(O_RDWR | O_APPEND);
+       for (const auto &cert : certs) {
+               customBundle.write(cert.c_str(), cert.length());
+               customBundle.write(NEW_LINE.c_str(), NEW_LINE.length());
+       }
 }
 
 AppCustomTrustAnchor::AppCustomTrustAnchor(const std::string &packageId,
@@ -201,7 +264,7 @@ int AppCustomTrustAnchor::install(bool withSystemCerts) noexcept
 
        if (ret != 0) {
                ERROR("Failed to intall ACTA. Remove custom directory for rollback.");
-               this->uninstall();
+               this->m_pImpl->uninstall(true);
        }
 
        return ret;
index 793575b..7a37785 100644 (file)
@@ -23,7 +23,6 @@
 
 #include <cstdio>
 #include <vector>
-#include <memory>
 #include <stdexcept>
 
 #include <openssl/pem.h>
@@ -32,23 +31,31 @@ namespace transec {
 
 namespace {
 
-using FilePtr = std::unique_ptr<FILE, decltype(&::fclose)>;
 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
 
-std::string Certificate::getSubjectNameHash(const std::string &path)
+Certificate::Certificate(const std::string &path) :
+       m_fp(FilePtr(fopen(path.c_str(), "rb"), ::fclose))
 {
-       FilePtr fp(fopen(path.c_str(), "r"), ::fclose);
-       if (fp == nullptr)
+       if (this->m_fp == nullptr)
                throw std::invalid_argument("Faild to open certificate.");
+}
 
-       X509Ptr x509(::PEM_read_X509(fp.get(), NULL, NULL, NULL), ::X509_free);
+std::string Certificate::getSubjectNameHash() const
+{
+       X509Ptr x509(::PEM_read_X509(this->m_fp.get(), NULL, NULL, NULL),
+                                ::X509_free);
        if (x509 == nullptr) {
-               ::rewind(fp.get());
-               x509 = X509Ptr(::PEM_read_X509_AUX(fp.get(), NULL, NULL, NULL),
+               ::rewind(this->m_fp.get());
+               x509 = X509Ptr(::PEM_read_X509_AUX(this->m_fp.get(), NULL, NULL, NULL),
                                           ::X509_free);
        }
 
@@ -62,4 +69,39 @@ std::string Certificate::getSubjectNameHash(const std::string &path)
        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 transec
index 45d0172..fe216db 100644 (file)
 #pragma once
 
 #include <string>
+#include <memory>
+#include <cstdio>
 
 namespace transec {
 
+using FilePtr = std::unique_ptr<FILE, decltype(&::fclose)>;
+
 class Certificate {
 public:
-       static std::string getSubjectNameHash(const std::string &path);
+       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 transec
index ac757b0..dc94e1c 100644 (file)
@@ -35,7 +35,8 @@ using namespace transec;
 RUNNER_TEST(T0601_GET_SUBJECT_NAME_HASH)
 {
        try {
-               auto hash = Certificate::getSubjectNameHash(TEST_PEM_PATH);
+               Certificate certificate(TEST_PEM_PATH);
+               auto hash = certificate.getSubjectNameHash();
                RUNNER_ASSERT_MSG(hash.compare(TEST_PEM_HASH) == 0,
                                                  "Failed to get proper hash.");
        } catch (const std::exception &e) {
@@ -44,3 +45,17 @@ RUNNER_TEST(T0601_GET_SUBJECT_NAME_HASH)
                std::cout << "Unknown exception occured." << std::endl;
        }
 }
+
+RUNNER_TEST(T0602_GET_CERTIFICATE_DATA)
+{
+       try {
+               Certificate certificate(TEST_PEM_PATH);
+               auto data = certificate.getCertificateData();
+               RUNNER_ASSERT_MSG(data.compare(TEST_PEM_DATA) == 0,
+                                                 "Failed to get proper certificate data.");
+       } catch (const std::exception &e) {
+               std::cout << "std::exception occured." << e.what() << std::endl;
+       } catch (...) {
+               std::cout << "Unknown exception occured." << std::endl;
+       }
+}
index e40fba4..5531f60 100644 (file)
 
 #define TEST_PEM_PATH APP_CERTS_DIR "/02265526.0"
 #define TEST_PEM_HASH "02265526"
+
+#define TEST_PEM_DATA \
+       "-----BEGIN TRUSTED CERTIFICATE-----\n" \
+       "MIIEPjCCAyagAwIBAgIESlOMKDANBgkqhkiG9w0BAQsFADCBvjELMAkGA1UEBhMC\n" \
+       "VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50\n" \
+       "cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3Qs\n" \
+       "IEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVz\n" \
+       "dCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzIwHhcNMDkwNzA3MTcy\n" \
+       "NTU0WhcNMzAxMjA3MTc1NTU0WjCBvjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUVu\n" \
+       "dHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwt\n" \
+       "dGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0\n" \
+       "aG9yaXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVzdCBSb290IENlcnRpZmlj\n" \
+       "YXRpb24gQXV0aG9yaXR5IC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK\n" \
+       "AoIBAQC6hLZy254Ma+KZ6TABp3bqMriVQRrJ2mFOWHLP/vaCeb9zYQYKpSfYs1/T\n" \
+       "RU4cctZOMvJyig/3gxnQaoCAAEUesMfnmr8SVycco2gvCoe9amsOXmXzHHfV1IWN\n" \
+       "cCG0szLni6LVhjkCsbjSR87kyUnEO6fe+1R9V77w6G7CebI6C1XiUJgWMhNcL3hW\n" \
+       "wcKUs/Ja5CeanyTXxuzQmyWC48zCxEXFjJd6BmsqEZ+pCm5IO2/b1BEZQvePB7/1\n" \
+       "U1+cPvQXLOZprE4yTGJ36rfo5bs0vBmLrpxR57d+tVOxMyLlbc9wPBr64ptntoP0\n" \
+       "jaWvYkxN4FisZDQSA/i2jZRjJKRxAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAP\n" \
+       "BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqciZ60B7vfec7aVHUbI2fkBJmqzAN\n" \
+       "BgkqhkiG9w0BAQsFAAOCAQEAeZ8dlsa2eT8ijYfThwMEYGprmi5ZiXMRrEPR9RP/\n" \
+       "jTkrwPK9T3CMqS/qF8QLVJ7UG5aYMzyorWKiAHarWWluBh1+xLlEjZivEtRh2woZ\n" \
+       "Rkfz6/djwUAFQKXSt/S1mja/qYh2iARVBCuch38aNzx+LaUa2NSJXsq9rD1s2G2v\n" \
+       "1fN2D807iDginWyTmsQ9v4IbZT+mD12q/OWyFcq1rca8PdCE6OoGcrBNOTJ4vz4R\n" \
+       "nAuknZoh8/CbCzB428Hch0P+vGOaysXCHMnHjf87ElgI5rY97HosTvuDls4MPGmH\n" \
+       "VHOkc8KT/1EQrBVUAdj8BbGJoX90g5pJ19xOe4pIb4tF9jBLMB4GCCsGAQUFBwMD\n" \
+       "BggrBgEFBQcDBAYIKwYBBQUHAwEMKUVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9u\n" \
+       "IEF1dGhvcml0eSAtIEcy\n" \
+       "-----END TRUSTED CERTIFICATE-----\n"
+