[Release] wrt-installer_0.1.15
[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::InternalError,
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::InternalError,
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::InternalError,
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     pBuf.SetArray(pByte, 0, pBufSize);
197     pBuf.Flip();
198
199     ByteBuffer* getBuffer = pInstance->_TrustZoneService::EncryptN(*appInfo, pBuf);
200     return getBuffer;
201 }
202
203 Tizen::Base::ByteBuffer* TEST_dec(
204         Tizen::Base::ByteBuffer* appInfo,
205         const unsigned char *plainBuffer,
206         int pBufSize)
207 {
208     using namespace Tizen::Base;
209
210     Tizen::Security::Crypto::_TrustZoneService* pInstance;
211     pInstance = Tizen::Security::Crypto::_TrustZoneService::GetInstance();
212
213     ByteBuffer pBuf;
214     pBuf.Construct(pBufSize);
215     const byte *pByte = reinterpret_cast<const byte*>(plainBuffer);
216     pBuf.SetArray(pByte, 0, pBufSize);
217     pBuf.Flip();
218
219     ByteBuffer* getBufferTEST =
220         pInstance->_TrustZoneService::DecryptN(*appInfo, pBuf);
221     return getBufferTEST;
222 }
223 }
224
225 namespace Jobs {
226 namespace WidgetInstall {
227 TaskEncryptResource::TaskEncryptResource(InstallerContext& context) :
228     DPL::TaskDecl<TaskEncryptResource>(this),
229     m_context(context)
230 {
231     AddStep(&TaskEncryptResource::StepEncryptResource);
232 }
233
234 void TaskEncryptResource::StepEncryptResource()
235 {
236     LogDebug("Step Encrypt resource");
237
238     EncryptDirectory(m_context.locations->getTemporaryRootDir());
239     m_context.job->UpdateProgress(
240             InstallerContext::INSTALL_ECRYPTION_FILES,
241             "Ecrypt resource files");
242 }
243
244 void TaskEncryptResource::EncryptDirectory(std::string path)
245 {
246     FTS *fts;
247     FTSENT *ftsent;
248     char * const paths[] = { const_cast<char * const>(path.c_str()), NULL };
249
250     if ((fts = fts_open(paths, FTS_PHYSICAL | FTS_NOCHDIR, NULL)) == NULL) {
251         //ERROR
252         int error = errno;
253         LogWarning(__PRETTY_FUNCTION__ << ": fts_open failed with error: "
254                                        << strerror(error));
255         ThrowMsg(Exceptions::InternalError, "Error reading directory: "
256                  << path);
257     }
258
259     while ((ftsent = fts_read(fts)) != NULL) {
260         switch (ftsent->fts_info) {
261         case FTS_DP:
262         case FTS_DC:
263         case FTS_D:
264         case FTS_DEFAULT:
265         case FTS_SLNONE:
266             //directories, non-regular files, dangling symbolic links
267             break;
268         case FTS_F:
269         case FTS_NSOK:
270         case FTS_SL:
271             //regular files and other objects that can be counted
272             if (isSupportedForEncryption(ftsent->fts_path)) {
273                 EncryptFile(ftsent->fts_path);
274             }
275             break;
276         case FTS_NS:
277         case FTS_DOT:
278         case FTS_DNR:
279         case FTS_ERR:
280         default:
281             LogWarning(__PRETTY_FUNCTION__
282                        << ": traversal failed on file: "
283                        << ftsent->fts_path
284                        << " with error: "
285                        << strerror(ftsent->fts_errno));
286             ThrowMsg(Exceptions::InternalError, "Error reading file");
287             break;
288         }
289     }
290
291     if (fts_close(fts) == -1) {
292         int error = errno;
293         LogWarning(__PRETTY_FUNCTION__ << ": fts_close failed with error: "
294                                        << strerror(error));
295     }
296 }
297
298 void TaskEncryptResource::EncryptFile(const std::string &fileName)
299 {
300     try
301     {
302         LogDebug("Encrypt file: " << fileName);
303         std::string encFile = fileName + ".enc";
304
305         struct stat info;
306         memset(&info, 0, sizeof(info));
307         if (stat(fileName.c_str(), &info) != 0)
308         {
309             int error = errno;
310             ThrowMsg(Exceptions::InternalError,
311                      "Could not access file " << fileName <<
312                      "[" << DPL::GetErrnoString(error) << "]");
313         }
314         const std::size_t fileSize = info.st_size;
315
316         DPL::ScopedFClose inFile(openFile(fileName, "r"));
317         DPL::ScopedFClose outFile(openFile(encFile, "w"));
318
319         const std::size_t chunkSize = (fileSize > ENCRYPTION_CHUNK_MAX_SIZE
320                 ? ENCRYPTION_CHUNK_MAX_SIZE : fileSize);
321
322         std::unique_ptr<unsigned char[]> inChunk(new unsigned char[chunkSize]);
323         std::unique_ptr<unsigned char[]> outChunk;
324
325         std::size_t bytesRead = 0;
326         using namespace Tizen::Base;
327
328         std::string pkgid =
329             DPL::ToUTF8String(m_context.widgetConfig.tzAppid).c_str();
330         const byte *b_pkgid = reinterpret_cast<const byte*>(
331                     pkgid.c_str());
332         ByteBuffer appInfo;
333         appInfo.Construct(pkgid.length());
334         appInfo.SetArray(b_pkgid, 0, pkgid.length());
335         appInfo.Flip();
336
337         do
338         {
339             bytesRead = readBytes(inChunk.get(), chunkSize, inFile.Get());
340             if (0 != bytesRead) {
341                 ByteBuffer *getBuffer = EncryptChunkByTrustZone(
342                         &appInfo,
343                         inChunk.get(), bytesRead);
344                 int decBufSize = getBuffer->GetRemaining();
345
346                 outChunk.reset(new unsigned char[decBufSize]);
347                 memcpy(outChunk.get(), getBuffer->GetPointer(), getBuffer->GetRemaining());
348                 getBuffer->Reset();
349
350                 char writeSize[ENCRYPTION_DEC_CHUNK_SIZE];
351                 memset(writeSize, 0x00, ENCRYPTION_DEC_CHUNK_SIZE);
352                 std::stringstream toString;
353                 toString << decBufSize;
354                 strncpy(writeSize, toString.str().c_str(), toString.str().length());
355
356                 writeBytes((unsigned char*)writeSize,
357                         ENCRYPTION_DEC_CHUNK_SIZE, outFile.Get());
358                 writeBytes(outChunk.get(), decBufSize, outFile.Get());
359             }
360
361         } while (0 == std::feof(inFile.Get()));
362
363         outFile.Reset();
364         inFile.Reset();
365
366         LogDebug("File encrypted successfully");
367         LogDebug("Remove plain-text file: " << fileName);
368         if (0 != unlink(fileName.c_str()))
369         {
370             Throw(Exceptions::InternalError);
371         }
372
373         LogDebug("Rename encrypted file");
374         if (0 != std::rename(encFile.c_str(), fileName.c_str()))
375         {
376             Throw(Exceptions::InternalError);
377         }
378
379         std::string realPath = fileName;
380         realPath.replace(0,
381                          m_context.locations->getTemporaryRootDir().length(),
382                          m_context.locations->getSourceDir());
383
384         WrtDB::EncryptedFileInfo fileInfo;
385         fileInfo.fileName = DPL::FromUTF8String(realPath);
386         fileInfo.fileSize = fileSize;
387
388         m_context.widgetConfig.encryptedFiles.insert(fileInfo);
389     }
390     Catch (Exceptions::InternalError)
391     {
392         ReThrowMsg(Exceptions::ExtractFileFailed, fileName);
393     }
394 }
395
396 } //namespace WidgetInstall
397 } //namespace Jobs