[Release] wrt-installer_0.1.20
[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             TapiHandle *tapiHandle = tel_init(NULL);
322             char *meid = tel_get_misc_me_sn_sync(tapiHandle);
323             if (meid) {
324                 realMEID = meid;
325                 free(meid);
326             }
327             tel_deinit(tapiHandle);
328
329             DeveloperModeValidator developerModeValidator(
330                 complianceMode,
331                 developerMode,
332                 GlobalDAOReadOnly::getComplianceFakeImei(),
333                 GlobalDAOReadOnly::getComplianceFakeMeid(),
334                 realMEID);
335
336             developerModeValidator.check(data);
337
338             testCertificate |=
339                 data.getStorageType().contains(CertStoreId::DEVELOPER);
340
341             if (testCertificate && !developerMode) {
342                 LogError("Widget signed by test certificate, "
343                          "but developer mode is off.");
344                 ThrowMsg(Exceptions::InvalidPackage,
345                          "Widget signed by test certificate, "
346                          "but developer mode is off.");
347             }
348             m_contextData.widgetConfig.isTestWidget = testCertificate;
349         } Catch(ParserSchemaException::Base) {
350             LogError("Error occured in ParserSchema.");
351             ReThrowMsg(Exceptions::InvalidPackage,
352                        "Error occured in ParserSchema.");
353         }
354         Catch(DeveloperModeValidator::Exception::Base) {
355             LogError("Cannot validate developer certificate.");
356             ReThrowMsg(Exceptions::InvalidPackage,
357                        "Cannot validate developer certificate.");
358         }
359     }
360
361     if (signatureFiles.empty()) {
362         LogInfo("No signature files has been found.");
363     }
364
365     LogInfo("================ Step: <<Signature>> DONE ================");
366
367     m_contextData.job->UpdateProgress(
368         InstallerContext::INSTALL_DIGSIG_CHECK,
369         "Widget Signature checked");
370 }
371
372 void TaskCertify::createInstallPopup(PopupType type, const std::string &label)
373 {
374     m_contextData.job->Pause();
375     if (m_popup) {
376         destroyPopup();
377     }
378     bool ret = createPopup();
379     if (ret) {
380         loadPopup(type, label);
381         showPopup();
382     }
383 }
384 void TaskCertify::StepDeletePopupWin()
385 {
386     destroyPopup();
387 }
388
389 void TaskCertify::stepWarningPopup()
390 {
391     LogInfo("Step:: <<Warning Popup>>");
392     // SP-2151: If widget is not recognized (OCSP status of any of certificates
393     //          it is signed with is not recognized) WRT must notify user that
394     //          widget cannot be installed as a trusted application, and let the
395     //          user decide whether it should be installed as an untrusted
396     //          application.
397     if (!m_contextData.wacSecurity.isDistributorSigned()) {
398         std::string label = UNTRUSTED_WIDGET +
399             LABEL_NEW_LINE_2 +
400             QUESTION;
401         createInstallPopup(PopupType::WIDGET_UNRECOGNIZED, label);
402     }
403 }
404
405 std::string TaskCertify::createAuthorWidgetInfo() const
406 {
407     std::string authorInfo;
408     if (m_contextData.wacSecurity.isRecognized()) {
409         //authorInfo += _("IDS_IM_WIDGET_RECOGNISED");
410         authorInfo += _("WIDGET RECOGNISED");
411     } else {
412         //authorInfo += _("IDS_IM_WIDGET_UNRECOGNISED");
413         authorInfo += _("WIDGET UNRECOGNISED");
414     }
415
416     authorInfo += LABEL_NEW_LINE_2;
417     ValidationCore::CertificatePtr authorCert =
418         m_contextData.wacSecurity.getAuthorCertificatePtr();
419     if (!!authorCert) {
420         DPL::Optional < DPL::String > organizationName =
421             authorCert->getOrganizationName();
422
423         //authorInfo += _("IDS_IM_WIDGET_AUTHOR_ORGANIZATION_NAME");
424         authorInfo += _("AUTHOR ORGANIZATION NAME");
425         authorInfo += LABEL_NEW_LINE;
426
427         if (!organizationName.IsNull()) {
428             authorInfo += DPL::ToUTF8String(*organizationName);
429         } else {
430             //authorInfo += _("IDS_IM_WIDGET_ORGANIZATION_UNKNOWN");
431             authorInfo += _("WIDGET ORGANIZATION UNKNOWN");
432         }
433
434         authorInfo += LABEL_NEW_LINE_2;
435
436         DPL::Optional < DPL::String > countryName =
437             authorCert->getCountryName();
438
439         //authorInfo += _("IDS_IM_WIDGET_COUNTRY_NAME");
440         authorInfo += _("WIDGET COUNTRY NAME");
441         authorInfo += LABEL_NEW_LINE;
442
443         if (!countryName.IsNull()) {
444             authorInfo += DPL::ToUTF8String(*countryName);
445         } else {
446             //authorInfo += _("IDS_IM_WIDGET_COUNTRY_UNKNOWN");
447             authorInfo += _("WIDGET COUNTRY UNKNOWN");
448         }
449     } else {
450         authorInfo +=
451             //_("IDS_IM_WIDGET_DOES_NOT_CONTAIN_RECOGNIZED_AUTHOR_SIGNATURE");
452             _("Widget does not contain recognized author signature");
453     }
454     return authorInfo;
455 }
456
457 void TaskCertify::stepAuthorInfoPopup()
458 {
459     LogInfo("Step:: <<Author Popup Information>>");
460     std::string label
461         = createAuthorWidgetInfo() + LABEL_NEW_LINE_2 + QUESTION;
462     createInstallPopup(PopupType::WIDGET_AUTHOR_INFO, label);
463 }
464
465 void TaskCertify::stepFinalize()
466 {
467     LogInfo("Step: <<CERTYFYING DONE>>");
468
469     m_contextData.job->UpdateProgress(
470         InstallerContext::INSTALL_CERT_CHECK,
471         "Widget Certification Check Finished");
472 }
473
474 void TaskCertify::stepWarningPopupAnswer()
475 {
476     LogInfo("Step: <<Warning Popup Answer>>");
477     if (false == m_contextData.wacSecurity.isDistributorSigned() &&
478         WRT_POPUP_BUTTON_CANCEL == m_installCancel)
479     {
480         LogWarning("User does not agreed to install unsigned widgets!");
481         m_installCancel = WRT_POPUP_BUTTON;
482         destroyPopup();
483         ThrowMsg(Exceptions::NotAllowed, "Widget not allowed");
484     }
485 }
486
487 void TaskCertify::stepAuthorInfoPopupAnswer()
488 {
489     LogInfo("Step: <<Author Info Popup Answer>>");
490     if (WRT_POPUP_BUTTON_CANCEL == m_installCancel) {
491         LogWarning("User does not agreed to install widget!");
492         m_installCancel = WRT_POPUP_BUTTON;
493         destroyPopup();
494         ThrowMsg(Exceptions::NotAllowed, "Widget not allowed");
495     }
496 }
497
498 bool TaskCertify::isTizenWebApp() const
499 {
500     bool ret = FALSE;
501     if (m_installContext.widgetConfig.webAppType.appType
502         == WrtDB::AppType::APP_TYPE_TIZENWEBAPP)
503     {
504         ret = TRUE;
505     }
506
507     return ret;
508 }
509
510 void TaskCertify::stepVerifyUpdate()
511 {
512     LogInfo("Step: <<Check Update>>");
513     CertificatePtr newCertificate =
514         m_contextData.wacSecurity.getAuthorCertificatePtr();
515     CertificatePtr oldCertificate =
516         getOldAuthorSignerCertificate(m_installContext.widgetConfig.tzAppid);
517
518     if (!!newCertificate && !!oldCertificate) {
519         if (0 != newCertificate->getBase64().compare(oldCertificate->getBase64())) {
520             LogDebug("old widget's author signer certificate : " <<
521                     oldCertificate->getBase64());
522             LogDebug("new widget's author signer certificate : " <<
523                     newCertificate->getBase64());
524             ThrowMsg(Exceptions::InvalidPackage,
525                     "Author signer certificates doesn't match \
526                     between old widget and installing widget");
527         }
528     } else {
529         if (!(NULL == newCertificate.Get() && NULL == oldCertificate.Get())) {
530             ThrowMsg(Exceptions::InvalidPackage,
531                     "Author signer certificates doesn't match \
532                     between old widget and installing widget");
533         }
534     }
535 }
536 } //namespace WidgetInstall
537 } //namespace Jobs
538