Remove GUID and refactoring update mode
[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 <dpl/utils/wrt_global_settings.h>
44 #include <dpl/wrt-dao-ro/global_dao_read_only.h>
45
46 #include <ITapiModem.h>
47 #include <tapi_common.h>
48
49 using namespace ValidationCore;
50 using namespace WrtDB;
51
52 namespace {
53 const std::string LABEL_NEW_LINE = "<br>";
54 const std::string LABEL_NEW_LINE_2 = "<br><br>";
55 const std::string UNTRUSTED_WIDGET = "It is an Untrusted Widget";
56 const char *QUESTION = "Do you wanto to install?";
57
58 WidgetCertificateData toWidgetCertificateData(const SignatureData &data,
59                                               bool root)
60 {
61     WidgetCertificateData result;
62
63     result.chainId = data.getSignatureNumber();
64     LogDebug("result.chainId : " << result.chainId);
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.isUpdateMode) {
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 {
153     // this signature is verified -
154     // no point in check domain WAC_ROOT and WAC_RECOGNIZED
155     m_contextData.wacSecurity.setDistributorSigned(true);
156
157     CertificateCollection collection;
158     collection.load(data.getCertList());
159     Assert(collection.sort() &&
160            "Certificate collection can't sort");
161
162     Assert(collection.isChain() &&
163            "Certificate collection is not able to create chain. "
164            "It is not possible to verify this signature.");
165
166     m_contextData.wacSecurity.getCertificateChainListRef().push_back(
167         collection);
168
169     if (data.getSignatureNumber() == 1) {
170         m_contextData.wacSecurity.getCertificateListRef().push_back(
171             toWidgetCertificateData(data, true));
172         m_contextData.wacSecurity.getCertificateListRef().push_back(
173             toWidgetCertificateData(data, false));
174     }
175 }
176
177 void TaskCertify::processAuthorSignature(const SignatureData &data)
178 {
179     using namespace ValidationCore;
180     LogInfo("DNS Identity match!");
181     // this signature is verified or widget is distributor signed
182     m_contextData.wacSecurity.setAuthorCertificatePtr(data.getEndEntityCertificatePtr());
183     CertificatePtr test = m_contextData.wacSecurity.getAuthorCertificatePtr();
184
185     m_contextData.wacSecurity.getCertificateListRef().push_back(
186         toWidgetCertificateData(data, true));
187     m_contextData.wacSecurity.getCertificateListRef().push_back(
188         toWidgetCertificateData(data, false));
189
190     // match widget_id with one from dns identity set
191     WacWidgetId widgetId(m_contextData.widgetConfig.configInfo.widget_id);
192
193     CertificatePtr cert = data.getEndEntityCertificatePtr();
194     Assert(cert);
195     Certificate::AltNameSet dnsIdentity = cert->getAlternativeNameDNS();
196
197     CertificateCollection collection;
198     collection.load(data.getCertList());
199     collection.sort();
200     Assert(collection.isChain() &&
201            "Certificate collection is not able to create chain. "
202            "It is not possible to verify this signature.");
203
204     m_contextData.wacSecurity.getAuthorsCertificateChainListRef().push_back(
205         collection);
206
207     FOREACH(it, dnsIdentity){
208         if (widgetId.matchHost(*it)) {
209             m_contextData.wacSecurity.setRecognized(true);
210             return;
211         }
212     }
213 }
214
215 void TaskCertify::stepSignature()
216 {
217     LogInfo("================ Step: <<Signature>> ENTER ===============");
218
219     std::string widgetPath;
220     if (m_contextData.widgetConfig.packagingType ==
221         WrtDB::PKG_TYPE_DIRECTORY_WEB_APP)
222     {
223         widgetPath = m_contextData.locations->getSourceDir() + "/";
224     } else {
225         widgetPath = m_contextData.locations->getTemporaryPackageDir() + "/";
226     }
227
228     SignatureFileInfoSet signatureFiles;
229     SignatureFinder signatureFinder(widgetPath);
230     if (SignatureFinder::NO_ERROR != signatureFinder.find(signatureFiles)) {
231         LogError("Error in Signature Finder");
232         ThrowMsg(Exceptions::SignatureNotFound,
233                  "Error openig temporary widget directory");
234     }
235
236     SignatureFileInfoSet::reverse_iterator iter = signatureFiles.rbegin();
237     LogInfo("Number of signatures: " << signatureFiles.size());
238
239     bool complianceMode = GlobalDAOReadOnly::getComplianceMode();
240
241     for (; iter != signatureFiles.rend(); ++iter) {
242         LogInfo("Checking signature with id=" << iter->getFileNumber());
243         SignatureData data(widgetPath + iter->getFileName(),
244                            iter->getFileNumber());
245
246         Try {
247             SignatureReader xml;
248             xml.initialize(data, GlobalConfig::GetSignatureXmlSchema());
249             xml.read(data);
250
251             WrtSignatureValidator::AppType appType =
252                 WrtSignatureValidator::WAC20;
253
254             if (m_installContext.widgetConfig.webAppType ==
255                 APP_TYPE_TIZENWEBAPP)
256             {
257                 appType = WrtSignatureValidator::TIZEN;
258             }
259
260             WrtSignatureValidator::Result result;
261
262             WrtSignatureValidator validator(
263                     appType,
264                     !GlobalSettings::
265                     OCSPTestModeEnabled(),
266                     !GlobalSettings::
267                     CrlTestModeEnabled(),
268                     complianceMode);
269
270             result = validator.check(data, widgetPath);
271
272             if (m_contextData.widgetConfig.packagingType
273                 == WrtDB::PKG_TYPE_DIRECTORY_WEB_APP)
274             {
275                 // In directory installation mode, the validation is skipped.
276
277                 result = WrtSignatureValidator::SIGNATURE_VERIFIED;
278             }
279
280             if (result == WrtSignatureValidator::SIGNATURE_REVOKED) {
281                 LogWarning("Certificate is REVOKED");
282                 ThrowMsg(Exceptions::CertificateExpired,
283                          "Certificate is REVOKED");
284             }
285
286             if (result == WrtSignatureValidator::SIGNATURE_INVALID) {
287                 LogWarning("Signature is INVALID");
288                 // TODO change exception name
289                 ThrowMsg(Exceptions::SignatureInvalid,
290                          "Invalid Package");
291             }
292
293             if (data.isAuthorSignature()) {
294                 if (result == WrtSignatureValidator::SIGNATURE_VERIFIED ||
295                     m_contextData.wacSecurity.isDistributorSigned())
296                 {
297                     processAuthorSignature(data);
298                 } else if (result ==
299                            WrtSignatureValidator::SIGNATURE_DISREGARD)
300                 {
301                     continue;
302                 }
303             } else {
304                 if (result == WrtSignatureValidator::SIGNATURE_DISREGARD) {
305                     continue;
306                 }
307                 // now signature _must_ be verified
308                 processDistributorSignature(data);
309             }
310         } Catch(ParserSchemaException::Base) {
311             LogError("Error occured in ParserSchema.");
312             ReThrowMsg(Exceptions::SignatureInvalid,
313                        "Error occured in ParserSchema.");
314         }
315     }
316
317     if (signatureFiles.empty()) {
318         LogInfo("No signature files has been found.");
319     }
320
321     LogInfo("================ Step: <<Signature>> DONE ================");
322
323     m_contextData.job->UpdateProgress(
324         InstallerContext::INSTALL_DIGSIG_CHECK,
325         "Widget Signature checked");
326 }
327
328 void TaskCertify::createInstallPopup(PopupType type, const std::string &label)
329 {
330     m_contextData.job->Pause();
331     if (m_popup) {
332         destroyPopup();
333     }
334     bool ret = createPopup();
335     if (ret) {
336         loadPopup(type, label);
337         showPopup();
338     }
339 }
340 void TaskCertify::StepDeletePopupWin()
341 {
342     destroyPopup();
343 }
344
345 void TaskCertify::stepWarningPopup()
346 {
347     LogInfo("Step:: <<Warning Popup>>");
348     // SP-2151: If widget is not recognized (OCSP status of any of certificates
349     //          it is signed with is not recognized) WRT must notify user that
350     //          widget cannot be installed as a trusted application, and let the
351     //          user decide whether it should be installed as an untrusted
352     //          application.
353     if (!m_contextData.wacSecurity.isDistributorSigned()) {
354         std::string label = UNTRUSTED_WIDGET +
355             LABEL_NEW_LINE_2 +
356             QUESTION;
357         createInstallPopup(PopupType::WIDGET_UNRECOGNIZED, label);
358     }
359 }
360
361 std::string TaskCertify::createAuthorWidgetInfo() const
362 {
363     std::string authorInfo;
364     if (m_contextData.wacSecurity.isRecognized()) {
365         //authorInfo += _("IDS_IM_WIDGET_RECOGNISED");
366         authorInfo += _("WIDGET RECOGNISED");
367     } else {
368         //authorInfo += _("IDS_IM_WIDGET_UNRECOGNISED");
369         authorInfo += _("WIDGET UNRECOGNISED");
370     }
371
372     authorInfo += LABEL_NEW_LINE_2;
373     ValidationCore::CertificatePtr authorCert =
374         m_contextData.wacSecurity.getAuthorCertificatePtr();
375     if (!!authorCert) {
376         DPL::Optional < DPL::String > organizationName =
377             authorCert->getOrganizationName();
378
379         //authorInfo += _("IDS_IM_WIDGET_AUTHOR_ORGANIZATION_NAME");
380         authorInfo += _("AUTHOR ORGANIZATION NAME");
381         authorInfo += LABEL_NEW_LINE;
382
383         if (!organizationName.IsNull()) {
384             authorInfo += DPL::ToUTF8String(*organizationName);
385         } else {
386             //authorInfo += _("IDS_IM_WIDGET_ORGANIZATION_UNKNOWN");
387             authorInfo += _("WIDGET ORGANIZATION UNKNOWN");
388         }
389
390         authorInfo += LABEL_NEW_LINE_2;
391
392         DPL::Optional < DPL::String > countryName =
393             authorCert->getCountryName();
394
395         //authorInfo += _("IDS_IM_WIDGET_COUNTRY_NAME");
396         authorInfo += _("WIDGET COUNTRY NAME");
397         authorInfo += LABEL_NEW_LINE;
398
399         if (!countryName.IsNull()) {
400             authorInfo += DPL::ToUTF8String(*countryName);
401         } else {
402             //authorInfo += _("IDS_IM_WIDGET_COUNTRY_UNKNOWN");
403             authorInfo += _("WIDGET COUNTRY UNKNOWN");
404         }
405     } else {
406         authorInfo +=
407             //_("IDS_IM_WIDGET_DOES_NOT_CONTAIN_RECOGNIZED_AUTHOR_SIGNATURE");
408             _("Widget does not contain recognized author signature");
409     }
410     return authorInfo;
411 }
412
413 void TaskCertify::stepAuthorInfoPopup()
414 {
415     LogInfo("Step:: <<Author Popup Information>>");
416     std::string label
417         = createAuthorWidgetInfo() + LABEL_NEW_LINE_2 + QUESTION;
418     createInstallPopup(PopupType::WIDGET_AUTHOR_INFO, label);
419 }
420
421 void TaskCertify::stepFinalize()
422 {
423     LogInfo("Step: <<CERTYFYING DONE>>");
424
425     m_contextData.job->UpdateProgress(
426         InstallerContext::INSTALL_CERT_CHECK,
427         "Widget Certification Check Finished");
428 }
429
430 void TaskCertify::stepWarningPopupAnswer()
431 {
432     LogInfo("Step: <<Warning Popup Answer>>");
433     if (false == m_contextData.wacSecurity.isDistributorSigned() &&
434         WRT_POPUP_BUTTON_CANCEL == m_installCancel)
435     {
436         LogWarning("User does not agreed to install unsigned widgets!");
437         m_installCancel = WRT_POPUP_BUTTON;
438         destroyPopup();
439         ThrowMsg(Exceptions::NotAllowed, "Widget not allowed");
440     }
441 }
442
443 void TaskCertify::stepAuthorInfoPopupAnswer()
444 {
445     LogInfo("Step: <<Author Info Popup Answer>>");
446     if (WRT_POPUP_BUTTON_CANCEL == m_installCancel) {
447         LogWarning("User does not agreed to install widget!");
448         m_installCancel = WRT_POPUP_BUTTON;
449         destroyPopup();
450         ThrowMsg(Exceptions::NotAllowed, "Widget not allowed");
451     }
452 }
453
454 bool TaskCertify::isTizenWebApp() const
455 {
456     bool ret = FALSE;
457     if (m_installContext.widgetConfig.webAppType.appType
458         == WrtDB::AppType::APP_TYPE_TIZENWEBAPP)
459     {
460         ret = TRUE;
461     }
462
463     return ret;
464 }
465
466 void TaskCertify::stepVerifyUpdate()
467 {
468     LogInfo("Step: <<Check Update>>");
469     CertificatePtr newCertificate =
470         m_contextData.wacSecurity.getAuthorCertificatePtr();
471     CertificatePtr oldCertificate =
472         getOldAuthorSignerCertificate(m_installContext.widgetConfig.tzAppid);
473
474     if (!!newCertificate && !!oldCertificate) {
475         if (0 != newCertificate->getBase64().compare(oldCertificate->getBase64())) {
476             LogDebug("old widget's author signer certificate : " <<
477                     oldCertificate->getBase64());
478             LogDebug("new widget's author signer certificate : " <<
479                     newCertificate->getBase64());
480             ThrowMsg(Exceptions::NotMatchedCertification,
481                     "Author signer certificates doesn't match \
482                     between old widget and installing widget");
483         }
484     } else {
485         if (!(NULL == newCertificate.Get() && NULL == oldCertificate.Get())) {
486             ThrowMsg(Exceptions::NotMatchedCertification,
487                     "Author signer certificates doesn't match \
488                     between old widget and installing widget");
489         }
490     }
491 }
492 } //namespace WidgetInstall
493 } //namespace Jobs
494