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