[Release] wrt-installer_0.1.38
[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 <FBaseByteBuffer.h>
44 #include <security/FSecCrypto_TrustZoneService.h>
45
46 #include <widget_install/job_widget_install.h>
47 #include <widget_install/widget_install_context.h>
48 #include <widget_install/widget_install_errors.h>
49
50 using namespace WrtDB;
51
52 extern "C"
53 {
54     void InitWebAppInfo(const char* appId, const char* rootPath);
55 }
56
57 namespace {
58 const std::size_t ENCRYPTION_CHUNK_MAX_SIZE = 4096; // bytes
59 const std::size_t ENCRYPTION_DEC_CHUNK_SIZE = 4; // bytes
60 static bool initWebApp = false;
61
62 std::set<std::string>& getSupportedForEncryption()
63 {
64     static std::set<std::string> encryptSet;
65     if (encryptSet.empty()) {
66         encryptSet.insert(".html");
67         encryptSet.insert(".css");
68         encryptSet.insert(".js");
69     }
70     return encryptSet;
71 }
72
73 bool isSupportedForEncryption(const std::string &file)
74 {
75     size_t foundKey = file.rfind(".");
76     if (std::string::npos != foundKey) {
77         std::string mimeType = file.substr(foundKey);
78         return getSupportedForEncryption().count(mimeType) > 0;
79     }
80     return false;
81 }
82
83 /**
84  * Opens a file.
85  *
86  * @param path Path to a file.
87  * @param mode Mode.
88  * @return Stream handle.
89  * @throw ExtractFileFailed If error (other than EINTR) occurs.
90  */
91 FILE* openFile(const std::string& path, const std::string& mode)
92 {
93     FILE* result = NULL;
94
95     do
96     {
97         result = fopen(path.c_str(), mode.c_str());
98     } while ((NULL == result) && (EINTR == errno));
99
100     if (NULL == result)
101     {
102         ThrowMsg(Jobs::WidgetInstall::Exceptions::EncryptionFailed,
103                  "Could not open file " << path);
104     }
105
106     return result;
107 }
108
109 /**
110  * Reads bytes from a stream.
111  *
112  * @param buffer Buffer to read the bytes into.
113  * @param count Number of bytes to read.
114  * @param stream Stream to read from.
115  * @return Number of bytes read
116  * @throw ExtractFileFailed If error (other than EINTR) occurs.
117  */
118 std::size_t readBytes(unsigned char* buffer, std::size_t count, FILE* stream)
119 {
120     std::size_t result = std::fread(buffer,
121                                     sizeof(unsigned char),
122                                     count,
123                                     stream);
124
125     if (result != count)
126     {
127         int error = errno;
128         if (0 != std::ferror(stream))
129         {
130             if (EINTR != error)
131             {
132                 ThrowMsg(Jobs::WidgetInstall::Exceptions::ErrorExternalInstallingFailure,
133                          "Error while reading data" <<
134                          " [" << DPL::GetErrnoString(error) << "]");
135             }
136         }
137     }
138
139     return result;
140 }
141
142 /**
143  * Writes bytes to a stream.
144  *
145  * @param buffer Data to write.
146  * @param count Number of bytes.
147  * @param stream Stream to write to.
148  * @throw ExtractFileFailed If error (other than EINTR) occurs.
149  */
150 void writeBytes(unsigned char* buffer, std::size_t count, FILE* stream)
151 {
152     std::size_t bytesWritten = 0;
153     std::size_t bytesToWrite = 0;
154     do
155     {
156         bytesToWrite = count - bytesWritten;
157         bytesWritten = std::fwrite(buffer + bytesWritten,
158                                    sizeof(unsigned char),
159                                    count - bytesWritten,
160                                    stream);
161         if ((bytesWritten != bytesToWrite) && (EINTR != errno))
162         {
163             int error = errno;
164             ThrowMsg(Jobs::WidgetInstall::Exceptions::EncryptionFailed,
165                      "Error while writing data" <<
166                      " [" << DPL::GetErrnoString(error) << "]");
167         }
168     } while ((bytesWritten != bytesToWrite) && (EINTR == errno));
169 }
170
171 /*
172  * get encrypted string from trustzone
173 */
174 Tizen::Base::ByteBuffer* EncryptChunkByTrustZone(
175         Tizen::Base::ByteBuffer* appInfo,
176         const unsigned char *plainBuffer,
177         int pBufSize)
178 {
179     using namespace Tizen::Base;
180     if(!initWebApp)
181     {
182         char* pAppId = null;
183         pAppId = (char*)calloc(appInfo->GetRemaining()+1, 1);
184         memcpy(pAppId, appInfo->GetPointer(), appInfo->GetRemaining());
185         InitWebAppInfo(pAppId, "");
186         free (pAppId);
187         initWebApp = true;
188     }
189
190     Tizen::Security::Crypto::_TrustZoneService* pInstance;
191     pInstance = Tizen::Security::Crypto::_TrustZoneService::GetInstance();
192
193     ByteBuffer pBuf;
194     pBuf.Construct(pBufSize);
195     const byte *pByte = reinterpret_cast<const byte*>(plainBuffer);
196     if (pBuf.SetArray(pByte, 0, pBufSize) != E_SUCCESS) {
197         LogDebug("Couldnot set pBuf");
198         return NULL;
199     }
200     pBuf.Flip();
201
202     return pInstance->_TrustZoneService::EncryptN(*appInfo, pBuf);
203 }
204
205 Tizen::Base::ByteBuffer* TEST_dec(
206         Tizen::Base::ByteBuffer* appInfo,
207         const unsigned char *plainBuffer,
208         int pBufSize)
209 {
210     using namespace Tizen::Base;
211
212     Tizen::Security::Crypto::_TrustZoneService* pInstance;
213     pInstance = Tizen::Security::Crypto::_TrustZoneService::GetInstance();
214
215     ByteBuffer pBuf;
216     pBuf.Construct(pBufSize);
217     const byte *pByte = reinterpret_cast<const byte*>(plainBuffer);
218     if (pBuf.SetArray(pByte, 0, pBufSize) != E_SUCCESS) {
219         LogDebug("Couldnot set pBuf");
220         return NULL;
221     }
222     pBuf.Flip();
223
224     return pInstance->_TrustZoneService::DecryptN(*appInfo, pBuf);
225 }
226 }
227
228 namespace Jobs {
229 namespace WidgetInstall {
230 TaskEncryptResource::TaskEncryptResource(InstallerContext& context) :
231     DPL::TaskDecl<TaskEncryptResource>(this),
232     m_context(context)
233 {
234     AddStep(&TaskEncryptResource::StepEncryptResource);
235 }
236
237 void TaskEncryptResource::StepEncryptResource()
238 {
239     LogDebug("Step Encrypt resource");
240
241     EncryptDirectory(m_context.locations->getTemporaryRootDir());
242     m_context.job->UpdateProgress(
243             InstallerContext::INSTALL_ECRYPTION_FILES,
244             "Ecrypt resource files");
245 }
246
247 void TaskEncryptResource::EncryptDirectory(std::string path)
248 {
249     FTS *fts;
250     FTSENT *ftsent;
251     char * const paths[] = { const_cast<char * const>(path.c_str()), NULL };
252
253     if ((fts = fts_open(paths, FTS_PHYSICAL | FTS_NOCHDIR, NULL)) == NULL) {
254         //ERROR
255         int error = errno;
256         LogWarning(__PRETTY_FUNCTION__ << ": fts_open failed with error: "
257                                        << strerror(error));
258         ThrowMsg(Exceptions::EncryptionFailed, "Error reading directory: "
259                  << path);
260     }
261
262     while ((ftsent = fts_read(fts)) != NULL) {
263         switch (ftsent->fts_info) {
264         case FTS_DP:
265         case FTS_DC:
266         case FTS_D:
267         case FTS_DEFAULT:
268         case FTS_SLNONE:
269             //directories, non-regular files, dangling symbolic links
270             break;
271         case FTS_F:
272         case FTS_NSOK:
273         case FTS_SL:
274             //regular files and other objects that can be counted
275             if (isSupportedForEncryption(ftsent->fts_path)) {
276                 EncryptFile(ftsent->fts_path);
277             }
278             break;
279         case FTS_NS:
280         case FTS_DOT:
281         case FTS_DNR:
282         case FTS_ERR:
283         default:
284             LogWarning(__PRETTY_FUNCTION__
285                        << ": traversal failed on file: "
286                        << ftsent->fts_path
287                        << " with error: "
288                        << strerror(ftsent->fts_errno));
289             ThrowMsg(Exceptions::EncryptionFailed, "Error reading file");
290             break;
291         }
292     }
293
294     if (fts_close(fts) == -1) {
295         int error = errno;
296         LogWarning(__PRETTY_FUNCTION__ << ": fts_close failed with error: "
297                                        << strerror(error));
298     }
299 }
300
301 void TaskEncryptResource::EncryptFile(const std::string &fileName)
302 {
303     LogDebug("Encrypt file: " << fileName);
304     std::string encFile = fileName + ".enc";
305
306     struct stat info;
307     memset(&info, 0, sizeof(info));
308     if (stat(fileName.c_str(), &info) != 0)
309     {
310         int error = errno;
311         ThrowMsg(Exceptions::EncryptionFailed,
312                 "Could not access file " << fileName <<
313                 "[" << DPL::GetErrnoString(error) << "]");
314     }
315     const std::size_t fileSize = info.st_size;
316
317     DPL::ScopedFClose inFile(openFile(fileName, "r"));
318     DPL::ScopedFClose outFile(openFile(encFile, "w"));
319
320     const std::size_t chunkSize = (fileSize > ENCRYPTION_CHUNK_MAX_SIZE
321             ? ENCRYPTION_CHUNK_MAX_SIZE : fileSize);
322
323     std::unique_ptr<unsigned char[]> inChunk(new unsigned char[chunkSize]);
324     std::unique_ptr<unsigned char[]> outChunk;
325
326     std::size_t bytesRead = 0;
327     using namespace Tizen::Base;
328
329     std::string pkgid =
330         DPL::ToUTF8String(m_context.widgetConfig.tzAppid).c_str();
331     const byte *b_pkgid = reinterpret_cast<const byte*>(
332             pkgid.c_str());
333     ByteBuffer appInfo;
334     appInfo.Construct(pkgid.length());
335
336     if (appInfo.SetArray(b_pkgid, 0, pkgid.length()) != E_SUCCESS) {
337         LogDebug("Couldnot set appInfo");
338         return;
339     }
340
341     appInfo.Flip();
342
343     do
344     {
345         bytesRead = readBytes(inChunk.get(), chunkSize, inFile.Get());
346         if (0 != bytesRead) {
347             ByteBuffer *getBuffer = EncryptChunkByTrustZone(
348                     &appInfo,
349                     inChunk.get(), bytesRead);
350             if (getBuffer == NULL) {
351                 ThrowMsg(Exceptions::EncryptionFailed,
352                         "Encryption Failed using TrustZone");
353             }
354             int decBufSize = getBuffer->GetRemaining();
355
356             outChunk.reset(new unsigned char[decBufSize]);
357             memcpy(outChunk.get(), getBuffer->GetPointer(), getBuffer->GetRemaining());
358             getBuffer->Reset();
359
360             char writeSize[ENCRYPTION_DEC_CHUNK_SIZE];
361             memset(writeSize, 0x00, ENCRYPTION_DEC_CHUNK_SIZE);
362             std::stringstream toString;
363             toString << decBufSize;
364             strncpy(writeSize, toString.str().c_str(), toString.str().length());
365
366             writeBytes((unsigned char*)writeSize,
367                     ENCRYPTION_DEC_CHUNK_SIZE, outFile.Get());
368             writeBytes(outChunk.get(), decBufSize, outFile.Get());
369         }
370
371     } while (0 == std::feof(inFile.Get()));
372
373     outFile.Reset();
374     inFile.Reset();
375
376     LogDebug("File encrypted successfully");
377     LogDebug("Remove plain-text file: " << fileName);
378     if (0 != unlink(fileName.c_str()))
379     {
380         Throw(Exceptions::EncryptionFailed);
381     }
382
383     LogDebug("Rename encrypted file");
384     if (0 != std::rename(encFile.c_str(), fileName.c_str()))
385     {
386         Throw(Exceptions::EncryptionFailed);
387     }
388
389     std::string realPath = fileName;
390     realPath.replace(0,
391             m_context.locations->getTemporaryRootDir().length(),
392             m_context.locations->getSourceDir());
393
394     WrtDB::EncryptedFileInfo fileInfo;
395     fileInfo.fileName = DPL::FromUTF8String(realPath);
396     fileInfo.fileSize = fileSize;
397
398     m_context.widgetConfig.encryptedFiles.insert(fileInfo);
399 }
400
401 } //namespace WidgetInstall
402 } //namespace Jobs