• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "package_file.h"
17 
18 #include <fstream>
19 
20 #include "db_errno.h"
21 #include "value_hash_calc.h"
22 #include "parcel.h"
23 #include "platform_specific.h"
24 
25 namespace DistributedDB {
26 using std::string;
27 using std::vector;
28 using std::list;
29 using std::ifstream;
30 using std::ofstream;
31 using std::ios;
32 using std::ios_base;
33 
34 namespace {
35     constexpr uint32_t MAX_FILE_NAME_LEN = 256;
36     constexpr uint32_t CHECKSUM_LEN = SHA256_DIGEST_LENGTH;
37     constexpr uint32_t CHECKSUM_BLOCK_SIZE = 64;
38     constexpr uint32_t DEVICE_ID_LEN = SHA256_DIGEST_LENGTH;
39     constexpr uint32_t MAGIC_LEN = 16;
40     constexpr uint32_t CURRENT_VERSION = 0;
41     constexpr uint64_t BUFFER_LEN = 4096;
42     const string MAGIC = "HW package file";
43     const string FILE_SEPARATOR = "/";
44     const string INVALID_FILE_WORDS = "..";
45 
46     const uint32_t FILE_HEADER_LEN = MAGIC_LEN + CHECKSUM_LEN + DEVICE_ID_LEN + Parcel::GetUInt32Len() * 3;
47     const uint32_t FILE_CONTEXT_LEN = MAX_FILE_NAME_LEN + Parcel::GetUInt32Len() * 2 + Parcel::GetUInt64Len() * 2;
48 }
49 
50 struct FileContext {
51     char fileName[MAX_FILE_NAME_LEN] = {0};
52     uint32_t fileType = 0;
53     uint32_t parentID = 0;
54     uint64_t fileLen = 0;
55     uint64_t offset = 0;
56 };
57 
Clear(ofstream & target,const string & targetFile)58 static void Clear(ofstream &target, const string &targetFile)
59 {
60     if (target.is_open()) {
61         target.close();
62     }
63     if (OS::RemoveFile(targetFile.c_str()) != E_OK) {
64         LOGE("Remove file failed.");
65     }
66     return;
67 }
68 
GetChecksum(const string & file,vector<char> & result)69 static int GetChecksum(const string &file, vector<char> &result)
70 {
71     ifstream fileHandle(file, ios::in | ios::binary);
72     if (!fileHandle.good()) {
73         LOGE("[GetChecksum]Error fileHandle!");
74         return -E_INVALID_PATH;
75     }
76     ValueHashCalc calc;
77     int errCode = calc.Initialize();
78     if (errCode != E_OK) {
79         LOGE("[GetChecksum]Calc Initialize fail!");
80         return errCode;
81     }
82     fileHandle.seekg(static_cast<int64_t>(MAGIC_LEN + Parcel::GetUInt32Len() + CHECKSUM_LEN), ios_base::beg);
83     vector<uint8_t> buffer(CHECKSUM_BLOCK_SIZE, 0);
84     bool readEnd = false;
85     while (!readEnd) {
86         fileHandle.read(reinterpret_cast<char *>(buffer.data()), buffer.size());
87         if (fileHandle.eof()) {
88             readEnd = true;
89         } else if (!fileHandle.good()) {
90             LOGE("[GetChecksum]fileHandle error!");
91             return -E_INVALID_PATH;
92         }
93         errCode = calc.Update(buffer);
94         if (errCode != E_OK) {
95             LOGE("[GetChecksum]Calc Update fail!");
96             return errCode;
97         }
98         buffer.assign(CHECKSUM_BLOCK_SIZE, 0);
99     }
100     vector<uint8_t> resultBuf;
101     errCode = calc.GetResult(resultBuf);
102     if (errCode != E_OK) {
103         LOGE("[GetChecksum]Calc GetResult fail!");
104         return errCode;
105     }
106     result.assign(resultBuf.begin(), resultBuf.end());
107     return E_OK;
108 }
109 
GetFileContexts(const string & sourcePath,list<FileContext> & fileContexts)110 static int GetFileContexts(const string &sourcePath, list<FileContext> &fileContexts)
111 {
112     list<OS::FileAttr> files;
113     int errCode = OS::GetFileAttrFromPath(sourcePath, files, false);
114     if (errCode != E_OK) {
115         LOGE("[GetFileContexts] get file attr from path fail, errCode = [%d]", errCode);
116         return errCode;
117     }
118     FileContext fileContext;
119     int countLimit = 0;
120     for (auto file = files.begin(); file != files.end(); file++, countLimit++) {
121         if (countLimit > 20) { // Limit number of files 20 for security
122             LOGE("Too deep access for get file context!");
123             return -E_INVALID_PATH;
124         }
125 
126         if (file->fileType != OS::FILE && file->fileType != OS::PATH) {
127             continue;
128         }
129 
130         errCode = memset_s(fileContext.fileName, MAX_FILE_NAME_LEN, 0, MAX_FILE_NAME_LEN);
131         if (errCode != EOK) {
132             return -E_SECUREC_ERROR;
133         }
134 
135         if (file->fileName.size() >= MAX_FILE_NAME_LEN) {
136             LOGE("file name is too long!");
137             return -E_INVALID_FILE;
138         }
139 
140         errCode = memcpy_s(fileContext.fileName, MAX_FILE_NAME_LEN, file->fileName.c_str(), file->fileName.size());
141         if (errCode != EOK) {
142             return -E_SECUREC_ERROR;
143         }
144 
145         fileContext.fileLen = file->fileLen;
146         fileContext.fileType = file->fileType;
147         fileContexts.push_back(fileContext);
148     }
149     LOGD("Get file contexts, fileContexts size is [%zu]", fileContexts.size());
150     return E_OK;
151 }
152 
FileContentCopy(ifstream & sourceFile,ofstream & targetFile,uint64_t fileLen)153 static int FileContentCopy(ifstream &sourceFile, ofstream &targetFile, uint64_t fileLen)
154 {
155     uint64_t leftLen = fileLen;
156     vector<char> buffer(BUFFER_LEN, 0);
157     while (leftLen > 0) {
158         uint64_t readLen = (leftLen > BUFFER_LEN) ? BUFFER_LEN : leftLen;
159         sourceFile.read(buffer.data(), readLen);
160         if (!sourceFile.good()) {
161             LOGE("[FileContentCopy] SourceFile error! sys[%d]", errno);
162             return -E_INVALID_PATH;
163         }
164         targetFile.write(buffer.data(), readLen);
165         if (!targetFile.good()) {
166             LOGE("[FileContentCopy] TargetFile error! sys[%d]", errno);
167             return -E_INVALID_PATH;
168         }
169         leftLen -= readLen;
170     }
171     return E_OK;
172 }
173 
PackFileHeader(ofstream & targetFile,const FileInfo & fileInfo,uint32_t fileNum)174 static int PackFileHeader(ofstream &targetFile, const FileInfo &fileInfo, uint32_t fileNum)
175 {
176     if (fileInfo.deviceID.size() != DEVICE_ID_LEN) {
177         return -E_INVALID_ARGS;
178     }
179     vector<uint8_t> buffer(FILE_HEADER_LEN, 0);
180     vector<char> checksum(CHECKSUM_LEN, 0);
181     Parcel parcel(buffer.data(), FILE_HEADER_LEN);
182 
183     int errCode = parcel.WriteBlob(MAGIC.c_str(), MAGIC_LEN);
184     if (errCode != E_OK) {
185         return errCode;
186     }
187     // before current version package version is always 0
188     errCode = parcel.WriteUInt32(CURRENT_VERSION);
189     if (errCode != E_OK) {
190         return errCode;
191     }
192     errCode = parcel.WriteBlob(checksum.data(), CHECKSUM_LEN);
193     if (errCode != E_OK) {
194         return errCode;
195     }
196     errCode = parcel.WriteBlob(fileInfo.deviceID.c_str(), DEVICE_ID_LEN);
197     if (errCode != E_OK) {
198         return errCode;
199     }
200     errCode = parcel.WriteUInt32(fileInfo.dbType);
201     if (errCode != E_OK) {
202         return errCode;
203     }
204     errCode = parcel.WriteUInt32(fileNum);
205     if (errCode != E_OK) {
206         return errCode;
207     }
208     targetFile.write(reinterpret_cast<char *>(buffer.data()), buffer.size());
209     if (!targetFile.good()) {
210         return -E_INVALID_PATH;
211     }
212     return E_OK;
213 }
214 
CheckMagicHeader(Parcel & fileHeaderParcel)215 static int CheckMagicHeader(Parcel &fileHeaderParcel)
216 {
217     vector<char> buffer(MAGIC_LEN, 0);
218     (void)fileHeaderParcel.ReadBlob(buffer.data(), MAGIC_LEN);
219     if (fileHeaderParcel.IsError()) {
220         LOGE("[CheckMagicHeader]fileHeaderParcel error!");
221         return -E_PARSE_FAIL;
222     }
223     if (memcmp(MAGIC.c_str(), buffer.data(), MAGIC_LEN) != 0) {
224         return -E_INVALID_FILE;
225     }
226     return E_OK;
227 }
228 
UnpackFileHeader(ifstream & sourceFile,const string & sourceFileName,FileInfo & fileInfo,uint32_t & fileNum)229 static int UnpackFileHeader(ifstream &sourceFile, const string &sourceFileName, FileInfo &fileInfo, uint32_t &fileNum)
230 {
231     vector<uint8_t> fileHeader(FILE_HEADER_LEN, 0);
232     sourceFile.read(reinterpret_cast<char *>(fileHeader.data()), FILE_HEADER_LEN);
233     if (!sourceFile.good()) {
234         LOGE("UnpackFileHeader sourceFile error!");
235         return -E_INVALID_FILE;
236     }
237     Parcel parcel(fileHeader.data(), FILE_HEADER_LEN);
238     int errCode = CheckMagicHeader(parcel);
239     if (errCode != E_OK) {
240         return errCode;
241     }
242     uint32_t version;
243     vector<char> buffer(CHECKSUM_LEN, 0);
244     (void)parcel.ReadUInt32(version);
245     (void)parcel.ReadBlob(buffer.data(), CHECKSUM_LEN);
246     if (parcel.IsError()) {
247         LOGE("UnpackFileHeader parcel version error!");
248         return -E_PARSE_FAIL;
249     }
250     vector<char> checksum(CHECKSUM_LEN, 0);
251     errCode = GetChecksum(sourceFileName, checksum);
252     if (errCode != E_OK) {
253         LOGE("Get checksum failed.");
254         return errCode;
255     }
256     if (buffer != checksum) {
257         LOGE("Checksum check failed.");
258         return -E_INVALID_FILE;
259     }
260     buffer.resize(DEVICE_ID_LEN);
261     (void)parcel.ReadBlob(buffer.data(), DEVICE_ID_LEN);
262     if (parcel.IsError()) {
263         return -E_PARSE_FAIL;
264     }
265     fileInfo.deviceID.resize(DEVICE_ID_LEN);
266     fileInfo.deviceID.assign(buffer.begin(), buffer.end());
267     (void)parcel.ReadUInt32(fileInfo.dbType);
268     (void)parcel.ReadUInt32(fileNum);
269     if (parcel.IsError()) {
270         LOGE("UnpackFileHeader parcel dbType error!");
271         return -E_PARSE_FAIL;
272     }
273     return E_OK;
274 }
275 
PackFileContext(ofstream & targetFile,const FileContext & fileContext)276 static int PackFileContext(ofstream &targetFile, const FileContext &fileContext)
277 {
278     vector<uint8_t> buffer(FILE_CONTEXT_LEN, 0);
279     Parcel parcel(buffer.data(), FILE_CONTEXT_LEN);
280     int errCode = parcel.WriteBlob(fileContext.fileName, MAX_FILE_NAME_LEN);
281     if (errCode != E_OK) {
282         LOGE("PackFileContext fileContext fileName error!");
283         return errCode;
284     }
285     errCode = parcel.WriteUInt32(fileContext.fileType);
286     if (errCode != E_OK) {
287         return errCode;
288     }
289     errCode = parcel.WriteUInt32(0);
290     if (errCode != E_OK) {
291         return errCode;
292     }
293     errCode = parcel.WriteUInt64(fileContext.fileLen);
294     if (errCode != E_OK) {
295         return errCode;
296     }
297     errCode = parcel.WriteUInt64(fileContext.offset);
298     if (errCode != E_OK) {
299         return errCode;
300     }
301     targetFile.write(reinterpret_cast<char *>(buffer.data()), buffer.size());
302     if (!targetFile.good()) {
303         return -E_INVALID_PATH;
304     }
305     return E_OK;
306 }
307 
UnpackFileContext(ifstream & sourceFile,FileContext & fileContext)308 static int UnpackFileContext(ifstream &sourceFile, FileContext &fileContext)
309 {
310     vector<uint8_t> buffer(FILE_CONTEXT_LEN, 0);
311     sourceFile.read(reinterpret_cast<char *>(buffer.data()), buffer.size());
312     if (!sourceFile.good()) {
313         return -E_INVALID_PATH;
314     }
315     Parcel parcel(buffer.data(), FILE_CONTEXT_LEN);
316     (void)parcel.ReadBlob(fileContext.fileName, MAX_FILE_NAME_LEN);
317     (void)parcel.ReadUInt32(fileContext.fileType);
318     (void)parcel.ReadUInt32(fileContext.parentID);
319     (void)parcel.ReadUInt64(fileContext.fileLen);
320     (void)parcel.ReadUInt64(fileContext.offset);
321     if (parcel.IsError()) {
322         return -E_PARSE_FAIL;
323     }
324     return E_OK;
325 }
326 
PackFileContent(ofstream & targetFile,const string & sourcePath,const FileContext & fileContext)327 static int PackFileContent(ofstream &targetFile, const string &sourcePath, const FileContext &fileContext)
328 {
329     if (fileContext.fileType != OS::FILE) {
330         return E_OK;
331     }
332     string fileName = sourcePath + fileContext.fileName;
333     ifstream file(fileName, ios::in | ios::binary);
334     if (!file.good()) {
335         LOGE("[PackFileContent] File error! sys[%d]", errno);
336         return -E_INVALID_PATH;
337     }
338     file.seekg(0, ios_base::end);
339     if (!file.good()) {
340         LOGE("[PackFileContent]file error after seekg! sys[%d]", errno);
341         return -E_INVALID_PATH;
342     }
343     if (file.tellg() < 0) {
344         LOGE("[PackFileContent]file error after tellg! sys[%d]", errno);
345         return -E_INVALID_PATH;
346     }
347     uint64_t fileLen = static_cast<uint64_t>(file.tellg());
348     file.seekg(0, ios_base::beg);
349     if (!file.good()) {
350         LOGE("[PackFileContent]file error after seekg fileLen! sys[%d]", errno);
351         return -E_INVALID_PATH;
352     }
353 
354     return FileContentCopy(file, targetFile, fileLen);
355 }
356 
UnpackFileContent(ifstream & sourceFile,const string & targetPath,const FileContext & fileContext)357 static int UnpackFileContent(ifstream &sourceFile, const string &targetPath, const FileContext &fileContext)
358 {
359     if (fileContext.fileType != OS::FILE) {
360         return E_OK;
361     }
362 
363     string fileName = fileContext.fileName;
364     fileName = targetPath + FILE_SEPARATOR + fileName;
365 
366     // check if fileName contains the words ".."
367     std::string::size_type pos = fileName.find(INVALID_FILE_WORDS);
368     if (pos != std::string::npos) {
369         LOGE("[UnpackFileContent]fileName contains the words double dot!!!");
370         return -E_INVALID_PATH;
371     }
372 
373     ofstream file(fileName, ios::out | ios::binary);
374     if (!file.good()) {
375         file.close();
376         LOGE("[UnpackFileContent]Get checksum failed.");
377         return -E_INVALID_PATH;
378     }
379     int errCode = FileContentCopy(sourceFile, file, fileContext.fileLen);
380     file.close();
381     return errCode;
382 }
383 
WriteChecksum(const string & targetFile)384 static int WriteChecksum(const string &targetFile)
385 {
386     vector<char> checksum(CHECKSUM_LEN, 0);
387     int errCode = GetChecksum(targetFile, checksum);
388     if (errCode != E_OK) {
389         LOGE("Get checksum failed.");
390         return errCode;
391     }
392     ofstream targetHandle(targetFile, ios::in | ios::out | ios::binary);
393     if (!targetHandle.good()) {
394         Clear(targetHandle, targetFile);
395         LOGE("[WriteChecksum]targetHandle error, sys err [%d]", errno);
396         return -E_INVALID_PATH;
397     }
398     targetHandle.seekp(static_cast<int64_t>(MAGIC_LEN + Parcel::GetUInt32Len()), ios_base::beg);
399     if (!targetHandle.good()) {
400         Clear(targetHandle, targetFile);
401         LOGE("[WriteChecksum]targetHandle error after seekp, sys err [%d]", errno);
402         return -E_INVALID_PATH;
403     }
404     targetHandle.write(checksum.data(), checksum.size());
405     if (!targetHandle.good()) {
406         Clear(targetHandle, targetFile);
407         LOGE("[WriteChecksum]targetHandle error after write, sys err [%d]", errno);
408         return -E_INVALID_PATH;
409     }
410     targetHandle.close();
411     return E_OK;
412 }
413 
CopyFilePermissions(const string & sourceFile,const string & targetFile)414 static int CopyFilePermissions(const string &sourceFile, const string &targetFile)
415 {
416     uint32_t permissions;
417     int errCode = OS::GetFilePermissions(sourceFile, permissions);
418     if (errCode != E_OK) {
419         LOGE("Get file permissions failed.");
420         return errCode;
421     }
422     errCode = OS::SetFilePermissions(targetFile, permissions);
423     if (errCode != E_OK) {
424         LOGE("Set file permissions failed.");
425     }
426     return errCode;
427 }
428 
PackageFiles(const string & sourcePath,const string & targetFile,const FileInfo & fileInfo)429 int PackageFile::PackageFiles(const string &sourcePath, const string &targetFile,
430     const FileInfo &fileInfo)
431 {
432     int errCode = ExePackage(sourcePath, targetFile, fileInfo);
433     if (errno == EKEYREVOKED) {
434         errCode = -E_EKEYREVOKED;
435         LOGE("[PackageFile][PackageFiles] Forbid access files errCode [%d].", errCode);
436     }
437     return errCode;
438 }
439 
GetPackageVersion(const std::string & sourceFile,uint32_t & version)440 int PackageFile::GetPackageVersion(const std::string &sourceFile, uint32_t &version)
441 {
442     int errCode = E_OK;
443     vector<uint8_t> fileHeader(FILE_HEADER_LEN, 0);
444     Parcel parcel(fileHeader.data(), FILE_HEADER_LEN);
445 
446     ifstream sourceHandle(sourceFile, ios::in | ios::binary);
447     if (!sourceHandle.good()) {
448         LOGE("sourceHandle error, sys err [%d]", errno);
449         errCode = -E_INVALID_PATH;
450         goto END;
451     }
452 
453     sourceHandle.read(reinterpret_cast<char *>(fileHeader.data()), FILE_HEADER_LEN);
454     if (!sourceHandle.good()) {
455         LOGE("GetPackageVersion read sourceFile handle error!");
456         errCode = -E_INVALID_PATH;
457         goto END;
458     }
459 
460     errCode = CheckMagicHeader(parcel);
461     if (errCode != E_OK) {
462         errCode = -E_INVALID_PATH;
463         goto END;
464     }
465 
466     (void)parcel.ReadUInt32(version);
467 END:
468     if (errno == EKEYREVOKED) {
469         errCode = -E_EKEYREVOKED;
470         LOGE("[PackageFile][PackageFiles] Forbid access files by secLabel, errCode [%d].", errCode);
471     }
472     return errCode;
473 }
474 
ExePackage(const string & sourcePath,const string & targetFile,const FileInfo & fileInfo)475 int PackageFile::ExePackage(const string &sourcePath, const string &targetFile,
476     const FileInfo &fileInfo)
477 {
478     list<FileContext> fileContexts;
479     int errCode = GetFileContexts(sourcePath, fileContexts);
480     if (errCode != E_OK) {
481         return errCode;
482     }
483     if (fileContexts.empty()) {
484         return -E_EMPTY_PATH;
485     }
486     bool targetExists = OS::CheckPathExistence(targetFile);
487     ofstream targetHandle(targetFile, ios::out | ios::binary);
488     if (!targetHandle.good()) {
489         Clear(targetHandle, targetFile);
490         LOGE("[PackageFiles]targetHandle error, sys err [%d], [%zu]", errno, fileContexts.size());
491         return -E_INVALID_PATH;
492     }
493 
494     if (!targetExists) {
495         errCode = CopyFilePermissions(sourcePath + FILE_SEPARATOR + string(fileContexts.front().fileName), targetFile);
496         if (errCode != E_OK) {
497             LOGE("Copy file fail when execute pack files! errCode = [%d]", errCode);
498             Clear(targetHandle, targetFile);
499             return errCode;
500         }
501     }
502 
503     errCode = PackFileHeader(targetHandle, fileInfo, static_cast<uint32_t>(fileContexts.size()));
504     if (errCode != E_OK) {
505         Clear(targetHandle, targetFile);
506         LOGE("[PackageFiles]Pack file header err[%d]!!!", errCode);
507         return errCode;
508     }
509     // FILE_HEADER_LEN is 92, FILE_CONTEXT_LEN is 280, fileContexts.size() < UINT_MAX, the offset will never overflow.
510     uint64_t offset = FILE_HEADER_LEN + FILE_CONTEXT_LEN * static_cast<uint64_t>(fileContexts.size());
511     for (auto &file : fileContexts) {
512         file.offset = offset;
513         errCode = PackFileContext(targetHandle, file);
514         if (errCode != E_OK) {
515             Clear(targetHandle, targetFile);
516             LOGE("[PackageFiles]Pack file context err[%d]!!!", errCode);
517             return errCode;
518         }
519         offset += file.fileLen;
520     }
521     for (const auto &file : fileContexts) {
522         // If file type is path no need pack content in PackFileContent
523         errCode = PackFileContent(targetHandle, sourcePath, file);
524         if (errCode != E_OK) {
525             Clear(targetHandle, targetFile);
526             return errCode;
527         }
528     }
529     targetHandle.close();
530     return WriteChecksum(targetFile);
531 }
532 
UnpackFile(const string & sourceFile,const string & targetPath,FileInfo & fileInfo)533 int PackageFile::UnpackFile(const string &sourceFile, const string &targetPath, FileInfo &fileInfo)
534 {
535     ifstream sourceHandle(sourceFile, ios::in | ios::binary);
536     if (!sourceHandle.good()) {
537         LOGE("sourceHandle error, sys err [%d]", errno);
538         return -E_INVALID_PATH;
539     }
540     uint32_t fileNum;
541     int errCode = UnpackFileHeader(sourceHandle, sourceFile, fileInfo, fileNum);
542     if (errCode != E_OK) {
543         return errCode;
544     }
545     FileContext fileContext;
546     list<FileContext> fileContexts;
547     sourceHandle.seekg(static_cast<int64_t>(FILE_HEADER_LEN), ios_base::beg);
548     if (!sourceHandle.good()) {
549         return -E_INVALID_PATH;
550     }
551     for (uint32_t fileCount = 0; fileCount < fileNum; fileCount++) {
552         errCode = UnpackFileContext(sourceHandle, fileContext);
553         if (errCode != E_OK) {
554             return errCode;
555         }
556         fileContexts.push_back(fileContext);
557     }
558 
559     for (const auto &file : fileContexts) {
560         if (file.fileType == OS::PATH) {
561             std::string dirPath = targetPath + "/" + std::string(file.fileName);
562             if (!OS::CheckPathExistence(dirPath) && OS::MakeDBDirectory(dirPath) != E_OK) {
563                 return -E_SYSTEM_API_FAIL;
564             }
565             continue;
566         }
567         errCode = UnpackFileContent(sourceHandle, targetPath, file);
568         if (errCode != E_OK) {
569             return errCode;
570         }
571     }
572     return E_OK;
573 }
574 }
575