Fix in StepWgtPatchStorageDirectory
[platform/core/appfw/wgt-backend.git] / src / wgt / step / encryption / step_encrypt_resources.cc
1 // Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
2 // Use of this source code is governed by a apache 2.0 license that can be
3 // found in the LICENSE file.
4
5 #include "wgt/step/encryption/step_encrypt_resources.h"
6
7 #include <web_app_enc.h>
8
9 #include <boost/filesystem/operations.hpp>
10 #include <boost/system/error_code.hpp>
11
12 #include <common/utils/file_util.h>
13 #include <common/utils/byte_size_literals.h>
14
15 #include <manifest_parser/utils/logging.h>
16 #include <sys/stat.h>
17
18 #include <algorithm>
19 #include <cstdio>
20 #include <cstdlib>
21 #include <set>
22 #include <string>
23
24 namespace {
25
26 const std::size_t kEncryptionChunkMaxSize = 8_kB;  // bytes
27 const std::set<std::string> encryptSet { ".html", ".htm", ".css", ".js"};
28
29 FILE* OpenFile(const std::string& path, const std::string& mode) {
30   FILE* result = nullptr;
31
32   do {
33     result = fopen(path.c_str(), mode.c_str());
34   } while ((nullptr == result));
35
36   return result;
37 }
38
39 std::size_t ReadBytes(unsigned char* buffer, std::size_t count, FILE* stream) {
40   std::size_t result = std::fread(buffer,
41                                   1,
42                                   count,
43                                   stream);
44   if (result != count) {
45     if (0 != std::ferror(stream)) {
46       LOG(ERROR) << "Error while reading data";
47     }
48   }
49
50   return result;
51 }
52
53 void WriteBytes(unsigned char* buffer, std::size_t count, FILE* stream) {
54   // original file is treated as destination!
55   if (count <= 0) {
56     return;
57   }
58   std::size_t bytes_written = 0;
59   do {
60      std::size_t bytes_appended = std::fwrite(
61          buffer + bytes_written,
62          sizeof(unsigned char),
63          count - bytes_written,
64          stream);
65     if ((bytes_appended == 0)) {
66       LOG(ERROR) << "Error while writing data";
67       return;
68     }
69     bytes_written += bytes_appended;
70   } while ((bytes_written < count));
71 }
72
73 }  // namespace
74
75
76 namespace wgt {
77 namespace encryption {
78
79 namespace bf = boost::filesystem;
80 namespace bs = boost::system;
81
82 common_installer::Step::Status StepEncryptResources::precheck() {
83   backend_data_ = static_cast<WgtBackendData*>(context_->backend_data.get());
84   if (!backend_data_) {
85     LOG(ERROR) << "no backend data";
86     return common_installer::Step::Status::ERROR;
87   }
88
89   SetEncryptionRoot();
90
91   if (input_.empty()) {
92     LOG(ERROR) << "unpacked_dir_path attribute is empty";
93     return Step::Status::INVALID_VALUE;
94   }
95   if (!bf::exists(input_)) {
96     LOG(ERROR) << "unpacked_dir_path (" << input_ << ") path does not exist";
97     return Step::Status::INVALID_VALUE;
98   }
99
100   return common_installer::Step::Status::OK;
101 }
102
103 common_installer::Step::Status StepEncryptResources::process() {
104   if (!backend_data_->settings.get().encryption_enabled()) {
105     LOG(DEBUG) << "no encryption";
106     return common_installer::Step::Status::OK;
107   }
108   LOG(DEBUG) << "Encrypting";
109
110   if (!Encrypt(input_)) {
111     LOG(ERROR) << "Error during encryption";
112     return common_installer::Step::Status::ERROR;
113   }
114
115   return common_installer::Step::Status::OK;
116 }
117
118 bool StepEncryptResources::Encrypt(const bf::path &src) {
119   // traversing through src dir (recurrence if subdir found)
120   // for every file found, check if it should be encrypted (ToBeEncrypted)
121   // if yes, encrypt it (and replace original one)
122   // if not, leave it
123   for (bf::directory_iterator file(src);
124        file != bf::directory_iterator();
125        ++file) {
126     bs::error_code error_code;
127     bf::path current(file->path());
128
129     bool is_dir = bf::is_directory(current, error_code);
130     if (error_code)
131       return false;
132     if (is_dir) {
133       if (!Encrypt(current))
134         return false;
135       continue;
136     }
137
138     bool is_sym = bf::is_symlink(symlink_status(current, error_code));
139     if (error_code)
140       return false;
141     if (is_sym)
142       continue;
143
144     // it is regular file (not dir, not symlink)
145     if (ToBeEncrypted(current)) {
146       LOG(INFO) << "File for encryption: " << current;
147       if (!EncryptFile(current))
148         return false;
149     }
150   }
151   return true;
152 }
153
154 bool StepEncryptResources::EncryptFile(const bf::path &src) {
155   bf::path encFile(src.string() + ".enc");
156   struct stat info;
157   memset(&info, 0, sizeof(info));
158   if (stat(src.string().c_str(), &info) != 0) {
159     LOG(ERROR) << "Could not access file " << src.string();
160     return false;
161   }
162   const std::size_t fileSize = info.st_size;
163   if (0 == fileSize) {
164     LOG(ERROR) << src.string().c_str() << " size is 0, so encryption is skiped";
165     return true;
166   }
167
168   FILE *input = OpenFile(src.string().c_str(), "rb");
169   if (input == nullptr) {
170     LOG(ERROR) << "Cannot open file for encryption: " << src.string();
171     return false;
172   }
173
174   FILE *output = OpenFile(encFile.string().c_str(), "wb");
175   if (output == nullptr) {
176     LOG(ERROR) << "Cannot create encrypted file: " << encFile.string();
177     fclose(input);
178     return false;
179   }
180
181   std::size_t chunkSize = (fileSize > kEncryptionChunkMaxSize
182                                  ? kEncryptionChunkMaxSize : fileSize);
183
184   std::unique_ptr<unsigned char[]> inChunk(new unsigned char[chunkSize]);
185   std::size_t bytesRead = 0;
186
187   do {
188     bytesRead = ReadBytes(inChunk.get(), chunkSize, input);
189     if (0 != bytesRead) {
190       unsigned char* encrypted_data = nullptr;
191       size_t encrypted_size = 0;
192       // TODO(p.sikorski) check if it is Preloaded
193       int ret;
194       if (context_->request_mode.get()
195               == common_installer::RequestMode::GLOBAL) {
196         ret = wae_encrypt_global_web_application(
197                 context_->pkgid.get().c_str(),
198                 context_->is_readonly_package.get() ?
199                 true : false,
200                 inChunk.get(),
201                 (size_t)bytesRead,
202                 &encrypted_data,
203                 &encrypted_size);
204       } else {
205           ret = wae_encrypt_web_application(
206                   context_->uid.get(),
207                   context_->pkgid.get().c_str(),
208                   inChunk.get(),
209                   (size_t)bytesRead,
210                   &encrypted_data,
211                   &encrypted_size);
212       }
213
214       if (WAE_ERROR_NONE != ret) {
215         LOG(ERROR) << "Error during encrypting:";
216         switch (ret) {
217           case WAE_ERROR_INVALID_PARAMETER:
218             LOG(ERROR) << "WAE_ERROR_INVALID_PARAMETER";
219             break;
220           case WAE_ERROR_PERMISSION_DENIED:
221             LOG(ERROR) << "WAE_ERROR_PERMISSION_DENIED";
222             break;
223           case WAE_ERROR_NO_KEY:
224             LOG(ERROR) << "WAE_ERROR_NO_KEY";
225             break;
226           case WAE_ERROR_KEY_MANAGER:
227             LOG(ERROR) << "WAE_ERROR_KEY_MANAGER";
228             break;
229           case WAE_ERROR_CRYPTO:
230             LOG(ERROR) << "WAE_ERROR_CRYPTO";
231             break;
232           case WAE_ERROR_UNKNOWN:
233             LOG(ERROR) << "WAE_ERROR_UNKNOWN";
234             break;
235           default:
236             LOG(ERROR) << "UNKNOWN";
237             break;
238         }
239         fclose(output);
240         fclose(input);
241         return false;
242       }
243
244       if (encrypted_size <= 0) {
245         LOG(ERROR) << "Encryption Failed using TrustZone";
246         fclose(output);
247         fclose(input);
248         return false;
249       }
250
251       std::stringstream toString;
252       toString << encrypted_size;
253
254       WriteBytes((unsigned char*)toString.str().c_str(), sizeof(int), output);
255       WriteBytes((unsigned char*)encrypted_data, encrypted_size, output);
256       free(encrypted_data);
257     }
258     inChunk.reset(new unsigned char[chunkSize]);
259   } while (!std::feof(input));
260
261   fclose(output);
262   fclose(input);
263
264   LOG(DEBUG) << "File encrypted successfully";
265   if (0 != unlink(src.string().c_str())) {
266     return false;
267   }
268
269   LOG(DEBUG) << "Rename encrypted file";
270   if (0 != std::rename(encFile.c_str(), src.string().c_str())) {
271     return false;
272   }
273
274   return true;
275 }
276
277 void StepEncryptResources::SetEncryptionRoot() {
278   input_ = context_->unpacked_dir_path.get();
279 }
280
281 bool StepEncryptResources::ToBeEncrypted(const bf::path &file) {
282   size_t found_key = file.string().rfind(".");
283   if (std::string::npos != found_key) {
284     std::string mimeType = file.string().substr(found_key);
285     std::transform(mimeType.begin(), mimeType.end(), mimeType.begin(),
286                    ::tolower);
287     return encryptSet.count(mimeType) > 0;
288   }
289   return false;
290 }
291
292 }  // namespace encryption
293 }  // namespace wgt