#include "common/certificate_validation.h"
-#include <boost/format.hpp>
+#include <vcore/Certificate.h>
#include <vcore/SignatureValidator.h>
+#include <algorithm>
+#include <filesystem>
+#include <fstream>
+#include <regex>
+#include <utility>
+
+#include "common/privileges.h"
#include "common/utils/base64.h"
+#include "common/utils/file_util.h"
+#include "common/utils/glist_range.h"
-namespace bf = boost::filesystem;
namespace ci = common_installer;
+namespace fs = std::filesystem;
namespace {
-bool SetAuthorCertificate(ValidationCore::SignatureData data,
- common_installer::CertificateInfo* cert_info) {
+const char kSignatureAuthor[] = "author-signature.xml";
+const char kRegexDistributorSignature[] = "^(signature)([1-9][0-9]*)(\\.xml)";
+
+static bool SetCertificate(const ValidationCore::SignatureData& data,
+ Property<ValidationCore::CertificatePtr>* certificate,
+ Property<ValidationCore::CertificatePtr>* intermediate_certificate,
+ Property<ValidationCore::CertificatePtr>* root_certificate,
+ bool intermediate_mandatory) {
ValidationCore::CertificateList cert_list = data.getCertList();
ValidationCore::CertificateList::iterator it = cert_list.begin();
if (it == cert_list.end()) {
LOG(ERROR) << "No certificates in certificate list";
return false;
}
- unsigned char* public_key;
- size_t len;
- (*it)->getPublicKeyDER(&public_key, &len);
- std::string author_id =
- ci::EncodeBase64(reinterpret_cast<const char*>(public_key));
- cert_info->author_id.set(author_id);
- cert_info->author_certificate.set(*it);
- // cert_list has at least 3 certificates: end-user, intermediate, root
- // currently pkgmgr can store only one intermediate cert, so just set
- // first intermediate cert here.
+ certificate->set(*it);
++it;
if (it == cert_list.end()) {
- LOG(ERROR) << "No intermediate certificates in certificate list";
- return false;
+ if (intermediate_mandatory) {
+ LOG(ERROR) << "No intermediate certificates in certificate list";
+ return false;
+ } else {
+ intermediate_certificate->set({});
+ }
+ } else {
+ intermediate_certificate->set(*it);
}
- cert_info->author_intermediate_certificate.set(*it);
- cert_info->author_root_certificate.set(data.getRootCaCertificatePtr());
+ root_certificate->set(data.getRootCaCertificatePtr());
return true;
}
-bool SetDistributorCertificate(ValidationCore::SignatureData data,
+static bool SetAuthorCertificate(const ValidationCore::SignatureData& data,
common_installer::CertificateInfo* cert_info) {
ValidationCore::CertificateList cert_list = data.getCertList();
ValidationCore::CertificateList::iterator it = cert_list.begin();
LOG(ERROR) << "No certificates in certificate list";
return false;
}
- cert_info->distributor_certificate.set(*it);
- ++it;
- if (it == cert_list.end()) {
- LOG(ERROR) << "No intermediate certificates in certificate list";
- return false;
+ unsigned char* public_key = nullptr;
+ size_t len;
+ (*it)->getPublicKeyDER(&public_key, &len);
+ std::string author_id =
+ ci::EncodeBase64(public_key, len);
+ cert_info->author_id.set(std::move(author_id));
+ free(public_key);
+
+ return SetCertificate(data,
+ &cert_info->auth_cert,
+ &cert_info->auth_im_cert,
+ &cert_info->auth_root_cert,
+ true);
+}
+
+bool SetDistributorCertificate(const ValidationCore::SignatureData& data,
+ common_installer::CertificateInfo* cert_info) {
+ return SetCertificate(data,
+ &cert_info->dist_cert,
+ &cert_info->dist_im_cert,
+ &cert_info->dist_root_cert,
+ true);
+}
+
+bool SetDistributor2Certificate(const ValidationCore::SignatureData& data,
+ common_installer::CertificateInfo* cert_info) {
+ return SetCertificate(data,
+ &cert_info->dist2_cert,
+ &cert_info->dist2_im_cert,
+ &cert_info->dist2_root_cert,
+ false);
+}
+
+bool SetSignature(std::ifstream* ifs,
+ ValidationCore::CertificatePtr* certificate,
+ std::string* cert_str) {
+ std::string cert;
+ std::getline(*ifs, cert);
+ if (cert.length() != 0) {
+ ValidationCore::CertificatePtr cert_ptr(
+ new ValidationCore::Certificate(
+ cert, ValidationCore::Certificate::FormType::FORM_BASE64));
+ *certificate = std::move(cert_ptr);
+ *cert_str = std::move(cert);
}
- cert_info->distributor_intermediate_certificate.set(*it);
- cert_info->distributor_root_certificate.set(data.getRootCaCertificatePtr());
+
+ return true;
+}
+
+bool ReadSignature(std::ifstream* ifs,
+ ValidationCore::CertificatePtr* cert,
+ ValidationCore::CertificatePtr* im_cert,
+ ValidationCore::CertificatePtr* root_cert,
+ std::string* root_cert_str) {
+
+ SetSignature(ifs, cert, root_cert_str);
+ SetSignature(ifs, im_cert, root_cert_str);
+ SetSignature(ifs, root_cert, root_cert_str);
return true;
}
}
}
-void SetPrivilegeLevel(ValidationCore::SignatureData data,
+void SetPrivilegeLevel(const ValidationCore::SignatureData& data,
common_installer::PrivilegeLevel* level) {
// already set
if (*level != common_installer::PrivilegeLevel::UNTRUSTED)
}
bool ValidateSignatureFile(
- const bf::path& base_path,
+ const fs::path& base_path,
const ValidationCore::SignatureFileInfo& file_info,
common_installer::PrivilegeLevel* level,
common_installer::CertificateInfo* cert_info,
bool check_reference, std::string* error_message) {
- bf::path path = base_path / file_info.getFileName();
- LOG(INFO) << "Processing signature: " << path;
+ LOG(INFO) << "Processing signature: " << file_info.getFileName();
ValidationCore::SignatureValidator validator(file_info);
ValidationCore::SignatureData data;
- ValidationCore::VCerr result = validator.check(
- base_path.string(), // app content path for checking hash of file ref.
- true, // ocsp check flag
- check_reference, // file reference hash check flag
- data); // output signature data
+ ValidationCore::VCerr result;
+ if (check_reference) {
+ result = validator.check(base_path.string(), true, true, data);
+ } else {
+ result = validator.checkList(true, ValidationCore::UriList(), data);
+ }
- std::string errnum = boost::str(boost::format("%d") % result);
+ std::string errnum = std::to_string(result);
*error_message = validator.errorToString(result);
*error_message += ":<" + errnum + ">";
LOG(ERROR) << "Certificate is revoked";
return false;
case ValidationCore::E_SIG_DISREGARDED:
- if (data.isAuthorSignature()) {
- LOG(ERROR) << "Author-signiture is disregarded";
+ LOG(INFO) << "Signature disregarded: " << file_info.getFileName();
+ // in this case, signature2.xml is signed with non-Tizen certificate
+ if (file_info.getFileNumber() == 2) {
+ if (!SetDistributor2Certificate(data, cert_info))
return false;
- }
- LOG(WARNING) << "Signature disregarded: " << path;
- break;
+ }
+ break;
case ValidationCore::E_SIG_NONE:
if (data.isAuthorSignature()) {
// set author certificates to be saved in pkgmgr
SetPrivilegeLevel(data, level);
if (!SetDistributorCertificate(data, cert_info))
return false;
+ } else if (file_info.getFileNumber() == 2) {
+ if (!SetDistributor2Certificate(data, cert_info))
+ return false;
}
- // TODO(s89.jang): Set distributor2 certificate
break;
default:
LOG(ERROR) << "signature validation check failed : "
return true;
}
-bool ValidateSignatures(const bf::path& base_path,
+bool CheckAuthorSignature(const ValidationCore::SignatureFileInfo& file_info) {
+ return file_info.getFileName().find(kSignatureAuthor) != std::string::npos;
+}
+
+bool CheckDistSignature(const ValidationCore::SignatureFileInfo& file_info) {
+ std::regex distributor_regex(kRegexDistributorSignature);
+ fs::path file_path(file_info.getFileName());
+ return std::regex_search(file_path.filename().string(), distributor_regex);
+}
+
+bool ValidateSignatures(const fs::path& base_path,
PrivilegeLevel* level, common_installer::CertificateInfo* cert_info,
bool check_reference, std::string* error_message) {
// Find signature files
}
LOG(INFO) << "Number of signature files: " << signature_files.size();
+ bool author_signatures = std::any_of(
+ signature_files.begin(), signature_files.end(), CheckAuthorSignature);
+
+ bool distributor_signatures = std::any_of(
+ signature_files.begin(), signature_files.end(), CheckDistSignature);
+
+ if (getuid() != 0 && (!author_signatures || !distributor_signatures) &&
+ check_reference) {
+ LOG(ERROR) << "Author or distribuor signature is missing.";
+ return false;
+ }
+
// Read xml schema for signatures
for (auto& file_info : signature_files) {
std::string error;
return true;
}
+bool GetSignatureFromFile(const std::string& pkgid,
+ bool is_readonly_package, PrivilegeLevel* level,
+ ci::CertificateInfo* cert_info) {
+ CertSvcInstance instance;
+ CertSvcCertificate certificate;
+ CertSvcVisibility visibility = CERTSVC_VISIBILITY_DEVELOPER;
+ std::string root_cert;
+
+ fs::path file_path((is_readonly_package) ?
+ tzplatform_getenv(TZ_SYS_RO_SHARE) : tzplatform_getenv(TZ_SYS_SHARE));
+ file_path /= std::string("signatures/" + pkgid + ".txt");
+ std::ifstream ifs(file_path.c_str(), std::ifstream::in);
+ if (!ifs.is_open()) {
+ LOG(INFO) << "Failed to open file : " << file_path;
+ return false;
+ }
+
+ if (!ReadSignature(&ifs,
+ &cert_info->dist2_cert.get(),
+ &cert_info->dist2_im_cert.get(),
+ &cert_info->dist2_root_cert.get(),
+ &root_cert) ||
+ !ReadSignature(&ifs,
+ &cert_info->dist_cert.get(),
+ &cert_info->dist_im_cert.get(),
+ &cert_info->dist_root_cert.get(),
+ &root_cert))
+ return false;
+ if (root_cert.length() == 0) {
+ LOG(INFO) << "Dist root cert not exists";
+ return false;
+ }
+
+ int ret = certsvc_instance_new(&instance);
+ if (ret != CERTSVC_SUCCESS) {
+ LOG(ERROR) << "certsvc_instance_new failed :" << ret;
+ return false;
+ }
+ ret = certsvc_certificate_new_from_memory(instance,
+ reinterpret_cast<const unsigned char *>(root_cert.c_str()),
+ strlen(root_cert.c_str()),
+ CERTSVC_FORM_DER_BASE64,
+ &certificate);
+ if (ret != CERTSVC_SUCCESS) {
+ LOG(ERROR) << "certsvc_certificate_new_from_memory failed :" << ret;
+ certsvc_instance_free(instance);
+ return false;
+ }
+
+ ret = certsvc_certificate_get_visibility(certificate, &visibility);
+ if (ret != CERTSVC_SUCCESS) {
+ LOG(ERROR) << "Failed to get visibility from file :" << ret;
+ certsvc_certificate_free(certificate);
+ certsvc_instance_free(instance);
+ return false;
+ }
+
+ certsvc_certificate_free(certificate);
+ certsvc_instance_free(instance);
+ *level = CertStoreIdToPrivilegeLevel(visibility);
+
+ return true;
+}
+
+void FreePrivilegeList(GList* priv) {
+ g_list_free_full(priv, free);
+}
+
bool ValidatePrivilegeLevel(common_installer::PrivilegeLevel level,
- bool is_webapp, const char* api_version, GList* privileges,
+ uid_t uid, const char* api_version, GList* privileges,
std::string* error_message) {
if (level == common_installer::PrivilegeLevel::UNTRUSTED) {
if (privileges) {
}
}
+ GList* native_privileges =
+ ci::PrivilegeXToPrivilege(privileges, ci::kNativePrivilegeType);
+ std::unique_ptr<GList, decltype(FreePrivilegeList)*> native_privs_deleter(
+ native_privileges, FreePrivilegeList);
+ GList* web_privileges =
+ ci::PrivilegeXToPrivilege(privileges, ci::kWebPrivilegeType);
+ std::unique_ptr<GList, decltype(FreePrivilegeList)*> web_privs_deleter(
+ web_privileges, FreePrivilegeList);
+
+ for (const std::pair<GList*, bool>& pair :
+ std::initializer_list<std::pair<GList*, bool>>{
+ {native_privileges, false},
+ {web_privileges, true}
+ }) {
+ char* error = nullptr;
+ int status = PRVMGR_ERR_NONE;
+ // Do the privilege check only if the package has privileges
+ if (pair.first) {
+ status = privilege_manager_verify_privilege(uid, api_version,
+ pair.second ? PRVMGR_PACKAGE_TYPE_WRT : PRVMGR_PACKAGE_TYPE_CORE,
+ pair.first, PrivilegeLevelToVisibility(level), &error);
+ }
+ if (status != PRVMGR_ERR_NONE) {
+ std::string errnum = std::to_string(status);
+ if (error)
+ *error_message = error;
+ else
+ *error_message = "";
+ LOG(ERROR) << "Error while verifing privilege level: "
+ << *error_message << " <" << errnum << ">";
+ *error_message += ":<" + errnum + ">";
+ free(error);
+ return false;
+ }
+ }
+ LOG(INFO) << "Privilege level checked";
+ return true;
+}
+
+bool ValidateMetadataPrivilege(common_installer::PrivilegeLevel level,
+ const char* api_version, GList* metadata_list,
+ std::string* error_message) {
+ if (!metadata_list)
+ return true;
char* error = nullptr;
int status = PRVMGR_ERR_NONE;
- // Do the privilege check only if the package has privileges
- if (privileges) {
- status = privilege_manager_verify_privilege(api_version,
- is_webapp ? PRVMGR_PACKAGE_TYPE_WRT : PRVMGR_PACKAGE_TYPE_CORE,
- privileges, PrivilegeLevelToVisibility(level), &error);
- }
+
+ GList *metadata_keylist = NULL;
+ for (metadata_x* meta : GListRange<metadata_x*>(metadata_list))
+ metadata_keylist = g_list_append(metadata_keylist, meta->key);
+
+ status = privilege_manager_verify_metadata(api_version, metadata_keylist,
+ PrivilegeLevelToVisibility(level), &error);
+
+ if (metadata_keylist)
+ g_list_free(metadata_keylist);
+
if (status != PRVMGR_ERR_NONE) {
- std::string errnum =
- boost::str(boost::format("%d") % status);
- LOG(ERROR) << "Error while verifing privilege level: "
- << error << " <" << errnum << ">";
- *error_message = error;
- *error_message += ":<" + errnum + ">";
- free(error);
- return false;
+ std::string errnum = std::to_string(status);
+ if (error)
+ *error_message = error;
+ else
+ *error_message = "";
+ LOG(ERROR) << "Error while verifing metadata privilege: "
+ << *error_message << " <" << errnum << ">";
+ *error_message += ":<" + errnum + ">";
+ free(error);
+ return false;
}
- LOG(INFO) << "Privilege level checked";
return true;
}
+bool IsSameAuthor(const std::string& cert_str1, const std::string& cert_str2) {
+ try {
+ ValidationCore::Certificate cert1 = ValidationCore::Certificate(
+ cert_str1, ValidationCore::Certificate::FormType::FORM_BASE64);
+ ValidationCore::Certificate cert2 = ValidationCore::Certificate(
+ cert_str2, ValidationCore::Certificate::FormType::FORM_BASE64);
+ return cert1.getPublicKeyString() == cert2.getPublicKeyString();
+ } catch (const ValidationCore::Certificate::Exception::Base &e) {
+ LOG(ERROR) << "Exception occured on cert-svc-vcore getBase64: "
+ << e.DumpToString();
+ return false;
+ } catch (...) {
+ LOG(ERROR) << "Error while getting Certificate";
+ return false;
+ }
+}
+
} // namespace common_installer