File encryption method reading whole files into a dynamic stack buffer.
authorZbigniew Kostrzewa <z.kostrzewa@samsung.com>
Mon, 11 Feb 2013 08:36:21 +0000 (09:36 +0100)
committerGerrit Code Review <gerrit2@kim11>
Tue, 12 Feb 2013 08:39:25 +0000 (17:39 +0900)
[Issue#] LINUXWRT-91
[Problem] Encryption method tried to encrypt whole file at once which
may introduce problems for large files. What is more, buffer for file data
was dynamically allocated on stack but such feature is not compliant
with the C++11 standard.
[Cause] N/A
[Solution] Encrypt files chunk by chunk.
[SCMRequest] N/A
[Verification]
1. Build repository.
2. Install a test widget without encryption. Widget should launch
   successfully.
3. Install the same widget with encryption enabled. Widget should launch
   successfully and present the same results.

Tip: To enforce widget's content encryption add following line to
widget's configuration file:

<tizen:setting encryption="enable" />

Change-Id: Ieb1c9dfeede28bfd69f910b1d34d650b6a353795

src/jobs/widget_install/task_encrypt_resource.cpp

index 0d61b81..81e6014 100644 (file)
 #undef __USE_FILE_OFFSET64
 
 #include <unistd.h>
-#include <string>
 #include <sys/stat.h>
 #include <fts.h>
 #include <string.h>
 #include <errno.h>
+#include <cstdio>
+
+#include <memory>
 
 #include <dpl/log/log.h>
 #include <dpl/errno_string.h>
 #include <dpl/foreach.h>
+#include <dpl/scoped_fclose.h>
 #include <dpl/wrt-dao-ro/global_config.h>
+#include <dpl/string.h>
 
 #include <widget_install/job_widget_install.h>
 #include <widget_install/widget_install_context.h>
@@ -43,6 +47,8 @@ using namespace WrtDB;
 using namespace WRTEncryptor;
 
 namespace {
+const std::size_t ENCRYPTION_CHUNK_MAX_SIZE = 1024; // bytes
+
 std::set<std::string>& getSupportedForEncryption()
 {
     static std::set<std::string> encryptSet;
@@ -63,6 +69,94 @@ bool isSupportedForEncryption(const std::string &file)
     }
     return false;
 }
+
+/**
+ * Opens a file.
+ *
+ * @param path Path to a file.
+ * @param mode Mode.
+ * @return Stream handle.
+ * @throw ExtractFileFailed If error (other than EINTR) occurs.
+ */
+FILE* openFile(const std::string& path, const std::string& mode)
+{
+    FILE* result = NULL;
+
+    do
+    {
+        result = fopen(path.c_str(), mode.c_str());
+    } while ((NULL == result) && (EINTR == errno));
+
+    if (NULL == result)
+    {
+        ThrowMsg(Jobs::WidgetInstall::Exceptions::InternalError,
+                 "Could not open file " << path);
+    }
+
+    return result;
+}
+
+/**
+ * Reads bytes from a stream.
+ *
+ * @param buffer Buffer to read the bytes into.
+ * @param count Number of bytes to read.
+ * @param stream Stream to read from.
+ * @return Number of bytes read
+ * @throw ExtractFileFailed If error (other than EINTR) occurs.
+ */
+std::size_t readBytes(unsigned char* buffer, std::size_t count, FILE* stream)
+{
+    std::size_t result = std::fread(buffer,
+                                    sizeof(unsigned char),
+                                    count,
+                                    stream);
+
+    if (result != count)
+    {
+        int error = errno;
+        if (0 != std::ferror(stream))
+        {
+            if (EINTR != error)
+            {
+                ThrowMsg(Jobs::WidgetInstall::Exceptions::InternalError,
+                         "Error while reading data" <<
+                         " [" << DPL::GetErrnoString(error) << "]");
+            }
+        }
+    }
+
+    return result;
+}
+
+/**
+ * Writes bytes to a stream.
+ *
+ * @param buffer Data to write.
+ * @param count Number of bytes.
+ * @param stream Stream to write to.
+ * @throw ExtractFileFailed If error (other than EINTR) occurs.
+ */
+void writeBytes(unsigned char* buffer, std::size_t count, FILE* stream)
+{
+    std::size_t bytesWritten = 0;
+    std::size_t bytesToWrite = 0;
+    do
+    {
+        bytesToWrite = count - bytesWritten;
+        bytesWritten = std::fwrite(buffer + bytesWritten,
+                                   sizeof(unsigned char),
+                                   count - bytesWritten,
+                                   stream);
+        if ((bytesWritten != bytesToWrite) && (EINTR != errno))
+        {
+            int error = errno;
+            ThrowMsg(Jobs::WidgetInstall::Exceptions::InternalError,
+                     "Error while writing data" <<
+                     " [" << DPL::GetErrnoString(error) << "]");
+        }
+    } while ((bytesWritten != bytesToWrite) && (EINTR == errno));
+}
 }
 
 namespace Jobs {
@@ -139,76 +233,93 @@ void TaskEncryptResource::EncryptDirectory(std::string path)
 
 void TaskEncryptResource::EncryptFile(const std::string &fileName)
 {
-    Try
+    try
     {
-        LogDebug("Need to ecnrypt file Name " << fileName);
+        LogDebug("Encrypt file: " << fileName);
         std::string encFile = fileName + ".enc";
 
-        struct stat buf;
-        int ret = stat(fileName.c_str(), &buf);
-        if (ret == 0) {
-            size_t fileSize = buf.st_size;
+        struct stat info = {0};
+        if (stat(fileName.c_str(), &info) != 0)
+        {
+            int error = errno;
+            ThrowMsg(Exceptions::InternalError,
+                     "Could not access file " << fileName <<
+                     "[" << DPL::GetErrnoString(error) << "]");
+        }
+        const std::size_t fileSize = info.st_size;
 
-            FILE* resFp = fopen(fileName.c_str(), "r");
-            if (NULL == resFp) {
-                LogError("Couldnot open file : " << fileName);
-                return;
-            }
+        DPL::ScopedFClose inFile(openFile(fileName, "r"));
+        DPL::ScopedFClose outFile(openFile(encFile, "w"));
 
-            int blockSize = m_resEnc->GetBlockSize(fileSize);
-            LogDebug("Get block size : " << blockSize);
-
-            unsigned char readBuf[fileSize];
-            unsigned char outEncBuf[blockSize];
-            memset(readBuf, 0, fileSize);
-            memset(outEncBuf, 0, blockSize);
-
-            ret = fread(readBuf, sizeof(unsigned char), fileSize, resFp);
-            if (ret != fileSize) {
-                LogError(
-                    "Failed to read ecryption buffer with error: " <<
-                    strerror(errno) );
-                fclose(resFp);
-                return;
-            }
+        const std::size_t chunkSize = (fileSize > ENCRYPTION_CHUNK_MAX_SIZE
+                ? ENCRYPTION_CHUNK_MAX_SIZE : fileSize);
+        const int maxBlockSize = m_resEnc->GetBlockSize(chunkSize);
 
-            m_resEnc->EncryptChunk(readBuf, outEncBuf, fileSize);
+        std::unique_ptr<unsigned char[]> inChunk(new unsigned char[chunkSize]);
+        std::unique_ptr<unsigned char[]> outChunk;
 
-            FILE* encFp = fopen(encFile.c_str(), "w");
-            if (NULL == encFp) {
-                LogError("Failed to open ecryption file");
-                fclose(resFp);
-                return;
+        std::size_t bytesRead = 0;
+        int curBlockSize = 0;
+        do
+        {
+            bytesRead = readBytes(inChunk.get(), chunkSize, inFile.Get());
+            if (chunkSize != bytesRead)
+            {
+                curBlockSize = m_resEnc->GetBlockSize(bytesRead);
+                outChunk.reset(new unsigned char[curBlockSize]);
+            }
+            else
+            {
+                if (maxBlockSize != curBlockSize)
+                {
+                    curBlockSize = maxBlockSize;
+                    outChunk.reset(new unsigned char[curBlockSize]);
+                }
             }
-            fwrite(outEncBuf, sizeof(unsigned char), blockSize, encFp);
 
-            fclose(resFp);
-            fclose(encFp);
+            m_resEnc->EncryptChunk(inChunk.get(), outChunk.get(), bytesRead);
 
-            LogDebug("Success to encrypt file");
-            LogDebug("Remove unecrypted file : " << fileName);
+            writeBytes(outChunk.get(), curBlockSize, outFile.Get());
 
-            unlink(fileName.c_str());
-            if ((rename(encFile.c_str(), fileName.c_str())) != 0) {
-                ThrowMsg(Exceptions::ExtractFileFailed, fileName);
-            }
+        } while (0 == std::feof(inFile.Get()));
 
-            std::string realPath = fileName;
-            realPath.replace(0,
-                             m_context.locations->getTemporaryRootDir().length(),
-                             m_context.locations->getSourceDir());
+        LogDebug("File encrypted successfully");
 
-            WrtDB::EncryptedFileInfo info;
-            info.fileName = DPL::FromUTF8String(realPath);
-            info.fileSize = fileSize;
+        outFile.Reset();
+        inFile.Reset();
 
-            m_context.widgetConfig.encryptedFiles.insert(info);
+        LogDebug("Remove plain-text file: " << fileName);
+        if (0 != unlink(fileName.c_str()))
+        {
+            Throw(Exceptions::InternalError);
         }
+
+        LogDebug("Rename encrypted file");
+        if (0 != std::rename(encFile.c_str(), fileName.c_str()))
+        {
+            Throw(Exceptions::InternalError);
+        }
+
+        std::string realPath = fileName;
+        realPath.replace(0,
+                         m_context.locations->getTemporaryRootDir().length(),
+                         m_context.locations->getSourceDir());
+
+        WrtDB::EncryptedFileInfo fileInfo;
+        fileInfo.fileName = DPL::FromUTF8String(realPath);
+        fileInfo.fileSize = fileSize;
+
+        m_context.widgetConfig.encryptedFiles.insert(fileInfo);
+    }
+    Catch (Exceptions::InternalError)
+    {
+        ReThrowMsg(Exceptions::ExtractFileFailed, fileName);
     }
-    Catch(ResourceEncryptor::Exception::Base)
+    Catch (ResourceEncryptor::Exception::Base)
     {
         ReThrowMsg(Exceptions::ExtractFileFailed, fileName);
     }
 }
+
 } //namespace WidgetInstall
 } //namespace Jobs