#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,
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;
};
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());
"[" + 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);
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;
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,
if (ret != 0) {
ERROR("Failed to intall ACTA. Remove custom directory for rollback.");
- this->uninstall();
+ this->m_pImpl->uninstall(true);
}
return ret;
#include <cstdio>
#include <vector>
-#include <memory>
#include <stdexcept>
#include <openssl/pem.h>
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);
}
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
#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"
+