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