Fix static analysis issues
[platform/core/appfw/app-installers.git] / src / common / certificate_validation.cc
index 9fb02ef..0d7b924 100644 (file)
@@ -4,45 +4,56 @@
 
 #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();
@@ -50,14 +61,64 @@ bool SetDistributorCertificate(ValidationCore::SignatureData data,
     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;
 }
 
@@ -93,7 +154,7 @@ privilege_manager_visibility_e PrivilegeLevelToVisibility(
   }
 }
 
-void SetPrivilegeLevel(ValidationCore::SignatureData data,
+void SetPrivilegeLevel(const ValidationCore::SignatureData& data,
     common_installer::PrivilegeLevel* level) {
   // already set
   if (*level != common_installer::PrivilegeLevel::UNTRUSTED)
@@ -102,23 +163,23 @@ void SetPrivilegeLevel(ValidationCore::SignatureData data,
 }
 
 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 + ">";
 
@@ -127,12 +188,13 @@ bool ValidateSignatureFile(
       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
@@ -144,8 +206,10 @@ bool ValidateSignatureFile(
         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 : "
@@ -155,7 +219,17 @@ bool ValidateSignatureFile(
   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
@@ -168,6 +242,18 @@ bool ValidateSignatures(const bf::path& base_path,
   }
   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;
@@ -180,8 +266,76 @@ bool ValidateSignatures(const bf::path& base_path,
   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) {
@@ -192,26 +346,93 @@ bool ValidatePrivilegeLevel(common_installer::PrivilegeLevel level,
     }
   }
 
+  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