tizen beta release
[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 <dpl/event/nested_loop.h>
28 #include <appcore-common.h> //TODO is it necessary here?
29 #include <pcrecpp.h>
30
31 //WRT INCLUDES
32 #include <widget_install/task_certify.h>
33 #include <widget_install/job_widget_install.h>
34 #include <widget_install/widget_install_errors.h>
35 #include <widget_install/widget_install_context.h>
36 #include <dpl/log/log.h>
37 #include <wrt_error.h>
38 #include <dpl/wrt-dao-ro/global_config.h>
39 #include "wac_widget_id.h"
40
41 #include <vcore/SignatureReader.h>
42 #include <vcore/SignatureFinder.h>
43 #include <vcore/SignatureValidator.h>
44 #include <vcore/DeveloperModeValidator.h>
45 #include <dpl/utils/wrt_global_settings.h>
46 #include <dpl/wrt-dao-ro/global_dao_read_only.h>
47
48 using namespace ValidationCore;
49 using namespace WrtDB;
50
51 namespace {
52 enum ButtonId
53 {
54     BUTTON_ID_INSTALL,
55     BUTTON_ID_RESIGN
56 };
57
58 const std::string LABEL_NEW_LINE = "<br>";
59 const std::string LABEL_NEW_LINE_2 = "<br><br>";
60
61 WidgetCertificateData toWidgetCertificateData(const SignatureData &data,
62                                               bool root)
63 {
64     WidgetCertificateData result;
65
66     result.chainId = data.getSignatureNumber();
67
68     result.owner = data.isAuthorSignature() ?
69         WidgetCertificateData::AUTHOR : WidgetCertificateData::DISTRIBUTOR;
70
71     result.type = root ?
72         WidgetCertificateData::ROOT : WidgetCertificateData::ENDENTITY;
73
74     CertificatePtr certificate;
75
76     if (root) {
77         certificate = data.getRootCaCertificatePtr();
78     } else {
79         certificate = data.getEndEntityCertificatePtr();
80     }
81
82     Assert(!certificate->getCommonName().IsNull() && "CommonName is Null");
83
84     result.strCommonName = *certificate->getCommonName();
85
86     result.strMD5Fingerprint = std::string("md5 ") +
87         SignatureValidator::FingerprintToColonHex(
88             certificate->getFingerprint(Certificate::FINGERPRINT_MD5));
89
90     result.strSHA1Fingerprint = std::string("sha-1 ") +
91         SignatureValidator::FingerprintToColonHex(
92             certificate->getFingerprint(Certificate::FINGERPRINT_SHA1));
93
94     return result;
95 }
96 } // namespace anonymous
97
98 namespace Jobs {
99 namespace WidgetInstall {
100 TaskCertify::TaskCertify(InstallerContext &inCont) :
101     DPL::TaskDecl<TaskCertify>(this),
102     m_contextData(inCont),
103     m_cancelInstallation(false),
104     m_userAgreedToInstallUntrustedWidget(false)
105 {
106     AddStep(&TaskCertify::stepSignature);
107     AddStep(&TaskCertify::stepWarningPopup);
108     AddStep(&TaskCertify::stepWarningPopupAnswer);
109     AddStep(&TaskCertify::stepAuthorInfoPopup);
110     AddStep(&TaskCertify::stepAuthorInfoPopupAnswer);
111     AddStep(&TaskCertify::stepFinalize);
112 }
113
114 TaskCertify::~TaskCertify()
115 {
116 }
117
118 void TaskCertify::processDistributorSignature(const SignatureData &data,
119                                               bool first)
120 {
121     // this signature is verified -
122     // no point in check domain WAC_ROOT and WAC_RECOGNIZED
123     m_contextData.wacSecurity.setDistributorSigned(true);
124
125     if (data.getStorageType().contains(CertStoreId::WAC_ROOT)) {
126         m_contextData.wacSecurity.setWacSigned(true);
127     }
128
129     CertificateCollection collection;
130     collection.load(data.getCertList());
131     collection.sort();
132     Assert(collection.isChain() &&
133            "Certificate collection is not able to create chain. "
134            "It is not possible to verify this signature.");
135
136     m_contextData.wacSecurity.getCertificateChainListRef().push_back(
137             collection);
138
139     if (first) {
140         m_contextData.wacSecurity.getCertificateListRef().push_back(
141             toWidgetCertificateData(data, true));
142         m_contextData.wacSecurity.getCertificateListRef().push_back(
143             toWidgetCertificateData(data, false));
144     }
145 }
146
147 void TaskCertify::processAuthorSignature(const SignatureData &data)
148 {
149     using namespace ValidationCore;
150     LogInfo("DNS Identity match!");
151     // this signature is verified or widget is distributor signed
152     m_contextData.wacSecurity.getAuthorCertificatePtr() =
153         data.getEndEntityCertificatePtr();
154     m_contextData.wacSecurity.getCertificateListRef().push_back(
155         toWidgetCertificateData(data, true));
156     m_contextData.wacSecurity.getCertificateListRef().push_back(
157         toWidgetCertificateData(data, false));
158
159     // match widget_id with one from dns identity set
160     WacWidgetId widgetId(m_contextData.widgetConfig.configInfo.widget_id);
161
162     Certificate::AltNameSet dnsIdentity =
163         data.getEndEntityCertificatePtr()->getAlternativeNameDNS();
164
165     FOREACH(it, dnsIdentity){
166         if (widgetId.matchHost(*it)) {
167             m_contextData.wacSecurity.setRecognized(true);
168             return;
169         }
170     }
171 }
172
173 void TaskCertify::stepSignature()
174 {
175     Assert(!m_contextData.tempWidgetPath.empty());
176
177     std::string widgetPath = m_contextData.tempWidgetPath + "/";
178
179     SignatureFileInfoSet signatureFiles;
180     SignatureFinder signatureFinder(widgetPath);
181     if (SignatureFinder::NO_ERROR != signatureFinder.find(signatureFiles)) {
182         LogError("Error in Signature Finder");
183         ThrowMsg(Exceptions::InvalidPackage,
184                  "Error openig temporary widget directory");
185     }
186
187     SignatureFileInfoSet::reverse_iterator iter = signatureFiles.rbegin();
188     LogInfo("No of signatures: " << signatureFiles.size());
189
190     bool firstDistributorSignature = true;
191     bool testCertificate = false;
192
193     bool complianceMode = GlobalDAOReadOnly::getComplianceMode();
194
195     for (; iter != signatureFiles.rend(); ++iter) {
196         LogInfo("Checking signature with id=" << iter->getFileNumber());
197         SignatureData data(widgetPath + iter->getFileName(),
198                            iter->getFileNumber());
199
200         Try {
201             SignatureReader xml;
202             xml.initialize(data, GlobalConfig::GetSignatureXmlSchema());
203             xml.read(data);
204             SignatureValidator validator(GlobalConfig::IsOCSPEnabled(),
205                                          GlobalConfig::IsCRLEnabled(),
206                                          complianceMode);
207             SignatureValidator::Result result =
208                 validator.check(data, widgetPath);
209
210             if (result == SignatureValidator::SIGNATURE_REVOKED) {
211                 LogWarning("Certificate is REVOKED");
212                 ThrowMsg(Exceptions::InvalidPackage,
213                          "Certificate is REVOKED");
214             }
215
216             if (result == SignatureValidator::SIGNATURE_INVALID) {
217                 LogWarning("Signature is INVALID");
218                 // TODO change exception name
219                 ThrowMsg(Exceptions::InvalidPackage,
220                          "Invalid Package");
221             }
222
223             if (data.isAuthorSignature()) {
224                 if (result == SignatureValidator::SIGNATURE_VERIFIED ||
225                     m_contextData.wacSecurity.isDistributorSigned())
226                 {
227                     processAuthorSignature(data);
228                 } else if (result == SignatureValidator::SIGNATURE_DISREGARD) {
229                     continue;
230                 }
231             } else {
232                 if (result == SignatureValidator::SIGNATURE_DISREGARD) {
233                     continue;
234                 }
235                 // now signature _must_ be verified
236                 processDistributorSignature(data, firstDistributorSignature);
237                 firstDistributorSignature = false;
238             }
239
240             DeveloperModeValidator developerModeValidator(
241                 complianceMode,
242                 GlobalDAOReadOnly::getComplianceFakeImei(),
243                 GlobalDAOReadOnly::getComplianceFakeMeid());
244
245             developerModeValidator.check(data);
246
247             testCertificate |=
248                 data.getStorageType().contains(CertStoreId::DEVELOPER);
249
250             bool developerMode = GlobalDAOReadOnly::GetDeveloperMode();
251
252             if (testCertificate && !developerMode) {
253                 LogDebug("Widget signed by test certificate, "
254                          "but developer mode is off.");
255                 ThrowMsg(Exceptions::InvalidPackage,
256                          "Widget signed by test certificate, "
257                          "but developer mode is off.");
258             }
259             m_contextData.widgetConfig.isTestWidget = testCertificate;
260         } Catch(ParserSchemaException::Base) {
261             LogDebug("Error occured in ParserSchema.");
262             ReThrowMsg(Exceptions::InvalidPackage,
263                        "Error occured in ParserSchema.");
264         }
265         Catch(DeveloperModeValidator::Exception::Base) {
266             LogDebug("Cannot validate developer certificate.");
267             ReThrowMsg(Exceptions::InvalidPackage,
268                        "Cannot validate developer certificate.");
269         }
270     }
271
272     if (signatureFiles.empty()) {
273         LogInfo("No signature files has been found.");
274     }
275
276     LogInfo("================ Step: <<CSignature>> DONE ================");
277 }
278
279 void TaskCertify::stepWarningPopup()
280 {
281     LogInfo("Step:: <<Warning Popup>>");
282     // SP-2151: If widget is not recognized (OCSP status of any of certificates
283     //          it is signed with is not recognized) WRT must notify user that
284     //          widget cannot be installed as a trusted application, and let the
285     //          user decide whether it should be installed as an untrusted
286     //          application.
287     if (!m_contextData.wacSecurity.isDistributorSigned()) {
288         if (GlobalSettings::GetPopupsEnabledFlag()) {
289             m_contextData.job->Pause();
290             std::string label = _("IDS_IM_POP_WIDGET_UNTRUSTED_WARNING") +
291                                 LABEL_NEW_LINE +
292                                 _("IDS_IM_WIDGET_WANT_TO_INSTALL");
293             using namespace DPL::Popup;
294             CtrlPopupPtr popup =
295                 PopupControllerSingleton::Instance().CreatePopup();
296             popup->SetTitle(_("IDS_IM_POP_WIDGET_UNTRUSTED_TITLE"));
297             popup->Append(new PopupObject::Label(label));
298             popup->Append(new PopupObject::Button(_("IDS_IM_BUTTON_INSTALL"),
299                                                    BUTTON_ID_INSTALL));
300             popup->Append(new PopupObject::Button(_("IDS_IM_BUTTON_RESIGN"),
301                                                    BUTTON_ID_RESIGN));
302
303             ListenForAnswer(popup);
304
305             PopupAnswerCallback cb = MakeAnswerCallback(this,
306                     &TaskCertify::onWarningPopupAnswer);
307
308             ShowPopupEvent event(popup, cb, DPL::Event::UNDEFINED_LOOP_HANDLE);
309             CONTROLLER_POST_EVENT(PopupController, event);
310         } else {
311             m_userAgreedToInstallUntrustedWidget = true;
312         }
313     }
314 }
315
316 void TaskCertify::stepWarningPopupAnswer()
317 {
318     LogInfo("Step: <<Warning Popup Answer>>");
319     if (false == m_contextData.wacSecurity.isDistributorSigned() &&
320         false == m_userAgreedToInstallUntrustedWidget)
321     {
322         LogWarning("User does not agreed to install unsigned widgets!");
323         ThrowMsg(Exceptions::NotAllowed, "Widget not allowed");
324     }
325 }
326
327 void TaskCertify::stepAuthorInfoPopupAnswer()
328 {
329     LogInfo("Step: <<Author Info Popup Answer>>");
330     if (m_cancelInstallation) {
331         LogWarning("User does not agreed to install widget!");
332         ThrowMsg(Exceptions::NotAllowed, "Widget not allowed");
333     }
334 }
335
336 std::string TaskCertify::createAuthorWidgetInfo() const
337 {
338     std::string authorInfo;
339     if (m_contextData.wacSecurity.isRecognized()) {
340         authorInfo += _("IDS_IM_WIDGET_RECOGNISED");
341     } else {
342         authorInfo += _("IDS_IM_WIDGET_UNRECOGNISED");
343     }
344
345     authorInfo += LABEL_NEW_LINE_2;
346     ValidationCore::CertificatePtr authorCert =
347         m_contextData.wacSecurity.getAuthorCertificatePtr();
348     if (!!authorCert) {
349         DPL::Optional < DPL::String > organizationName =
350             authorCert->getOrganizationName();
351
352         authorInfo += _("IDS_IM_WIDGET_AUTHOR_ORGANIZATION_NAME");
353         authorInfo += LABEL_NEW_LINE;
354
355         if (!organizationName.IsNull()) {
356             authorInfo += DPL::ToUTF8String(*organizationName);
357         } else {
358             authorInfo += _("IDS_IM_WIDGET_ORGANIZATION_UNKNOWN");
359         }
360
361         authorInfo += LABEL_NEW_LINE_2;
362
363         DPL::Optional < DPL::String > countryName =
364             authorCert->getCountryName();
365
366         authorInfo += _("IDS_IM_WIDGET_COUNTRY_NAME");
367         authorInfo += LABEL_NEW_LINE;
368
369         if (!countryName.IsNull()) {
370             authorInfo += DPL::ToUTF8String(*countryName);
371         } else {
372             authorInfo += _("IDS_IM_WIDGET_COUNTRY_UNKNOWN");
373         }
374     } else {
375         authorInfo +=
376             _("IDS_IM_WIDGET_DOES_NOT_CONTAIN_RECOGNIZED_AUTHOR_SIGNATURE");
377     }
378     return authorInfo;
379 }
380
381 void TaskCertify::stepAuthorInfoPopup()
382 {
383     LogInfo("Step:: <<Author Popup Information>>");
384
385     if (!GlobalSettings::GetPopupsEnabledFlag()) {
386         LogDebug("Popups are not enabled! Author information wont be shown.");
387         return;
388     }
389
390     using namespace DPL::Popup;
391     m_contextData.job->Pause();
392     std::string label = createAuthorWidgetInfo() + LABEL_NEW_LINE + _("IDS_IM_WIDGET_WANT_TO_INSTALL");
393
394     CtrlPopupPtr popup = PopupControllerSingleton::Instance().CreatePopup();
395     popup->SetTitle(_("IDS_IM_WIDGET_HEAD"));
396     popup->Append(new PopupObject::Label(label));
397     popup->Append(new PopupObject::Button(_("IDS_IM_BUTTON_INSTALL"),
398                                            BUTTON_ID_INSTALL));
399     popup->Append(new PopupObject::Button(_("IDS_IM_BUTTON_RESIGN"),
400                                            BUTTON_ID_RESIGN));
401     ListenForAnswer(popup);
402     ShowPopupEvent event(popup,
403                          MakeAnswerCallback(
404                              this,
405                              &TaskCertify::onAuthorInfoPopupAnswer),
406                          DPL::Event::UNDEFINED_LOOP_HANDLE);
407     CONTROLLER_POST_EVENT(PopupController, event);
408 }
409
410 void TaskCertify::stepFinalize()
411 {
412     LogInfo("Step: <<CERTYFYING DONE>>");
413 }
414
415 void TaskCertify::onWarningPopupAnswer(const DPL::Popup::AnswerCallbackData& answer)
416 {
417     m_contextData.job->Resume();
418     if (BUTTON_ID_RESIGN == answer.buttonAnswer) {
419         m_userAgreedToInstallUntrustedWidget = false;
420     } else if (BUTTON_ID_INSTALL == answer.buttonAnswer) {
421         m_userAgreedToInstallUntrustedWidget = true;
422     } else {
423         Assert(false && "Unpredicted answer received.");
424     }
425 }
426
427 void TaskCertify::onAuthorInfoPopupAnswer(
428         const DPL::Popup::AnswerCallbackData& answer)
429 {
430     m_contextData.job->Resume();
431     if (BUTTON_ID_RESIGN == answer.buttonAnswer) {
432         m_cancelInstallation = true;
433     }
434 }
435 } //namespace WidgetInstall
436 } //namespace Jobs
437