Initialize Tizen 2.3
[framework/web/wrt-installer.git] / src_wearable / jobs / widget_install / task_encrypt_resource.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_ecnrypt_resource.cpp
18  * @author  Soyoung Kim (sy037.kim@samsung.com)
19  * @version 1.0
20  * @brief   Implementation file for installer task encrypt resource
21  */
22 #include "task_encrypt_resource.h"
23
24 #undef __USE_FILE_OFFSET64
25
26 #include <unistd.h>
27 #include <sys/stat.h>
28 #include <fts.h>
29 #include <string.h>
30 #include <errno.h>
31 #include <cstdio>
32 #include <sstream>
33 #include <iostream>
34 #include <algorithm>
35
36 #include <memory>
37
38 #include <dpl/errno_string.h>
39 #include <dpl/foreach.h>
40 #include <dpl/scoped_fclose.h>
41 #include <dpl/wrt-dao-ro/global_config.h>
42 #include <dpl/string.h>
43 #include <ss_manager.h>
44
45 #include <widget_install/job_widget_install.h>
46 #include <widget_install/widget_install_context.h>
47 #include <widget_install/widget_install_errors.h>
48
49 #include <installer_log.h>
50
51 using namespace WrtDB;
52
53 namespace {
54 const std::size_t ENCRYPTION_CHUNK_MAX_SIZE = 8192; // bytes
55 const std::size_t ENCRYPTION_DEC_CHUNK_SIZE = 4; // bytes
56
57 std::set<std::string>& getSupportedForEncryption()
58 {
59     static std::set<std::string> encryptSet;
60     if (encryptSet.empty()) {
61         encryptSet.insert(".html");
62         encryptSet.insert(".htm");
63         encryptSet.insert(".css");
64         encryptSet.insert(".js");
65     }
66     return encryptSet;
67 }
68
69 bool isSupportedForEncryption(const std::string &file)
70 {
71     size_t foundKey = file.rfind(".");
72     if (std::string::npos != foundKey) {
73         std::string mimeType = file.substr(foundKey);
74         std::transform(mimeType.begin(), mimeType.end(), mimeType.begin(),
75                        ::tolower);
76
77         return getSupportedForEncryption().count(mimeType) > 0;
78     }
79     return false;
80 }
81
82 /**
83  * Opens a file.
84  *
85  * @param path Path to a file.
86  * @param mode Mode.
87  * @return Stream handle.
88  * @throw ExtractFileFailed If error (other than EINTR) occurs.
89  */
90 FILE* openFile(const std::string& path, const std::string& mode)
91 {
92     FILE* result = NULL;
93
94     do
95     {
96         result = fopen(path.c_str(), mode.c_str());
97     } while ((NULL == result) && (EINTR == errno));
98
99     if (NULL == result)
100     {
101         ThrowMsg(Jobs::WidgetInstall::Exceptions::EncryptionFailed,
102                  "Could not open file " << path);
103     }
104
105     return result;
106 }
107
108 /**
109  * Reads bytes from a stream.
110  *
111  * @param buffer Buffer to read the bytes into.
112  * @param count Number of bytes to read.
113  * @param stream Stream to read from.
114  * @return Number of bytes read
115  * @throw ExtractFileFailed If error (other than EINTR) occurs.
116  */
117 std::size_t readBytes(unsigned char* buffer, std::size_t count, FILE* stream)
118 {
119     std::size_t result = std::fread(buffer,
120                                     sizeof(unsigned char),
121                                     count,
122                                     stream);
123
124     if (result != count)
125     {
126         int error = errno;
127         if (0 != std::ferror(stream))
128         {
129             if (EINTR != error)
130             {
131                 ThrowMsg(Jobs::WidgetInstall::Exceptions::ErrorExternalInstallingFailure,
132                          "Error while reading data" <<
133                          " [" << DPL::GetErrnoString(error) << "]");
134             }
135         }
136     }
137
138     return result;
139 }
140
141 /**
142  * Writes bytes to a stream.
143  *
144  * @param buffer Data to write.
145  * @param count Number of bytes.
146  * @param stream Stream to write to.
147  * @throw ExtractFileFailed If error (other than EINTR) occurs.
148  */
149 void writeBytes(unsigned char* buffer, std::size_t count, FILE* stream)
150 {
151     std::size_t bytesWritten = 0;
152     std::size_t bytesToWrite = 0;
153     do
154     {
155         bytesToWrite = count - bytesWritten;
156         bytesWritten = std::fwrite(buffer + bytesWritten,
157                                    sizeof(unsigned char),
158                                    count - bytesWritten,
159                                    stream);
160         if ((bytesWritten != bytesToWrite) && (EINTR != errno))
161         {
162             int error = errno;
163             ThrowMsg(Jobs::WidgetInstall::Exceptions::EncryptionFailed,
164                      "Error while writing data" <<
165                      " [" << DPL::GetErrnoString(error) << "]");
166         }
167     } while ((bytesWritten != bytesToWrite) && (EINTR == errno));
168 }
169
170 int ssmEncrypt(InstallMode::InstallTime time, std::string pkgId, const char*
171         inChunk, int inBytes, char** outChunk, int *outBytes)
172 {
173     if (time == InstallMode::InstallTime::PRELOAD) {
174         return ssm_encrypt_preloaded_application(inChunk, inBytes, outChunk, outBytes);
175     } else {
176         return ssm_encrypt(pkgId.c_str(),pkgId.length(), inChunk, inBytes, outChunk, outBytes);
177     }
178 }
179
180 }
181
182 namespace Jobs {
183 namespace WidgetInstall {
184 TaskEncryptResource::TaskEncryptResource(InstallerContext& context) :
185     DPL::TaskDecl<TaskEncryptResource>(this),
186     m_context(context)
187 {
188     AddStep(&TaskEncryptResource::StartStep);
189     AddStep(&TaskEncryptResource::StepEncryptResource);
190     AddStep(&TaskEncryptResource::EndStep);
191 }
192
193 void TaskEncryptResource::StepEncryptResource()
194 {
195     _D("Step Encrypt resource");
196
197     EncryptDirectory(m_context.locations->getSourceDir());
198 }
199
200 void TaskEncryptResource::EncryptDirectory(std::string path)
201 {
202     FTS *fts;
203     FTSENT *ftsent;
204     char * const paths[] = { const_cast<char * const>(path.c_str()), NULL };
205
206     if ((fts = fts_open(paths, FTS_PHYSICAL | FTS_NOCHDIR, NULL)) == NULL) {
207         //ERROR
208         int error = errno;
209         _W("%s: fts_open failed with error: %s", __PRETTY_FUNCTION__, strerror(error));
210         ThrowMsg(Exceptions::EncryptionFailed, "Error reading directory: "
211                  << path);
212     }
213
214     while ((ftsent = fts_read(fts)) != NULL) {
215         switch (ftsent->fts_info) {
216         case FTS_DP:
217         case FTS_DC:
218         case FTS_D:
219         case FTS_DEFAULT:
220         case FTS_SLNONE:
221             //directories, non-regular files, dangling symbolic links
222             break;
223         case FTS_F:
224         case FTS_NSOK:
225         case FTS_SL:
226             //regular files and other objects that can be counted
227             if (isSupportedForEncryption(ftsent->fts_path)) {
228                 EncryptFile(ftsent->fts_path);
229             }
230             break;
231         case FTS_NS:
232         case FTS_DOT:
233         case FTS_DNR:
234         case FTS_ERR:
235         default:
236             _W("%s: traversal failed on file: %s with error: %s", __PRETTY_FUNCTION__, ftsent->fts_path, strerror(ftsent->fts_errno));
237             ThrowMsg(Exceptions::EncryptionFailed, "Error reading file");
238             break;
239         }
240     }
241
242     if (fts_close(fts) == -1) {
243         int error = errno;
244         _W("%s: fts_close failed with error: %s", __PRETTY_FUNCTION__, strerror(error));
245     }
246 }
247
248 void TaskEncryptResource::EncryptFile(const std::string &fileName)
249 {
250     _D("Encrypt file: %s", fileName.c_str());
251     std::string encFile = fileName + ".enc";
252
253     struct stat info;
254     memset(&info, 0, sizeof(info));
255     if (stat(fileName.c_str(), &info) != 0)
256     {
257         int error = errno;
258         ThrowMsg(Exceptions::EncryptionFailed,
259                 "Could not access file " << fileName <<
260                 "[" << DPL::GetErrnoString(error) << "]");
261     }
262     const std::size_t fileSize = info.st_size;
263     if (0 == fileSize) {
264         _D("%s size is 0, so encryption is skiped", fileName.c_str());
265         return;
266     }
267
268     // If update installed preload web, should skip encryption.
269     if (!(m_context.mode.rootPath == InstallMode::RootPath::RO &&
270                 (m_context.mode.installTime == InstallMode::InstallTime::PRELOAD
271                  || m_context.mode.installTime == InstallMode::InstallTime::FOTA)
272                 && m_context.mode.extension == InstallMode::ExtensionType::DIR)) {
273
274         DPL::ScopedFClose inFile(openFile(fileName, "r"));
275         DPL::ScopedFClose outFile(openFile(encFile, "w"));
276
277         const std::size_t chunkSize = (fileSize > ENCRYPTION_CHUNK_MAX_SIZE
278                 ? ENCRYPTION_CHUNK_MAX_SIZE : fileSize);
279
280         std::unique_ptr<unsigned char[]> inChunk(new unsigned char[chunkSize]);
281         std::size_t bytesRead = 0;
282         std::string pkgId = DPL::ToUTF8String(m_context.widgetConfig.tzPkgid);
283
284         do
285         {
286             bytesRead = readBytes(inChunk.get(), chunkSize, inFile.Get());
287             if (0 != bytesRead) {
288                 int outDecSize = 0;
289                 char *outChunk = NULL;
290                 if (0 != ssmEncrypt(m_context.mode.installTime, pkgId,
291                             (char*)inChunk.get(), (int)bytesRead,
292                             &outChunk, &outDecSize)) {
293                     ThrowMsg(Exceptions::EncryptionFailed,
294                             "Encryption Failed using TrustZone");
295                 }
296
297                 std::stringstream toString;
298                 toString << outDecSize;
299
300                 writeBytes((unsigned char*)toString.str().c_str(),
301                         sizeof(int), outFile.Get());
302                 writeBytes((unsigned char*)outChunk, outDecSize, outFile.Get());
303                 delete outChunk;
304             }
305             inChunk.reset(new unsigned char[chunkSize]);
306
307         } while (0 == std::feof(inFile.Get()));
308
309         outFile.Reset();
310         inFile.Reset();
311
312         _D("File encrypted successfully");
313         _D("Remove plain-text file: %s", fileName.c_str());
314         if (0 != unlink(fileName.c_str()))
315         {
316             Throw(Exceptions::EncryptionFailed);
317         }
318
319         _D("Rename encrypted file");
320         if (0 != std::rename(encFile.c_str(), fileName.c_str()))
321         {
322             Throw(Exceptions::EncryptionFailed);
323         }
324     }
325
326     WrtDB::EncryptedFileInfo fileInfo;
327     fileInfo.fileName = DPL::FromUTF8String(fileName);
328     fileInfo.fileSize = fileSize;
329
330     m_context.widgetConfig.encryptedFiles.insert(fileInfo);
331 }
332
333 void TaskEncryptResource::StartStep()
334 {
335     LOGD("--------- <TaskEncryptResource> : START ----------");
336 }
337
338 void TaskEncryptResource::EndStep()
339 {
340     m_context.job->UpdateProgress(
341             InstallerContext::INSTALL_ECRYPTION_FILES,
342             "Ecrypt resource files");
343
344     LOGD("--------- <TaskEncryptResource> : END ----------");
345 }
346 } //namespace WidgetInstall
347 } //namespace Jobs