Introduce ChecksumValidator 76/32776/26
authorPawel Wieczorek <p.wieczorek2@samsung.com>
Tue, 23 Dec 2014 13:53:59 +0000 (14:53 +0100)
committerPawel Wieczorek <p.wieczorek2@samsung.com>
Wed, 4 Mar 2015 10:12:23 +0000 (11:12 +0100)
ChecksumValidator computes checksums for every file listed in checksum
index equivalent using crypt(3). As a result, base64-like (custom
alphabet) encoded strings are produced and compared with last known
corresponding digest. Its 4-character prefix indicates used algorithm.
Class will be later used as an integrity mechanism extension.

Change-Id: Ibaba636bae30c747e8eac5561e9b130d4398518e

src/storage/CMakeLists.txt
src/storage/ChecksumValidator.cpp [new file with mode: 0644]
src/storage/ChecksumValidator.h [new file with mode: 0644]
test/CMakeLists.txt

index 7bccc8c..996b37a 100644 (file)
@@ -24,6 +24,7 @@ SET(CYNARA_LIB_CYNARA_STORAGE_PATH ${CYNARA_PATH}/storage)
 
 SET(LIB_CYNARA_STORAGE_SOURCES
     ${CYNARA_LIB_CYNARA_STORAGE_PATH}/BucketDeserializer.cpp
+    ${CYNARA_LIB_CYNARA_STORAGE_PATH}/ChecksumValidator.cpp
     ${CYNARA_LIB_CYNARA_STORAGE_PATH}/InMemoryStorageBackend.cpp
     ${CYNARA_LIB_CYNARA_STORAGE_PATH}/Integrity.cpp
     ${CYNARA_LIB_CYNARA_STORAGE_PATH}/Storage.cpp
@@ -51,6 +52,7 @@ SET_TARGET_PROPERTIES(
 TARGET_LINK_LIBRARIES(${TARGET_LIB_CYNARA_STORAGE}
     ${CYNARA_DEP_LIBRARIES}
     ${TARGET_CYNARA_COMMON}
+    crypt
     )
 
 INSTALL(TARGETS ${TARGET_LIB_CYNARA_STORAGE} DESTINATION ${LIB_INSTALL_DIR})
diff --git a/src/storage/ChecksumValidator.cpp b/src/storage/ChecksumValidator.cpp
new file mode 100644 (file)
index 0000000..1354ad1
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ *  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License
+ */
+/**
+ * @file        src/storage/ChecksumValidator.cpp
+ * @author      Pawel Wieczorek <p.wieczorek2@samsung.com>
+ * @version     1.0
+ * @brief       This file contains ChecksumValidator implementation.
+ */
+
+#include <algorithm>
+#include <cstdlib>
+#include <cstring>
+#include <fstream>
+#include <memory>
+#include <new>
+#include <sstream>
+#include <unistd.h>
+
+#include <config/PathConfig.h>
+#include <exceptions/ChecksumRecordCorruptedException.h>
+#include <exceptions/UnexpectedErrorException.h>
+#include <log/log.h>
+
+#include "ChecksumValidator.h"
+
+namespace Cynara {
+
+const std::string ChecksumValidator::m_checksumFilename(PathConfig::StoragePath::checksumFilename);
+const std::string ChecksumValidator::m_backupFilenameSuffix(
+        PathConfig::StoragePath::backupFilenameSuffix);
+
+void ChecksumValidator::load(std::istream &stream) {
+    m_sums.clear();
+
+    std::string line;
+    std::size_t lineNum = 1;
+    while (std::getline(stream, line, PathConfig::StoragePath::recordSeparator)) {
+        try {
+            std::size_t beginToken = 0;
+            std::string filename = parseFilename(line, beginToken);
+            std::string checksum = parseChecksum(line, beginToken);
+
+            m_sums.insert({ filename, checksum });
+            ++lineNum;
+        } catch (const ChecksumRecordCorruptedException &ex) {
+            throw ex.withLineNumber(lineNum);
+        }
+    }
+};
+
+const std::string ChecksumValidator::generate(const std::string &data) {
+    const char *checksum = crypt(data.c_str(), "$1$");
+
+    if (nullptr != checksum) {
+        return checksum;
+    }
+
+    int err = errno;
+    if (err == ENOSYS) {
+        LOGE("'crypt' function was not implemented; error [%d] : <%s>", err, strerror(err));
+    } else {
+        LOGE("'crypt' function error [%d] : <%s>", err, strerror(err));
+    }
+    throw UnexpectedErrorException(err, strerror(err));
+};
+
+void ChecksumValidator::compare(std::istream &stream, const std::string &pathname,
+                                bool isBackupValid) {
+    if (isChecksumIndex(pathname)) {
+        return;
+    }
+
+    std::unique_ptr<char, decltype(free)*> pathnameDuplicate(strdup(pathname.c_str()), free);
+    if (pathnameDuplicate == nullptr) {
+        LOGE("Insufficient memory available to allocate duplicate filename: <%s>",
+             pathname.c_str());
+        throw std::bad_alloc();
+    }
+
+    std::string filename(::basename(pathnameDuplicate.get()));
+    std::stringstream copyStream;
+
+    if (isBackupValid) {
+        auto backupSuffixPos = filename.rfind(m_backupFilenameSuffix);
+
+        if ((std::string::npos != backupSuffixPos) &&
+            (filename.size() == (backupSuffixPos + m_backupFilenameSuffix.size()))) {
+            filename.erase(backupSuffixPos);
+        }
+    }
+
+    std::copy(std::istreambuf_iterator<char>(stream),
+              std::istreambuf_iterator<char>(),
+              std::ostreambuf_iterator<char>(copyStream));
+    stream.seekg(0);
+
+    if (m_sums[filename] != generate(copyStream.str())) {
+        throw ChecksumRecordCorruptedException(m_sums[filename]);
+    }
+};
+
+const std::string ChecksumValidator::parseFilename(const std::string &line,
+                                                   std::size_t &beginToken) {
+    std::size_t filenameEndToken = line.find(PathConfig::StoragePath::fieldSeparator, beginToken);
+    if (filenameEndToken != beginToken && filenameEndToken != std::string::npos) {
+        auto filename = line.substr(beginToken, filenameEndToken - beginToken);
+        beginToken = filenameEndToken + 1;
+        return filename;
+    }
+    throw ChecksumRecordCorruptedException(line);
+}
+
+const std::string ChecksumValidator::parseChecksum(const std::string &line,
+                                                   std::size_t &beginToken) {
+    if (beginToken >= line.size()) {
+        throw ChecksumRecordCorruptedException(line);
+    }
+
+    auto checksum = line.substr(beginToken);
+    beginToken = line.size();
+    return checksum;
+}
+
+bool ChecksumValidator::isChecksumIndex(const std::string &filename) const {
+    auto checksum = m_dbPath + m_checksumFilename;
+    return (filename == checksum || filename == checksum + m_backupFilenameSuffix);
+}
+
+} // namespace Cynara
diff --git a/src/storage/ChecksumValidator.h b/src/storage/ChecksumValidator.h
new file mode 100644 (file)
index 0000000..bfd577b
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ *  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License
+ */
+/**
+ * @file        src/storage/ChecksumValidator.h
+ * @author      Pawel Wieczorek <p.wieczorek2@samsung.com>
+ * @version     1.0
+ * @brief       This file contains ChecksumValidator header.
+ */
+
+#ifndef SRC_STORAGE_CHECKSUMVALIDATOR_H_
+#define SRC_STORAGE_CHECKSUMVALIDATOR_H_
+
+#include <fstream>
+#include <memory>
+#include <string>
+#include <unordered_map>
+
+namespace Cynara {
+
+class ChecksumValidator;
+typedef std::unique_ptr<ChecksumValidator> ChecksumValidatorUniquePtr;
+
+class ChecksumValidator {
+public:
+    ChecksumValidator(const std::string &path) : m_dbPath(path) {}
+
+    void load(std::istream &stream);
+    void compare(std::istream &stream, const std::string &pathname, bool isBackupValid);
+
+    void clear(void) {
+        m_sums.clear();
+    }
+
+    static const std::string generate(const std::string &data);
+
+protected:
+    typedef std::unordered_map<std::string, std::string> Checksums;
+
+    bool isChecksumIndex(const std::string &pathname) const;
+
+    static const std::string parseFilename(const std::string &line, std::size_t &beginToken);
+    static const std::string parseChecksum(const std::string &line, std::size_t &beginToken);
+
+    Checksums m_sums;
+    const std::string m_dbPath;
+    static const std::string m_checksumFilename;
+    static const std::string m_backupFilenameSuffix;
+};
+
+} // namespace Cynara
+
+#endif // SRC_STORAGE_CHECKSUMVALIDATOR_H_
index d937d3c..f9b42a8 100644 (file)
@@ -63,6 +63,7 @@ SET(CYNARA_SOURCES_FOR_TESTS
     ${CYNARA_SRC}/helpers/creds-commons/creds-commons.cpp
     ${CYNARA_SRC}/service/main/CmdlineParser.cpp
     ${CYNARA_SRC}/storage/BucketDeserializer.cpp
+    ${CYNARA_SRC}/storage/ChecksumValidator.cpp
     ${CYNARA_SRC}/storage/InMemoryStorageBackend.cpp
     ${CYNARA_SRC}/storage/Integrity.cpp
     ${CYNARA_SRC}/storage/Storage.cpp