Initialize Tizen 2.3
[framework/web/wrt-installer.git] / src / jobs / widget_install / task_certify.cpp
1 /*
2  * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
3  *
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
7  *
8  *        http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 /*
17  * @file    task_certify.cpp
18  * @author  Pawel Sikorski (p.sikorski@samgsung.com)
19  * @version
20  * @brief
21  */
22
23 //SYSTEM INCLUDES
24 #include <cstring>
25 #include <string>
26 #include <sstream>
27 #include <unistd.h>
28 #include <dirent.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <dpl/assert.h>
32 #include <pcrecpp.h>
33
34 //WRT INCLUDES
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"
39
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>
46
47 #include <dpl/string.h>
48 #include <dpl/log/secure_log.h>
49
50 using namespace ValidationCore;
51 using namespace WrtDB;
52
53 namespace {
54 const int SIGNATURE_FILE_NUMBER_DISTRIBUTOR1 = 1;
55 const int SIGNATURE_FILE_NUMBER_DISTRIBUTOR2 = 2;
56
57 WidgetCertificateData toWidgetCertificateData(const SignatureData &data,
58                                               bool root)
59 {
60     WidgetCertificateData result;
61
62     result.chainId = data.getSignatureNumber();
63     _D("result.chainId : %d", result.chainId);
64
65     result.owner = data.isAuthorSignature() ?
66         WidgetCertificateData::AUTHOR : WidgetCertificateData::DISTRIBUTOR;
67
68     if (data.isAuthorSignature()) {
69         result.owner = WidgetCertificateData::AUTHOR;
70     } else {
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;
76         } else {
77             result.owner = WidgetCertificateData::UNKNOWN;
78         }
79     }
80
81     result.type = root ?
82         WidgetCertificateData::ROOT : WidgetCertificateData::ENDENTITY;
83
84     CertificatePtr certificate;
85
86     if (root) {
87         certificate = data.getRootCaCertificatePtr();
88     } else {
89         certificate = data.getEndEntityCertificatePtr();
90     }
91
92     AssertMsg(certificate && !certificate->getCommonName().empty(),
93            "CommonName is Null");
94
95     result.strCommonName = DPL::FromUTF8String(certificate->getCommonName());
96
97     result.strMD5Fingerprint = std::string("md5 ") +
98         Certificate::FingerprintToColonHex(
99             certificate->getFingerprint(Certificate::FINGERPRINT_MD5));
100
101     result.strSHA1Fingerprint = std::string("sha-1 ") +
102         Certificate::FingerprintToColonHex(
103             certificate->getFingerprint(Certificate::FINGERPRINT_SHA1));
104
105     return result;
106 }
107
108 CertificatePtr getOldAuthorSignerCertificate(DPL::String appid)
109 {
110     WidgetDAOReadOnly dao(appid);
111     CertificateChainList chainList = dao.getWidgetCertificate(SIGNATURE_AUTHOR);
112
113     FOREACH(it, chainList)
114     {
115         ValidationCore::CertificateCollection chain;
116         if (false == chain.load(*it)) {
117             _E("Chain is broken");
118         }
119
120         if (!chain.sort()) {
121             _E("Chain failed at sorting");
122         }
123
124         ValidationCore::CertificateList list = chain.getCertificateList();
125
126         FOREACH(cert, list)
127         {
128             if (!(*cert)->isRootCert() && !(*cert)->isCA()) {
129                 return *cert;
130             }
131         }
132     }
133     return CertificatePtr();
134 }
135 } // namespace anonymous
136
137 namespace Jobs {
138 namespace WidgetInstall {
139 TaskCertify::TaskCertify(JobWidgetInstall * const &jobContext) :
140     DPL::TaskDecl<TaskCertify>(this),
141     m_jobContext(jobContext)
142 {
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);
148     }
149     AddStep(&TaskCertify::stepCertifyLevel);
150     AddStep(&TaskCertify::EndStep);
151 }
152
153 void TaskCertify::processDistributorSignature(const SignatureData &data)
154 {
155     // this signature is verified -
156     // no point in check domain WAC_ROOT and WAC_RECOGNIZED
157     m_jobContext->m_installerContext.widgetSecurity.setDistributorSigned(true);
158
159     CertificateCollection collection;
160     collection.load(data.getCertList());
161     AssertMsg(collection.sort(),
162            "Certificate collection can't sort");
163
164     AssertMsg(collection.isChain(),
165            "Certificate collection is not able to create chain. "
166            "It is not possible to verify this signature.");
167
168     if (SIGNATURE_FILE_NUMBER_DISTRIBUTOR1 == data.getSignatureNumber()) {
169         m_jobContext->m_installerContext.widgetSecurity.getCertificateChainListRef().push_back(
170                 collection);
171     } else {
172         m_jobContext->m_installerContext.widgetSecurity.getCertificateChainList2Ref().push_back(
173                 collection);
174     }
175
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));
180 }
181
182 void TaskCertify::processAuthorSignature(const SignatureData &data)
183 {
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();
189
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));
194
195     // match widget_id with one from dns identity set
196     WacWidgetId widgetId(m_jobContext->m_installerContext.widgetConfig.configInfo.widget_id);
197
198     CertificatePtr cert = data.getEndEntityCertificatePtr();
199     Assert(cert);
200     Certificate::AltNameSet dnsIdentity = cert->getAlternativeNameDNS();
201
202     CertificateCollection collection;
203     collection.load(data.getCertList());
204     collection.sort();
205     AssertMsg(collection.isChain(),
206            "Certificate collection is not able to create chain. "
207            "It is not possible to verify this signature.");
208
209     m_jobContext->m_installerContext.widgetSecurity.getAuthorsCertificateChainListRef().push_back(
210         collection);
211
212     FOREACH(it, dnsIdentity){
213         if (widgetId.matchHost(DPL::FromASCIIString(*it))) {
214             m_jobContext->m_installerContext.widgetSecurity.setRecognized(true);
215             return;
216         }
217     }
218 }
219
220 void TaskCertify::getSignatureFiles(std::string path, SignatureFileInfoSet& file)
221 {
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");
229     }
230 }
231
232 void TaskCertify::stepSignature()
233 {
234     LOGI("================ Step: <<Signature>> ENTER ===============");
235
236     std::string widgetPath;
237     widgetPath = m_jobContext->m_installerContext.locations->getPackageInstallationDir() + "/";
238
239     SignatureFileInfoSet signatureFiles;
240
241     Try {
242         getSignatureFiles(widgetPath, signatureFiles);
243
244         if (signatureFiles.size() <= 0) {
245             widgetPath += std::string(WrtDB::GlobalConfig::GetWidgetSrcPath())
246                 + "/";
247             if (0 == access(widgetPath.c_str(), F_OK)) {
248                 getSignatureFiles(widgetPath, signatureFiles);
249             }
250         }
251     } Catch(Exceptions::SignatureNotFound) {
252         ReThrowMsg(Exceptions::SignatureNotFound, widgetPath);
253     }
254
255     SignatureFileInfoSet::reverse_iterator iter = signatureFiles.rbegin();
256     _D("Number of signatures: %d", signatureFiles.size());
257     Level level = Level::UNKNOWN;
258
259     for (; iter != signatureFiles.rend(); ++iter) {
260         _D("Checking signature with id=%d", iter->getFileNumber());
261         SignatureData data(widgetPath + iter->getFileName(),
262                            iter->getFileNumber());
263
264         Try {
265             SignatureReader xml;
266             xml.initialize(data, GlobalConfig::GetSignatureXmlSchema());
267             xml.read(data);
268
269             WrtSignatureValidator::Result result;
270
271             WrtSignatureValidator validator(
272                     WrtSignatureValidator::TIZEN,
273                     !GlobalSettings::
274                     OCSPTestModeEnabled(),
275                     !GlobalSettings::
276                     CrlTestModeEnabled(),
277                     false);
278
279             result = validator.check(data, widgetPath);
280
281             if (m_jobContext->m_installerContext.mode.command == InstallMode::Command::RECOVERY
282                     || m_jobContext->m_installerContext.mode.installTime == InstallMode::InstallTime::FOTA)
283             {
284                 result = WrtSignatureValidator::SIGNATURE_VERIFIED;
285             }
286
287             if (result == WrtSignatureValidator::SIGNATURE_REVOKED) {
288                 _W("Certificate is REVOKED");
289                 ThrowMsg(Exceptions::CertificateExpired,
290                          "Certificate is REVOKED");
291             }
292
293             if (result != WrtSignatureValidator::SIGNATURE_VERIFIED &&
294                     iter->getFileNumber() <= 1) {
295                 _W("Signature is INVALID");
296                 // TODO change exception name
297                 ThrowMsg(Exceptions::SignatureInvalid,
298                         "Invalid Package");
299             }
300
301             if (data.isAuthorSignature()) {
302                 if (result == WrtSignatureValidator::SIGNATURE_VERIFIED ) {
303                     processAuthorSignature(data);
304                 }
305             } else {
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) {
314                         continue;
315                     }
316                     if (currentCertLevel > level) {
317                         level = currentCertLevel;
318                         _D("level %s", enumToString(level).c_str());
319                     }
320                 }
321             }
322         } Catch(ParserSchemaException::Base) {
323             _E("Error occured in ParserSchema.");
324             // cannot rethrow ValidationCore::Exception as DPL::Exception.
325             // let's just throw.
326             ThrowMsg(Exceptions::SignatureInvalid,
327                        "Error occured in ParserSchema.");
328         }
329         m_jobContext->m_installerContext.certLevel = level;
330     }
331
332     if (signatureFiles.empty()) {
333         _D("No signature files has been found.");
334         m_jobContext->m_installerContext.certLevel = level;
335     }
336
337     LOGI("================ Step: <<Signature>> DONE ================");
338
339     m_jobContext->UpdateProgress(
340         InstallerContext::INSTALL_DIGSIG_CHECK,
341         "Widget Signature checked");
342 }
343
344 std::string TaskCertify::enumToString(
345     TaskCertify::Level level)
346 {
347     switch (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)
353 #undef X
354     default:
355         return "UNKNOWN";
356     }
357 }
358
359 TaskCertify::Level TaskCertify::certTypeToLevel(
360     CertStoreId::Type type)
361 {
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;
375     }
376     return Level::UNKNOWN;
377 }
378
379 bool TaskCertify::isTizenWebApp() const
380 {
381     bool ret = false;
382     if (m_jobContext->m_installerContext.widgetConfig.webAppType.appType
383         == WrtDB::AppType::APP_TYPE_TIZENWEBAPP)
384     {
385         ret = true;
386     }
387
388     return ret;
389 }
390
391 void TaskCertify::stepVerifyUpdate()
392 {
393     LOGI("================ Step: <<VerifyUpdate>> ENTER ===============");
394
395     std::string oldAuthorCert;
396
397     int ret = 0;
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 : "";
409             }
410         }
411         pkgmgrinfo_pkginfo_destroy_certinfo(handle);
412     }
413
414     ValidationCore::CertificatePtr certPtr = m_jobContext->m_installerContext.widgetSecurity.getAuthorCertificatePtr();
415
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");
422         }
423     } else {
424         if (certPtr == NULL) {
425             ThrowMsg(Exceptions::NotMatchedCertification, "No author certificates");
426         }
427
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");
440         }
441     }
442     LOGI("================ Step: <<VerifyUpdate>> DONE ===============");
443 }
444
445 void TaskCertify::stepCertifyLevel()
446 {
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");
451     }
452     LOGI("================ Step: <<Certify Level>> DONE ================");
453 }
454
455 bool TaskCertify::checkConfigurationLevel(
456     TaskCertify::Level level)
457 {
458     if (!checkSettingLevel(level)) {
459         return false;
460     }
461     if (!checkNPRuntime(level)) {
462         return false;
463     }
464     if (!checkServiceLevel(level)) {
465         return false;
466     }
467     if (!checkBackgroundCategoryLevel(level)) {
468         return false;
469     }
470     return true;
471 }
472
473 bool TaskCertify::checkSettingLevel(
474     TaskCertify::Level level)
475 {
476     secureSettingMap data = {
477 #if ENABLE(PRE_LAUNCH)
478         {"pre-launching", Level::PLATFORM},
479 #endif
480         {"sound-mode", Level::PARTNER},
481         {"nodisplay", Level::PARTNER},
482         {"background-vibration", Level::PARTNER}
483     };
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());
489                 return false;
490             }
491         }
492     }
493     return true;
494 }
495
496 bool TaskCertify::checkNPRuntime(
497     TaskCertify::Level level)
498 {
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());
502
503     if (npruntime_dir == NULL) {
504         return true;
505     }
506
507     struct dirent dEntry;
508     struct dirent *dEntryResult;
509     int ret = 0;
510     bool result = true;
511     std::string suffix = ".so";
512
513     do {
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;
519
520             if (stat(fullName.c_str(), &statInfo) != 0) {
521                 closedir(npruntime_dir);
522                 return false;
523             }
524
525             if (S_ISDIR(statInfo.st_mode)) {
526                 if (("." == fileName) || (".." == fileName)) {
527                     continue;
528                 }
529             } else {
530                 if(fileName.size() >= suffix.size() &&
531                    fileName.compare(fileName.size() - suffix.size(), suffix.size(), suffix) == 0) {
532                     result = level >= Level::PARTNER;
533                     break;
534                 }
535             }
536         }
537     } while (dEntryResult != NULL && ret == 0);
538     closedir(npruntime_dir);
539     if (!result) {
540         _E("npruntime needs partner level");
541     }
542     return result;
543 }
544
545 bool TaskCertify::checkServiceLevel(
546     TaskCertify::Level level)
547 {
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());
551             return false;
552         }
553     }
554     return true;
555 }
556
557 bool TaskCertify::checkBackgroundCategoryLevel(
558     TaskCertify::Level level)
559 {
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());
564                 return false;
565             }
566         }
567     }
568     return true;
569 }
570
571 void TaskCertify::StartStep()
572 {
573     LOGI("--------- <TaskCertify> : START ----------");
574 }
575
576 void TaskCertify::EndStep()
577 {
578     LOGI("Step: <<CERTYFYING DONE>>");
579
580     m_jobContext->UpdateProgress(
581         InstallerContext::INSTALL_CERT_CHECK,
582         "Widget Certification Check Finished");
583
584     LOGI("--------- <TaskCertify> : END ----------");
585 }
586 } //namespace WidgetInstall
587 } //namespace Jobs
588