[Release] wrt-installer_0.1.16
[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 = m_contextData.locations->getTemporaryPackageDir() +
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 testCertificate = false;
239
240     bool complianceMode = GlobalDAOReadOnly::getComplianceMode();
241
242     for (; iter != signatureFiles.rend(); ++iter) {
243         LogInfo("Checking signature with id=" << iter->getFileNumber());
244         SignatureData data(widgetPath + iter->getFileName(),
245                            iter->getFileNumber());
246
247         Try {
248             SignatureReader xml;
249             xml.initialize(data, GlobalConfig::GetSignatureXmlSchema());
250             xml.read(data);
251
252             WrtSignatureValidator::AppType appType =
253                 WrtSignatureValidator::WAC20;
254
255             if (m_installContext.widgetConfig.webAppType ==
256                 APP_TYPE_TIZENWEBAPP)
257             {
258                 appType = WrtSignatureValidator::TIZEN;
259             }
260
261             WrtSignatureValidator::Result result;
262
263             WrtSignatureValidator validator(
264                     appType,
265                     !GlobalSettings::
266                     OCSPTestModeEnabled(),
267                     !GlobalSettings::
268                     CrlTestModeEnabled(),
269                     complianceMode);
270
271             result = validator.check(data, widgetPath);
272
273             if (m_contextData.widgetConfig.packagingType
274                 == WrtDB::PKG_TYPE_DIRECTORY_WEB_APP)
275             {
276                 // In directory installation mode, the validation is skipped.
277
278                 result = WrtSignatureValidator::SIGNATURE_VERIFIED;
279             }
280
281             if (result == WrtSignatureValidator::SIGNATURE_REVOKED) {
282                 LogWarning("Certificate is REVOKED");
283                 ThrowMsg(Exceptions::InvalidPackage,
284                          "Certificate is REVOKED");
285             }
286
287             if (result == WrtSignatureValidator::SIGNATURE_INVALID) {
288                 LogWarning("Signature is INVALID");
289                 // TODO change exception name
290                 ThrowMsg(Exceptions::InvalidPackage,
291                          "Invalid Package");
292             }
293
294             if (data.isAuthorSignature()) {
295                 if (result == WrtSignatureValidator::SIGNATURE_VERIFIED ||
296                     m_contextData.wacSecurity.isDistributorSigned())
297                 {
298                     processAuthorSignature(data);
299                 } else if (result ==
300                            WrtSignatureValidator::SIGNATURE_DISREGARD)
301                 {
302                     continue;
303                 }
304             } else {
305                 if (result == WrtSignatureValidator::SIGNATURE_DISREGARD) {
306                     continue;
307                 }
308                 // now signature _must_ be verified
309                 processDistributorSignature(data);
310             }
311
312             bool developerMode = GlobalDAOReadOnly::GetDeveloperMode();
313
314             std::string realMEID;
315             TapiHandle *tapiHandle = tel_init(NULL);
316             char *meid = tel_get_misc_me_sn_sync(tapiHandle);
317             if (meid) {
318                 realMEID = meid;
319                 free(meid);
320             }
321             tel_deinit(tapiHandle);
322
323             DeveloperModeValidator developerModeValidator(
324                 complianceMode,
325                 developerMode,
326                 GlobalDAOReadOnly::getComplianceFakeImei(),
327                 GlobalDAOReadOnly::getComplianceFakeMeid(),
328                 realMEID);
329
330             developerModeValidator.check(data);
331
332             testCertificate |=
333                 data.getStorageType().contains(CertStoreId::DEVELOPER);
334
335             if (testCertificate && !developerMode) {
336                 LogError("Widget signed by test certificate, "
337                          "but developer mode is off.");
338                 ThrowMsg(Exceptions::InvalidPackage,
339                          "Widget signed by test certificate, "
340                          "but developer mode is off.");
341             }
342             m_contextData.widgetConfig.isTestWidget = testCertificate;
343         } Catch(ParserSchemaException::Base) {
344             LogError("Error occured in ParserSchema.");
345             ReThrowMsg(Exceptions::InvalidPackage,
346                        "Error occured in ParserSchema.");
347         }
348         Catch(DeveloperModeValidator::Exception::Base) {
349             LogError("Cannot validate developer certificate.");
350             ReThrowMsg(Exceptions::InvalidPackage,
351                        "Cannot validate developer certificate.");
352         }
353     }
354
355     if (signatureFiles.empty()) {
356         LogInfo("No signature files has been found.");
357     }
358
359     LogInfo("================ Step: <<Signature>> DONE ================");
360
361     m_contextData.job->UpdateProgress(
362         InstallerContext::INSTALL_DIGSIG_CHECK,
363         "Widget Signature checked");
364 }
365
366 void TaskCertify::createInstallPopup(PopupType type, const std::string &label)
367 {
368     m_contextData.job->Pause();
369     if (m_popup) {
370         destroyPopup();
371     }
372     bool ret = createPopup();
373     if (ret) {
374         loadPopup(type, label);
375         showPopup();
376     }
377 }
378 void TaskCertify::StepDeletePopupWin()
379 {
380     destroyPopup();
381 }
382
383 void TaskCertify::stepWarningPopup()
384 {
385     LogInfo("Step:: <<Warning Popup>>");
386     // SP-2151: If widget is not recognized (OCSP status of any of certificates
387     //          it is signed with is not recognized) WRT must notify user that
388     //          widget cannot be installed as a trusted application, and let the
389     //          user decide whether it should be installed as an untrusted
390     //          application.
391     if (!m_contextData.wacSecurity.isDistributorSigned()) {
392         std::string label = UNTRUSTED_WIDGET +
393             LABEL_NEW_LINE_2 +
394             QUESTION;
395         createInstallPopup(PopupType::WIDGET_UNRECOGNIZED, label);
396     }
397 }
398
399 std::string TaskCertify::createAuthorWidgetInfo() const
400 {
401     std::string authorInfo;
402     if (m_contextData.wacSecurity.isRecognized()) {
403         //authorInfo += _("IDS_IM_WIDGET_RECOGNISED");
404         authorInfo += _("WIDGET RECOGNISED");
405     } else {
406         //authorInfo += _("IDS_IM_WIDGET_UNRECOGNISED");
407         authorInfo += _("WIDGET UNRECOGNISED");
408     }
409
410     authorInfo += LABEL_NEW_LINE_2;
411     ValidationCore::CertificatePtr authorCert =
412         m_contextData.wacSecurity.getAuthorCertificatePtr();
413     if (!!authorCert) {
414         DPL::Optional < DPL::String > organizationName =
415             authorCert->getOrganizationName();
416
417         //authorInfo += _("IDS_IM_WIDGET_AUTHOR_ORGANIZATION_NAME");
418         authorInfo += _("AUTHOR ORGANIZATION NAME");
419         authorInfo += LABEL_NEW_LINE;
420
421         if (!organizationName.IsNull()) {
422             authorInfo += DPL::ToUTF8String(*organizationName);
423         } else {
424             //authorInfo += _("IDS_IM_WIDGET_ORGANIZATION_UNKNOWN");
425             authorInfo += _("WIDGET ORGANIZATION UNKNOWN");
426         }
427
428         authorInfo += LABEL_NEW_LINE_2;
429
430         DPL::Optional < DPL::String > countryName =
431             authorCert->getCountryName();
432
433         //authorInfo += _("IDS_IM_WIDGET_COUNTRY_NAME");
434         authorInfo += _("WIDGET COUNTRY NAME");
435         authorInfo += LABEL_NEW_LINE;
436
437         if (!countryName.IsNull()) {
438             authorInfo += DPL::ToUTF8String(*countryName);
439         } else {
440             //authorInfo += _("IDS_IM_WIDGET_COUNTRY_UNKNOWN");
441             authorInfo += _("WIDGET COUNTRY UNKNOWN");
442         }
443     } else {
444         authorInfo +=
445             //_("IDS_IM_WIDGET_DOES_NOT_CONTAIN_RECOGNIZED_AUTHOR_SIGNATURE");
446             _("Widget does not contain recognized author signature");
447     }
448     return authorInfo;
449 }
450
451 void TaskCertify::stepAuthorInfoPopup()
452 {
453     LogInfo("Step:: <<Author Popup Information>>");
454     std::string label
455         = createAuthorWidgetInfo() + LABEL_NEW_LINE_2 + QUESTION;
456     createInstallPopup(PopupType::WIDGET_AUTHOR_INFO, label);
457 }
458
459 void TaskCertify::stepFinalize()
460 {
461     LogInfo("Step: <<CERTYFYING DONE>>");
462
463     m_contextData.job->UpdateProgress(
464         InstallerContext::INSTALL_CERT_CHECK,
465         "Widget Certification Check Finished");
466 }
467
468 void TaskCertify::stepWarningPopupAnswer()
469 {
470     LogInfo("Step: <<Warning Popup Answer>>");
471     if (false == m_contextData.wacSecurity.isDistributorSigned() &&
472         WRT_POPUP_BUTTON_CANCEL == m_installCancel)
473     {
474         LogWarning("User does not agreed to install unsigned widgets!");
475         m_installCancel = WRT_POPUP_BUTTON;
476         destroyPopup();
477         ThrowMsg(Exceptions::NotAllowed, "Widget not allowed");
478     }
479 }
480
481 void TaskCertify::stepAuthorInfoPopupAnswer()
482 {
483     LogInfo("Step: <<Author Info Popup Answer>>");
484     if (WRT_POPUP_BUTTON_CANCEL == m_installCancel) {
485         LogWarning("User does not agreed to install widget!");
486         m_installCancel = WRT_POPUP_BUTTON;
487         destroyPopup();
488         ThrowMsg(Exceptions::NotAllowed, "Widget not allowed");
489     }
490 }
491
492 bool TaskCertify::isTizenWebApp() const
493 {
494     bool ret = FALSE;
495     if (m_installContext.widgetConfig.webAppType.appType
496         == WrtDB::AppType::APP_TYPE_TIZENWEBAPP)
497     {
498         ret = TRUE;
499     }
500
501     return ret;
502 }
503
504 void TaskCertify::stepVerifyUpdate()
505 {
506     LogInfo("Step: <<Check Update>>");
507     CertificatePtr newCertificate =
508         m_contextData.wacSecurity.getAuthorCertificatePtr();
509     CertificatePtr oldCertificate =
510         getOldAuthorSignerCertificate(m_installContext.widgetConfig.tzAppid);
511
512     if (!!newCertificate && !!oldCertificate) {
513         if (0 != newCertificate->getBase64().compare(oldCertificate->getBase64())) {
514             LogDebug("old widget's author signer certificate : " <<
515                     oldCertificate->getBase64());
516             LogDebug("new widget's author signer certificate : " <<
517                     newCertificate->getBase64());
518             ThrowMsg(Exceptions::InvalidPackage,
519                     "Author signer certificates doesn't match \
520                     between old widget and installing widget");
521         }
522     } else {
523         if (!(NULL == newCertificate.Get() && NULL == oldCertificate.Get())) {
524             ThrowMsg(Exceptions::InvalidPackage,
525                     "Author signer certificates doesn't match \
526                     between old widget and installing widget");
527         }
528     }
529 }
530 } //namespace WidgetInstall
531 } //namespace Jobs
532