Rename invalid WRT_SMACK_LABEL macro to WRT_SMACK_ENABLED
[platform/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 #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 #ifdef DECRYPT
170
171 int ssmEncrypt(InstallMode::RootPath rootPath, std::string pkgId, const char*
172         inChunk, int inBytes, char** outChunk, int *outBytes)
173 {
174     if (rootPath == InstallMode::RootPath::RO) {
175         return ssm_encrypt_preloaded_application(inChunk, inBytes,
176                 outChunk, outBytes);
177     } else {
178         return ssm_encrypt(pkgId.c_str(), pkgId.length(),
179                 inChunk, inBytes,
180                 outChunk, outBytes);
181     }
182 }
183 #endif
184 }
185
186 namespace Jobs {
187 namespace WidgetInstall {
188 TaskEncryptResource::TaskEncryptResource(InstallerContext& context) :
189     DPL::TaskDecl<TaskEncryptResource>(this),
190     m_context(context)
191 {
192     AddStep(&TaskEncryptResource::StartStep);
193 #ifdef DECRYPT
194     AddStep(&TaskEncryptResource::StepEncryptResource);
195 #endif
196     AddStep(&TaskEncryptResource::EndStep);
197 }
198 #ifdef DECRYPT
199
200 void TaskEncryptResource::StepEncryptResource()
201 {
202     _D("Step Encrypt resource");
203
204     EncryptDirectory(m_context.locations->getTemporaryRootDir());
205 }
206
207 void TaskEncryptResource::EncryptDirectory(std::string path)
208 {
209     FTS *fts;
210     FTSENT *ftsent;
211     char * const paths[] = { const_cast<char * const>(path.c_str()), NULL };
212
213     if ((fts = fts_open(paths, FTS_PHYSICAL | FTS_NOCHDIR, NULL)) == NULL) {
214         //ERROR
215         int error = errno;
216         _W("%s: fts_open failed with error: %s", __PRETTY_FUNCTION__, strerror(error));
217         ThrowMsg(Exceptions::EncryptionFailed, "Error reading directory: "
218                  << path);
219     }
220
221     while ((ftsent = fts_read(fts)) != NULL) {
222         switch (ftsent->fts_info) {
223         case FTS_DP:
224         case FTS_DC:
225         case FTS_D:
226         case FTS_DEFAULT:
227         case FTS_SLNONE:
228             //directories, non-regular files, dangling symbolic links
229             break;
230         case FTS_F:
231         case FTS_NSOK:
232         case FTS_SL:
233             //regular files and other objects that can be counted
234             if (isSupportedForEncryption(ftsent->fts_path)) {
235                 EncryptFile(ftsent->fts_path);
236             }
237             break;
238         case FTS_NS:
239         case FTS_DOT:
240         case FTS_DNR:
241         case FTS_ERR:
242         default:
243             _W("%s: traversal failed on file: %s with error: %s", __PRETTY_FUNCTION__, ftsent->fts_path, strerror(ftsent->fts_errno));
244             ThrowMsg(Exceptions::EncryptionFailed, "Error reading file");
245             break;
246         }
247     }
248
249     if (fts_close(fts) == -1) {
250         int error = errno;
251         _W("%s: fts_close failed with error: %s", __PRETTY_FUNCTION__, strerror(error));
252     }
253 }
254
255 void TaskEncryptResource::EncryptFile(const std::string &fileName)
256 {
257     _D("Encrypt file: %s", fileName.c_str());
258     std::string encFile = fileName + ".enc";
259
260     struct stat info;
261     memset(&info, 0, sizeof(info));
262     if (stat(fileName.c_str(), &info) != 0)
263     {
264         int error = errno;
265         ThrowMsg(Exceptions::EncryptionFailed,
266                 "Could not access file " << fileName <<
267                 "[" << DPL::GetErrnoString(error) << "]");
268     }
269     const std::size_t fileSize = info.st_size;
270     if (0 == fileSize) {
271         _D("%s size is 0, so encryption is skiped", fileName.c_str());
272         return;
273     }
274
275     // If update installed preload web, should skip encryption.
276     if (!(m_context.mode.rootPath == InstallMode::RootPath::RO &&
277                 m_context.mode.installTime == InstallMode::InstallTime::PRELOAD
278                 && m_context.mode.extension == InstallMode::ExtensionType::DIR)) {
279
280         DPL::ScopedFClose inFile(openFile(fileName, "r"));
281         DPL::ScopedFClose outFile(openFile(encFile, "w"));
282
283         const std::size_t chunkSize = (fileSize > ENCRYPTION_CHUNK_MAX_SIZE
284                 ? ENCRYPTION_CHUNK_MAX_SIZE : fileSize);
285
286         std::unique_ptr<unsigned char[]> inChunk(new unsigned char[chunkSize]);
287         std::size_t bytesRead = 0;
288         /* TODO : pkgId should change to appId after wrt-client label changed. */
289         std::string pkgId = DPL::ToUTF8String(m_context.widgetConfig.tzPkgid);
290
291         do
292         {
293             bytesRead = readBytes(inChunk.get(), chunkSize, inFile.Get());
294             if (0 != bytesRead) {
295                 int outDecSize = 0;
296                 char *outChunk = NULL;
297                 if (0 != ssmEncrypt(m_context.mode.rootPath, pkgId,
298                             (char*)inChunk.get(), (int)bytesRead,
299                             &outChunk, &outDecSize)) {
300                     ThrowMsg(Exceptions::EncryptionFailed,
301                             "Encryption Failed using TrustZone");
302                 }
303
304                 std::stringstream toString;
305                 toString << outDecSize;
306
307                 writeBytes((unsigned char*)toString.str().c_str(),
308                         sizeof(int), outFile.Get());
309                 writeBytes((unsigned char*)outChunk, outDecSize, outFile.Get());
310                 delete outChunk;
311             }
312             inChunk.reset(new unsigned char[chunkSize]);
313
314         } while (0 == std::feof(inFile.Get()));
315
316         outFile.Reset();
317         inFile.Reset();
318
319         _D("File encrypted successfully");
320         _D("Remove plain-text file: %s", fileName.c_str());
321         if (0 != unlink(fileName.c_str()))
322         {
323             Throw(Exceptions::EncryptionFailed);
324         }
325
326         _D("Rename encrypted file");
327         if (0 != std::rename(encFile.c_str(), fileName.c_str()))
328         {
329             Throw(Exceptions::EncryptionFailed);
330         }
331     }
332
333     std::string realPath = fileName;
334     realPath.replace(0,
335             m_context.locations->getTemporaryRootDir().length(),
336             m_context.locations->getSourceDir());
337
338     WrtDB::EncryptedFileInfo fileInfo;
339     fileInfo.fileName = DPL::FromUTF8String(realPath);
340     fileInfo.fileSize = fileSize;
341
342     m_context.widgetConfig.encryptedFiles.insert(fileInfo);
343 }
344 #endif
345
346 void TaskEncryptResource::StartStep()
347 {
348     _D("--------- <TaskEncryptResource> : START ----------");
349 }
350
351 void TaskEncryptResource::EndStep()
352 {
353     m_context.job->UpdateProgress(
354             InstallerContext::INSTALL_ECRYPTION_FILES,
355             "Ecrypt resource files");
356
357     _D("--------- <TaskEncryptResource> : END ----------");
358 }
359 } //namespace WidgetInstall
360 } //namespace Jobs