2 * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 * @file task_certify.cpp
18 * @author Pawel Sikorski (p.sikorski@samgsung.com)
29 #include <sys/types.h>
31 #include <dpl/assert.h>
35 #include <widget_install/task_certify.h>
36 #include <dpl/wrt-dao-ro/global_config.h>
37 #include <dpl/wrt-dao-ro/widget_config.h>
38 #include "wac_widget_id.h"
40 #include <vcore/Certificate.h>
41 #include <vcore/SignatureReader.h>
42 #include <vcore/SignatureFinder.h>
43 #include <vcore/WrtSignatureValidator.h>
44 #include <pkgmgr-info.h>
45 #include <dpl/utils/wrt_global_settings.h>
47 #include <dpl/string.h>
48 #include <dpl/log/secure_log.h>
50 using namespace ValidationCore;
51 using namespace WrtDB;
54 const int SIGNATURE_FILE_NUMBER_DISTRIBUTOR1 = 1;
55 const int SIGNATURE_FILE_NUMBER_DISTRIBUTOR2 = 2;
57 WidgetCertificateData toWidgetCertificateData(const SignatureData &data,
60 WidgetCertificateData result;
62 result.chainId = data.getSignatureNumber();
63 _D("result.chainId : %d", result.chainId);
65 result.owner = data.isAuthorSignature() ?
66 WidgetCertificateData::AUTHOR : WidgetCertificateData::DISTRIBUTOR;
68 if (data.isAuthorSignature()) {
69 result.owner = WidgetCertificateData::AUTHOR;
71 if (SIGNATURE_FILE_NUMBER_DISTRIBUTOR1 == data.getSignatureNumber()) {
72 result.owner = WidgetCertificateData::DISTRIBUTOR;
73 } else if (SIGNATURE_FILE_NUMBER_DISTRIBUTOR2 ==
74 data.getSignatureNumber()){
75 result.owner = WidgetCertificateData::DISTRIBUTOR2;
77 result.owner = WidgetCertificateData::UNKNOWN;
82 WidgetCertificateData::ROOT : WidgetCertificateData::ENDENTITY;
84 CertificatePtr certificate;
87 certificate = data.getRootCaCertificatePtr();
89 certificate = data.getEndEntityCertificatePtr();
92 AssertMsg(certificate && !certificate->getCommonName().empty(),
93 "CommonName is Null");
95 result.strCommonName = DPL::FromUTF8String(certificate->getCommonName());
97 result.strMD5Fingerprint = std::string("md5 ") +
98 Certificate::FingerprintToColonHex(
99 certificate->getFingerprint(Certificate::FINGERPRINT_MD5));
101 result.strSHA1Fingerprint = std::string("sha-1 ") +
102 Certificate::FingerprintToColonHex(
103 certificate->getFingerprint(Certificate::FINGERPRINT_SHA1));
108 CertificatePtr getOldAuthorSignerCertificate(DPL::String appid)
110 WidgetDAOReadOnly dao(appid);
111 CertificateChainList chainList = dao.getWidgetCertificate(SIGNATURE_AUTHOR);
113 FOREACH(it, chainList)
115 ValidationCore::CertificateCollection chain;
116 if (false == chain.load(*it)) {
117 _E("Chain is broken");
121 _E("Chain failed at sorting");
124 ValidationCore::CertificateList list = chain.getCertificateList();
128 if (!(*cert)->isRootCert() && !(*cert)->isCA()) {
133 return CertificatePtr();
135 } // namespace anonymous
138 namespace WidgetInstall {
139 TaskCertify::TaskCertify(JobWidgetInstall * const &jobContext) :
140 DPL::TaskDecl<TaskCertify>(this),
141 m_jobContext(jobContext)
143 AddStep(&TaskCertify::StartStep);
144 AddStep(&TaskCertify::stepSignature);
145 // certi comparison determines whether the update.
146 if (true == m_jobContext->m_installerContext.isUpdateMode) {
147 AddStep(&TaskCertify::stepVerifyUpdate);
149 AddStep(&TaskCertify::stepCertifyLevel);
150 AddStep(&TaskCertify::EndStep);
153 void TaskCertify::processDistributorSignature(const SignatureData &data)
155 // this signature is verified -
156 // no point in check domain WAC_ROOT and WAC_RECOGNIZED
157 m_jobContext->m_installerContext.widgetSecurity.setDistributorSigned(true);
159 CertificateCollection collection;
160 collection.load(data.getCertList());
161 AssertMsg(collection.sort(),
162 "Certificate collection can't sort");
164 AssertMsg(collection.isChain(),
165 "Certificate collection is not able to create chain. "
166 "It is not possible to verify this signature.");
168 if (SIGNATURE_FILE_NUMBER_DISTRIBUTOR1 == data.getSignatureNumber()) {
169 m_jobContext->m_installerContext.widgetSecurity.getCertificateChainListRef().push_back(
172 m_jobContext->m_installerContext.widgetSecurity.getCertificateChainList2Ref().push_back(
176 m_jobContext->m_installerContext.widgetSecurity.getCertificateListRef().push_back(
177 toWidgetCertificateData(data, true));
178 m_jobContext->m_installerContext.widgetSecurity.getCertificateListRef().push_back(
179 toWidgetCertificateData(data, false));
182 void TaskCertify::processAuthorSignature(const SignatureData &data)
184 using namespace ValidationCore;
185 _D("DNS Identity match!");
186 // this signature is verified or widget is distributor signed
187 m_jobContext->m_installerContext.widgetSecurity.setAuthorCertificatePtr(data.getEndEntityCertificatePtr());
188 CertificatePtr test = m_jobContext->m_installerContext.widgetSecurity.getAuthorCertificatePtr();
190 m_jobContext->m_installerContext.widgetSecurity.getCertificateListRef().push_back(
191 toWidgetCertificateData(data, true));
192 m_jobContext->m_installerContext.widgetSecurity.getCertificateListRef().push_back(
193 toWidgetCertificateData(data, false));
195 // match widget_id with one from dns identity set
196 WacWidgetId widgetId(m_jobContext->m_installerContext.widgetConfig.configInfo.widget_id);
198 CertificatePtr cert = data.getEndEntityCertificatePtr();
200 Certificate::AltNameSet dnsIdentity = cert->getAlternativeNameDNS();
202 CertificateCollection collection;
203 collection.load(data.getCertList());
205 AssertMsg(collection.isChain(),
206 "Certificate collection is not able to create chain. "
207 "It is not possible to verify this signature.");
209 m_jobContext->m_installerContext.widgetSecurity.getAuthorsCertificateChainListRef().push_back(
212 FOREACH(it, dnsIdentity){
213 if (widgetId.matchHost(DPL::FromASCIIString(*it))) {
214 m_jobContext->m_installerContext.widgetSecurity.setRecognized(true);
220 void TaskCertify::getSignatureFiles(std::string path, SignatureFileInfoSet& file)
222 _D("path : %s", path.c_str());
223 SignatureFileInfoSet signatureFiles;
224 SignatureFinder signatureFinder(path);
225 if (SignatureFinder::NO_ERROR != signatureFinder.find(file)) {
226 _E("Error in Signature Finder : %s", path.c_str());
227 ThrowMsg(Exceptions::SignatureNotFound,
228 "Error openig temporary widget directory");
232 void TaskCertify::stepSignature()
234 LOGI("================ Step: <<Signature>> ENTER ===============");
236 std::string widgetPath;
237 widgetPath = m_jobContext->m_installerContext.locations->getPackageInstallationDir() + "/";
239 SignatureFileInfoSet signatureFiles;
242 getSignatureFiles(widgetPath, signatureFiles);
244 if (signatureFiles.size() <= 0) {
245 widgetPath += std::string(WrtDB::GlobalConfig::GetWidgetSrcPath())
247 if (0 == access(widgetPath.c_str(), F_OK)) {
248 getSignatureFiles(widgetPath, signatureFiles);
251 } Catch(Exceptions::SignatureNotFound) {
252 ReThrowMsg(Exceptions::SignatureNotFound, widgetPath);
255 SignatureFileInfoSet::reverse_iterator iter = signatureFiles.rbegin();
256 _D("Number of signatures: %d", signatureFiles.size());
257 Level level = Level::UNKNOWN;
259 for (; iter != signatureFiles.rend(); ++iter) {
260 _D("Checking signature with id=%d", iter->getFileNumber());
261 SignatureData data(widgetPath + iter->getFileName(),
262 iter->getFileNumber());
266 xml.initialize(data, GlobalConfig::GetSignatureXmlSchema());
269 WrtSignatureValidator::Result result;
271 WrtSignatureValidator validator(
272 WrtSignatureValidator::TIZEN,
274 OCSPTestModeEnabled(),
276 CrlTestModeEnabled(),
279 result = validator.check(data, widgetPath);
281 if (m_jobContext->m_installerContext.mode.command == InstallMode::Command::RECOVERY
282 || m_jobContext->m_installerContext.mode.installTime == InstallMode::InstallTime::FOTA)
284 result = WrtSignatureValidator::SIGNATURE_VERIFIED;
287 if (result == WrtSignatureValidator::SIGNATURE_REVOKED) {
288 _W("Certificate is REVOKED");
289 ThrowMsg(Exceptions::CertificateExpired,
290 "Certificate is REVOKED");
293 if (result != WrtSignatureValidator::SIGNATURE_VERIFIED &&
294 iter->getFileNumber() <= 1) {
295 _W("Signature is INVALID");
296 // TODO change exception name
297 ThrowMsg(Exceptions::SignatureInvalid,
301 if (data.isAuthorSignature()) {
302 if (result == WrtSignatureValidator::SIGNATURE_VERIFIED ) {
303 processAuthorSignature(data);
306 if (result == WrtSignatureValidator::SIGNATURE_VALID ||
307 result == WrtSignatureValidator::SIGNATURE_VERIFIED ||
308 result == WrtSignatureValidator::SIGNATURE_DISREGARD ||
309 result == WrtSignatureValidator::SIGNATURE_REVOKED) {
310 processDistributorSignature(data);
311 Level currentCertLevel =
312 certTypeToLevel(data.getVisibilityLevel());
313 if (currentCertLevel == Level::UNKNOWN) {
316 if (currentCertLevel > level) {
317 level = currentCertLevel;
318 _D("level %s", enumToString(level).c_str());
322 } Catch(ParserSchemaException::Base) {
323 _E("Error occured in ParserSchema.");
324 // cannot rethrow ValidationCore::Exception as DPL::Exception.
326 ThrowMsg(Exceptions::SignatureInvalid,
327 "Error occured in ParserSchema.");
329 m_jobContext->m_installerContext.certLevel = level;
332 if (signatureFiles.empty()) {
333 _D("No signature files has been found.");
334 m_jobContext->m_installerContext.certLevel = level;
337 LOGI("================ Step: <<Signature>> DONE ================");
339 m_jobContext->UpdateProgress(
340 InstallerContext::INSTALL_DIGSIG_CHECK,
341 "Widget Signature checked");
344 std::string TaskCertify::enumToString(
345 TaskCertify::Level level)
348 #define X(x, y) case x: return #y;
349 X(Level::UNKNOWN, UNKNOWN)
350 X(Level::PUBLIC, PUBLIC)
351 X(Level::PARTNER, PARTNER)
352 X(Level::PLATFORM, PLATFORM)
359 TaskCertify::Level TaskCertify::certTypeToLevel(
360 CertStoreId::Type type)
362 // CertStoreType.h (framework/security/cert-svc)
363 // RootCA's visibility level : public
364 // const Type VIS_PUBLIC = 1 << 6;
365 // RootCA's visibility level : partner
366 // const Type VIS_PARTNER = 1 << 7;
367 // RootCA's visibility level : platform
368 // const Type VIS_PLATFORM = 1 << 10;
369 if (type == CertStoreId::VIS_PUBLIC) {
370 return Level::PUBLIC;
371 } else if (type == CertStoreId::VIS_PARTNER) {
372 return Level::PARTNER;
373 } else if (type == CertStoreId::VIS_PLATFORM) {
374 return Level::PLATFORM;
376 return Level::UNKNOWN;
379 bool TaskCertify::isTizenWebApp() const
382 if (m_jobContext->m_installerContext.widgetConfig.webAppType.appType
383 == WrtDB::AppType::APP_TYPE_TIZENWEBAPP)
391 void TaskCertify::stepVerifyUpdate()
393 LOGI("================ Step: <<VerifyUpdate>> ENTER ===============");
395 std::string oldAuthorCert;
398 pkgmgrinfo_certinfo_h handle;
399 const char *authorCert = NULL;
400 ret = pkgmgrinfo_pkginfo_create_certinfo(&handle);
401 if (PMINFO_R_OK == ret) {
402 ret = pkgmgrinfo_pkginfo_load_certinfo(DPL::ToUTF8String(
403 m_jobContext->m_installerContext.widgetConfig.tzPkgid).c_str(), handle);
404 if (PMINFO_R_OK == ret) {
405 ret = pkgmgrinfo_pkginfo_get_cert_value(handle,
406 PMINFO_AUTHOR_SIGNER_CERT, &authorCert);
407 if (PMINFO_R_OK == ret) {
408 oldAuthorCert = (NULL != authorCert)? authorCert : "";
411 pkgmgrinfo_pkginfo_destroy_certinfo(handle);
414 ValidationCore::CertificatePtr certPtr = m_jobContext->m_installerContext.widgetSecurity.getAuthorCertificatePtr();
416 if (oldAuthorCert.empty()) {
417 _D("Old cert is empty.");
418 if (certPtr != NULL) {
419 ThrowMsg(Exceptions::NotMatchedCertification,
420 "Author signer certificates doesn't match \
421 between old widget and installing widget");
424 if (certPtr == NULL) {
425 ThrowMsg(Exceptions::NotMatchedCertification, "No author certificates");
428 std::string newAuthorPublicKeyStr = certPtr->getPublicKeyString();
429 //compare with public key
430 ValidationCore::Certificate installedCert(oldAuthorCert , ValidationCore::Certificate::FORM_BASE64);
431 std::string installedPublicKeyStr = installedCert.getPublicKeyString();
432 if (0 != installedPublicKeyStr.compare(newAuthorPublicKeyStr)) {
433 _D("old widget's author public key : %ls",
434 installedPublicKeyStr.c_str());
435 _D("new widget's author public key: %ls",
436 newAuthorPublicKeyStr.c_str());
437 ThrowMsg(Exceptions::NotMatchedCertification,
438 "Author signer certificates doesn't match \
439 between old widget and installing widget");
442 LOGI("================ Step: <<VerifyUpdate>> DONE ===============");
445 void TaskCertify::stepCertifyLevel()
447 LOGI("================ Step: <<Certify Level>> ENTER ===============");
448 if (!checkConfigurationLevel(static_cast<TaskCertify::Level>
449 (m_jobContext->m_installerContext.certLevel))) {
450 ThrowMsg(Exceptions::PrivilegeLevelViolation, "setting level violate");
452 LOGI("================ Step: <<Certify Level>> DONE ================");
455 bool TaskCertify::checkConfigurationLevel(
456 TaskCertify::Level level)
458 if (!checkSettingLevel(level)) {
461 if (!checkNPRuntime(level)) {
464 if (!checkServiceLevel(level)) {
467 if (!checkBackgroundCategoryLevel(level)) {
473 bool TaskCertify::checkSettingLevel(
474 TaskCertify::Level level)
476 secureSettingMap data = {
477 #if ENABLE(PRE_LAUNCH)
478 {"pre-launching", Level::PLATFORM},
480 {"sound-mode", Level::PARTNER},
481 {"nodisplay", Level::PARTNER},
482 {"background-vibration", Level::PARTNER}
484 FOREACH(it, m_jobContext->m_installerContext.widgetConfig.configInfo.settingsList) {
485 secureSettingIter ret = data.find(DPL::ToUTF8String(it->m_name));
486 if (ret != data.end()) {
487 if (level < ret->second) {
488 _E("\"%ls\" needs \"%s\" level", it->m_name.c_str(), enumToString(ret->second).c_str());
496 bool TaskCertify::checkNPRuntime(
497 TaskCertify::Level level)
499 DPL::String& pkgid = m_jobContext->m_installerContext.widgetConfig.tzPkgid;
500 std::string npruntime_path = WrtDB::WidgetConfig::GetWidgetNPRuntimePluginsPath(pkgid);
501 DIR *npruntime_dir = opendir(npruntime_path.c_str());
503 if (npruntime_dir == NULL) {
507 struct dirent dEntry;
508 struct dirent *dEntryResult;
511 std::string suffix = ".so";
514 struct stat statInfo;
515 ret = readdir_r(npruntime_dir, &dEntry, &dEntryResult);
516 if (dEntryResult != NULL && ret == 0) {
517 std::string fileName = dEntry.d_name;
518 std::string fullName = npruntime_path + "/" + fileName;
520 if (stat(fullName.c_str(), &statInfo) != 0) {
521 closedir(npruntime_dir);
525 if (S_ISDIR(statInfo.st_mode)) {
526 if (("." == fileName) || (".." == fileName)) {
530 if(fileName.size() >= suffix.size() &&
531 fileName.compare(fileName.size() - suffix.size(), suffix.size(), suffix) == 0) {
532 result = level >= Level::PARTNER;
537 } while (dEntryResult != NULL && ret == 0);
538 closedir(npruntime_dir);
540 _E("npruntime needs partner level");
545 bool TaskCertify::checkServiceLevel(
546 TaskCertify::Level level)
548 if (m_jobContext->m_installerContext.widgetConfig.configInfo.serviceAppInfoList.size() > 0) {
549 if (level < Level::PARTNER) {
550 _E("\"tizen:service\" needs \"%s \" level", enumToString(Level::PARTNER).c_str());
557 bool TaskCertify::checkBackgroundCategoryLevel(
558 TaskCertify::Level level)
560 FOREACH(it, m_jobContext->m_installerContext.widgetConfig.configInfo.backgroundCategoryList) {
561 if (!strcmp("system", DPL::ToUTF8String(*it).c_str())) {
562 if (level < Level::PARTNER) {
563 _E("<tizen:background-category value=\"system\"> needs \"%s\" level", enumToString(Level::PARTNER).c_str());
571 void TaskCertify::StartStep()
573 LOGI("--------- <TaskCertify> : START ----------");
576 void TaskCertify::EndStep()
578 LOGI("Step: <<CERTYFYING DONE>>");
580 m_jobContext->UpdateProgress(
581 InstallerContext::INSTALL_CERT_CHECK,
582 "Widget Certification Check Finished");
584 LOGI("--------- <TaskCertify> : END ----------");
586 } //namespace WidgetInstall