Fixed encryption logic
[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 <dpl/assert.h>
27 #include <appcore-common.h> //TODO is it necessary here?
28 #include <pcrecpp.h>
29
30 //WRT INCLUDES
31 #include <widget_install/task_certify.h>
32 #include <widget_install/job_widget_install.h>
33 #include <widget_install/widget_install_errors.h>
34 #include <widget_install/widget_install_context.h>
35 #include <dpl/log/log.h>
36 #include <dpl/wrt-dao-ro/global_config.h>
37 #include "wac_widget_id.h"
38
39 #include <vcore/Certificate.h>
40 #include <vcore/SignatureReader.h>
41 #include <vcore/SignatureFinder.h>
42 #include <vcore/WrtSignatureValidator.h>
43 #include <vcore/DeveloperModeValidator.h>
44 #include <dpl/utils/wrt_global_settings.h>
45 #include <dpl/wrt-dao-ro/global_dao_read_only.h>
46
47 #include <ITapiModem.h>
48 #include <tapi_common.h>
49
50 using namespace ValidationCore;
51 using namespace WrtDB;
52
53 namespace {
54 const std::string LABEL_NEW_LINE = "<br>";
55 const std::string LABEL_NEW_LINE_2 = "<br><br>";
56 const std::string UNTRUSTED_WIDGET = "It is an Untrusted Widget";
57 const char *QUESTION = "Do you wanto to install?";
58
59 WidgetCertificateData toWidgetCertificateData(const SignatureData &data,
60                                               bool root)
61 {
62     WidgetCertificateData result;
63
64     result.chainId = data.getSignatureNumber();
65
66     result.owner = data.isAuthorSignature() ?
67         WidgetCertificateData::AUTHOR : WidgetCertificateData::DISTRIBUTOR;
68
69     result.type = root ?
70         WidgetCertificateData::ROOT : WidgetCertificateData::ENDENTITY;
71
72     CertificatePtr certificate;
73
74     if (root) {
75         certificate = data.getRootCaCertificatePtr();
76     } else {
77         certificate = data.getEndEntityCertificatePtr();
78     }
79
80     Assert(certificate && !certificate->getCommonName().IsNull() &&
81            "CommonName is Null");
82
83     result.strCommonName = *certificate->getCommonName();
84
85     result.strMD5Fingerprint = std::string("md5 ") +
86         Certificate::FingerprintToColonHex(
87             certificate->getFingerprint(Certificate::FINGERPRINT_MD5));
88
89     result.strSHA1Fingerprint = std::string("sha-1 ") +
90         Certificate::FingerprintToColonHex(
91             certificate->getFingerprint(Certificate::FINGERPRINT_SHA1));
92
93     return result;
94 }
95
96 CertificatePtr getOldAuthorSignerCertificate(DPL::String appid)
97 {
98     WidgetDAOReadOnly dao(appid);
99     CertificateChainList chainList = dao.getWidgetCertificate(SIGNATURE_AUTHOR);
100
101     FOREACH(it, chainList)
102     {
103         ValidationCore::CertificateCollection chain;
104         if (false == chain.load(*it)) {
105             LogError("Chain is broken");
106         }
107
108         if (!chain.sort()) {
109             LogError("Chain failed at sorting");
110         }
111
112         ValidationCore::CertificateList list = chain.getCertificateList();
113
114         FOREACH(cert, list)
115         {
116             if (!(*cert)->isRootCert() && !(*cert)->isCA()) {
117                 return *cert;
118             }
119         }
120     }
121     return CertificatePtr(NULL);
122 }
123 } // namespace anonymous
124
125 namespace Jobs {
126 namespace WidgetInstall {
127 TaskCertify::TaskCertify(InstallerContext &inCont) :
128     DPL::TaskDecl<TaskCertify>(this),
129     WidgetInstallPopup(inCont),
130     m_contextData(inCont)
131 {
132     AddStep(&TaskCertify::stepSignature);
133     // certi comparison determines whether the update.
134     if (true == m_contextData.existingWidgetInfo.isExist) {
135         AddStep(&TaskCertify::stepVerifyUpdate);
136     }
137
138     // Block until fixed popup issues
139     if (!GlobalSettings::PopupsTestModeEnabled()
140         && !m_installContext.m_quiet && !isTizenWebApp())
141     {
142         AddStep(&TaskCertify::stepWarningPopup);
143         AddStep(&TaskCertify::stepWarningPopupAnswer);
144         AddStep(&TaskCertify::stepAuthorInfoPopup);
145         AddStep(&TaskCertify::stepAuthorInfoPopupAnswer);
146         AddStep(&TaskCertify::StepDeletePopupWin);
147     }
148     AddStep(&TaskCertify::stepFinalize);
149 }
150
151 void TaskCertify::processDistributorSignature(const SignatureData &data,
152                                               bool first)
153 {
154     // this signature is verified -
155     // no point in check domain WAC_ROOT and WAC_RECOGNIZED
156     m_contextData.wacSecurity.setDistributorSigned(true);
157
158     if (data.getStorageType().contains(CertStoreId::WAC_ROOT)) {
159         m_contextData.wacSecurity.setWacSigned(true);
160     }
161
162     CertificateCollection collection;
163     collection.load(data.getCertList());
164     Assert(collection.sort() &&
165            "Certificate collection can't sort");
166
167     Assert(collection.isChain() &&
168            "Certificate collection is not able to create chain. "
169            "It is not possible to verify this signature.");
170
171     m_contextData.wacSecurity.getCertificateChainListRef().push_back(
172         collection);
173
174     if (first) {
175         m_contextData.wacSecurity.getCertificateListRef().push_back(
176             toWidgetCertificateData(data, true));
177         m_contextData.wacSecurity.getCertificateListRef().push_back(
178             toWidgetCertificateData(data, false));
179     }
180 }
181
182 void TaskCertify::processAuthorSignature(const SignatureData &data)
183 {
184     using namespace ValidationCore;
185     LogInfo("DNS Identity match!");
186     // this signature is verified or widget is distributor signed
187     m_contextData.wacSecurity.setAuthorCertificatePtr(data.getEndEntityCertificatePtr());
188     CertificatePtr test = m_contextData.wacSecurity.getAuthorCertificatePtr();
189
190     m_contextData.wacSecurity.getCertificateListRef().push_back(
191         toWidgetCertificateData(data, true));
192     m_contextData.wacSecurity.getCertificateListRef().push_back(
193         toWidgetCertificateData(data, false));
194
195     // match widget_id with one from dns identity set
196     WacWidgetId widgetId(m_contextData.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     Assert(collection.isChain() &&
206            "Certificate collection is not able to create chain. "
207            "It is not possible to verify this signature.");
208
209     m_contextData.wacSecurity.getAuthorsCertificateChainListRef().push_back(
210         collection);
211
212     FOREACH(it, dnsIdentity){
213         if (widgetId.matchHost(*it)) {
214             m_contextData.wacSecurity.setRecognized(true);
215             return;
216         }
217     }
218 }
219
220 void TaskCertify::stepSignature()
221 {
222     LogInfo("================ Step: <<Signature>> ENTER ===============");
223
224     std::string widgetPath = m_contextData.locations->getTemporaryRootDir() +
225         "/";
226
227     SignatureFileInfoSet signatureFiles;
228     SignatureFinder signatureFinder(widgetPath);
229     if (SignatureFinder::NO_ERROR != signatureFinder.find(signatureFiles)) {
230         LogError("Error in Signature Finder");
231         ThrowMsg(Exceptions::InvalidPackage,
232                  "Error openig temporary widget directory");
233     }
234
235     SignatureFileInfoSet::reverse_iterator iter = signatureFiles.rbegin();
236     LogInfo("Number of signatures: " << signatureFiles.size());
237
238     bool firstDistributorSignature = true;
239     bool testCertificate = false;
240
241     bool complianceMode = GlobalDAOReadOnly::getComplianceMode();
242
243     for (; iter != signatureFiles.rend(); ++iter) {
244         LogInfo("Checking signature with id=" << iter->getFileNumber());
245         SignatureData data(widgetPath + iter->getFileName(),
246                            iter->getFileNumber());
247
248         Try {
249             SignatureReader xml;
250             xml.initialize(data, GlobalConfig::GetSignatureXmlSchema());
251             xml.read(data);
252
253             WrtSignatureValidator::AppType appType =
254                 WrtSignatureValidator::WAC20;
255
256             if (m_installContext.widgetConfig.webAppType ==
257                 APP_TYPE_TIZENWEBAPP)
258             {
259                 appType = WrtSignatureValidator::TIZEN;
260             }
261
262             WrtSignatureValidator::Result result;
263
264             WrtSignatureValidator validator(
265                     appType,
266                     !GlobalSettings::
267                     OCSPTestModeEnabled(),
268                     !GlobalSettings::
269                     CrlTestModeEnabled(),
270                     complianceMode);
271
272             result = validator.check(data, widgetPath);
273
274             if (m_contextData.widgetConfig.packagingType
275                 == WrtDB::PKG_TYPE_DIRECTORY_WEB_APP)
276             {
277                 // In directory installation mode, the validation is skipped.
278
279                 result = WrtSignatureValidator::SIGNATURE_VERIFIED;
280             }
281
282             if (result == WrtSignatureValidator::SIGNATURE_REVOKED) {
283                 LogWarning("Certificate is REVOKED");
284                 ThrowMsg(Exceptions::InvalidPackage,
285                          "Certificate is REVOKED");
286             }
287
288             if (result == WrtSignatureValidator::SIGNATURE_INVALID) {
289                 LogWarning("Signature is INVALID");
290                 // TODO change exception name
291                 ThrowMsg(Exceptions::InvalidPackage,
292                          "Invalid Package");
293             }
294
295             if (data.isAuthorSignature()) {
296                 if (result == WrtSignatureValidator::SIGNATURE_VERIFIED ||
297                     m_contextData.wacSecurity.isDistributorSigned())
298                 {
299                     processAuthorSignature(data);
300                 } else if (result ==
301                            WrtSignatureValidator::SIGNATURE_DISREGARD)
302                 {
303                     continue;
304                 }
305             } else {
306                 if (result == WrtSignatureValidator::SIGNATURE_DISREGARD) {
307                     continue;
308                 }
309                 // now signature _must_ be verified
310                 processDistributorSignature(data, firstDistributorSignature);
311                 firstDistributorSignature = false;
312             }
313
314             bool developerMode = GlobalDAOReadOnly::GetDeveloperMode();
315
316             std::string realMEID;
317             TapiHandle *tapiHandle = tel_init(NULL);
318             char *meid = tel_get_misc_me_sn_sync(tapiHandle);
319             if (meid) {
320                 realMEID = meid;
321                 free(meid);
322             }
323             tel_deinit(tapiHandle);
324
325             DeveloperModeValidator developerModeValidator(
326                 complianceMode,
327                 developerMode,
328                 GlobalDAOReadOnly::getComplianceFakeImei(),
329                 GlobalDAOReadOnly::getComplianceFakeMeid(),
330                 realMEID);
331
332             developerModeValidator.check(data);
333
334             testCertificate |=
335                 data.getStorageType().contains(CertStoreId::DEVELOPER);
336
337             if (testCertificate && !developerMode) {
338                 LogError("Widget signed by test certificate, "
339                          "but developer mode is off.");
340                 ThrowMsg(Exceptions::InvalidPackage,
341                          "Widget signed by test certificate, "
342                          "but developer mode is off.");
343             }
344             m_contextData.widgetConfig.isTestWidget = testCertificate;
345         } Catch(ParserSchemaException::Base) {
346             LogError("Error occured in ParserSchema.");
347             ReThrowMsg(Exceptions::InvalidPackage,
348                        "Error occured in ParserSchema.");
349         }
350         Catch(DeveloperModeValidator::Exception::Base) {
351             LogError("Cannot validate developer certificate.");
352             ReThrowMsg(Exceptions::InvalidPackage,
353                        "Cannot validate developer certificate.");
354         }
355     }
356
357     if (signatureFiles.empty()) {
358         LogInfo("No signature files has been found.");
359     }
360
361     LogInfo("================ Step: <<Signature>> DONE ================");
362
363     m_contextData.job->UpdateProgress(
364         InstallerContext::INSTALL_DIGSIG_CHECK,
365         "Widget Signature checked");
366 }
367
368 void TaskCertify::createInstallPopup(PopupType type, const std::string &label)
369 {
370     m_contextData.job->Pause();
371     if (m_popup) {
372         destroyPopup();
373     }
374     bool ret = createPopup();
375     if (ret) {
376         loadPopup(type, label);
377         showPopup();
378     }
379 }
380 void TaskCertify::StepDeletePopupWin()
381 {
382     destroyPopup();
383 }
384
385 void TaskCertify::stepWarningPopup()
386 {
387     LogInfo("Step:: <<Warning Popup>>");
388     // SP-2151: If widget is not recognized (OCSP status of any of certificates
389     //          it is signed with is not recognized) WRT must notify user that
390     //          widget cannot be installed as a trusted application, and let the
391     //          user decide whether it should be installed as an untrusted
392     //          application.
393     if (!m_contextData.wacSecurity.isDistributorSigned()) {
394         std::string label = UNTRUSTED_WIDGET +
395             LABEL_NEW_LINE_2 +
396             QUESTION;
397         createInstallPopup(PopupType::WIDGET_UNRECOGNIZED, label);
398     }
399 }
400
401 std::string TaskCertify::createAuthorWidgetInfo() const
402 {
403     std::string authorInfo;
404     if (m_contextData.wacSecurity.isRecognized()) {
405         //authorInfo += _("IDS_IM_WIDGET_RECOGNISED");
406         authorInfo += _("WIDGET RECOGNISED");
407     } else {
408         //authorInfo += _("IDS_IM_WIDGET_UNRECOGNISED");
409         authorInfo += _("WIDGET UNRECOGNISED");
410     }
411
412     authorInfo += LABEL_NEW_LINE_2;
413     ValidationCore::CertificatePtr authorCert =
414         m_contextData.wacSecurity.getAuthorCertificatePtr();
415     if (!!authorCert) {
416         DPL::Optional < DPL::String > organizationName =
417             authorCert->getOrganizationName();
418
419         //authorInfo += _("IDS_IM_WIDGET_AUTHOR_ORGANIZATION_NAME");
420         authorInfo += _("AUTHOR ORGANIZATION NAME");
421         authorInfo += LABEL_NEW_LINE;
422
423         if (!organizationName.IsNull()) {
424             authorInfo += DPL::ToUTF8String(*organizationName);
425         } else {
426             //authorInfo += _("IDS_IM_WIDGET_ORGANIZATION_UNKNOWN");
427             authorInfo += _("WIDGET ORGANIZATION UNKNOWN");
428         }
429
430         authorInfo += LABEL_NEW_LINE_2;
431
432         DPL::Optional < DPL::String > countryName =
433             authorCert->getCountryName();
434
435         //authorInfo += _("IDS_IM_WIDGET_COUNTRY_NAME");
436         authorInfo += _("WIDGET COUNTRY NAME");
437         authorInfo += LABEL_NEW_LINE;
438
439         if (!countryName.IsNull()) {
440             authorInfo += DPL::ToUTF8String(*countryName);
441         } else {
442             //authorInfo += _("IDS_IM_WIDGET_COUNTRY_UNKNOWN");
443             authorInfo += _("WIDGET COUNTRY UNKNOWN");
444         }
445     } else {
446         authorInfo +=
447             //_("IDS_IM_WIDGET_DOES_NOT_CONTAIN_RECOGNIZED_AUTHOR_SIGNATURE");
448             _("Widget does not contain recognized author signature");
449     }
450     return authorInfo;
451 }
452
453 void TaskCertify::stepAuthorInfoPopup()
454 {
455     LogInfo("Step:: <<Author Popup Information>>");
456     std::string label
457         = createAuthorWidgetInfo() + LABEL_NEW_LINE_2 + QUESTION;
458     createInstallPopup(PopupType::WIDGET_AUTHOR_INFO, label);
459 }
460
461 void TaskCertify::stepFinalize()
462 {
463     LogInfo("Step: <<CERTYFYING DONE>>");
464
465     m_contextData.job->UpdateProgress(
466         InstallerContext::INSTALL_CERT_CHECK,
467         "Widget Certification Check Finished");
468 }
469
470 void TaskCertify::stepWarningPopupAnswer()
471 {
472     LogInfo("Step: <<Warning Popup Answer>>");
473     if (false == m_contextData.wacSecurity.isDistributorSigned() &&
474         WRT_POPUP_BUTTON_CANCEL == m_installCancel)
475     {
476         LogWarning("User does not agreed to install unsigned widgets!");
477         m_installCancel = WRT_POPUP_BUTTON;
478         destroyPopup();
479         ThrowMsg(Exceptions::NotAllowed, "Widget not allowed");
480     }
481 }
482
483 void TaskCertify::stepAuthorInfoPopupAnswer()
484 {
485     LogInfo("Step: <<Author Info Popup Answer>>");
486     if (WRT_POPUP_BUTTON_CANCEL == m_installCancel) {
487         LogWarning("User does not agreed to install widget!");
488         m_installCancel = WRT_POPUP_BUTTON;
489         destroyPopup();
490         ThrowMsg(Exceptions::NotAllowed, "Widget not allowed");
491     }
492 }
493
494 bool TaskCertify::isTizenWebApp() const
495 {
496     bool ret = FALSE;
497     if (m_installContext.widgetConfig.webAppType.appType
498         == WrtDB::AppType::APP_TYPE_TIZENWEBAPP)
499     {
500         ret = TRUE;
501     }
502
503     return ret;
504 }
505
506 void TaskCertify::stepVerifyUpdate()
507 {
508     LogInfo("Step: <<Check Update>>");
509     CertificatePtr newCertificate =
510         m_contextData.wacSecurity.getAuthorCertificatePtr();
511     CertificatePtr oldCertificate =
512         getOldAuthorSignerCertificate(m_installContext.widgetConfig.tzAppid);
513
514     if (!!newCertificate && !!oldCertificate) {
515         if (0 != newCertificate->getBase64().compare(oldCertificate->getBase64())) {
516             LogDebug("old widget's author signer certificate : " <<
517                     oldCertificate->getBase64());
518             LogDebug("new widget's author signer certificate : " <<
519                     newCertificate->getBase64());
520             ThrowMsg(Exceptions::InvalidPackage,
521                     "Author signer certificates doesn't match \
522                     between old widget and installing widget");
523         }
524     } else {
525         if (!(NULL == newCertificate.Get() && NULL == oldCertificate.Get())) {
526             ThrowMsg(Exceptions::InvalidPackage,
527                     "Author signer certificates doesn't match \
528                     between old widget and installing widget");
529         }
530     }
531 }
532 } //namespace WidgetInstall
533 } //namespace Jobs
534