From 6edf503ae0ebf7ba4e72f47cc2a7626f4eb4c114 Mon Sep 17 00:00:00 2001 From: Gioacchino Mazzurco Date: Sat, 31 Aug 2019 16:22:24 +0200 Subject: [PATCH] Improve SQLCipher/SQLite support When compiled with SQLCipher it is now capable of handling databases created by SQLite-only versions Add support for SQLCipher on Android arm64 --- .../Android/prepare-toolchain-clang.sh | 15 +- libretroshare/src/util/retrodb.cc | 204 +++++++++++------- libretroshare/src/util/retrodb.h | 12 +- 3 files changed, 135 insertions(+), 96 deletions(-) diff --git a/build_scripts/Android/prepare-toolchain-clang.sh b/build_scripts/Android/prepare-toolchain-clang.sh index 322aee193..dcea8b2e2 100755 --- a/build_scripts/Android/prepare-toolchain-clang.sh +++ b/build_scripts/Android/prepare-toolchain-clang.sh @@ -325,13 +325,6 @@ build_sqlcipher() ################################################################################ " - case "${ANDROID_NDK_ARCH}" in - "arm64") - echo sqlcipher not supported for arm64 - return 0 - ;; - esac - B_dir="sqlcipher-${SQLCIPHER_SOURCE_VERSION}" rm -rf $B_dir @@ -342,6 +335,14 @@ build_sqlcipher() tar -xf $T_file cd $B_dir + case "${ANDROID_NDK_ARCH}" in + "arm64") + # SQLCipher config.sub is outdated and doesn't recognize newer architectures + rm config.sub + autoreconf --verbose --install --force + automake --add-missing --copy --force-missing + ;; + esac ./configure --with-pic --build=$(sh ./config.guess) \ --host=${cArch}-linux \ --prefix="${PREFIX}" --with-sysroot="${SYSROOT}" \ diff --git a/libretroshare/src/util/retrodb.cc b/libretroshare/src/util/retrodb.cc index 57d062812..a539c1676 100644 --- a/libretroshare/src/util/retrodb.cc +++ b/libretroshare/src/util/retrodb.cc @@ -3,7 +3,8 @@ * * * libretroshare: retroshare core library * * * - * Copyright 2012 Christopher Evi-Parker * + * Copyright (C) 2012 Christopher Evi-Parker * + * Copyright (C) 2019 Gioacchino Mazzurco * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License as * @@ -22,106 +23,143 @@ #include #include -#include -#include "util/rstime.h" -#include +#include +#include +#include -#include "retrodb.h" -#include "rsdbbind.h" +#include "util/rstime.h" +#include "util/retrodb.h" +#include "util/rsdbbind.h" +#include "util/stacktrace.h" +#include "util/rsdir.h" //#define RETRODB_DEBUG -#ifndef NO_SQLCIPHER -#define ENABLE_ENCRYPTED_DB -#endif - const int RetroDb::OPEN_READONLY = SQLITE_OPEN_READONLY; const int RetroDb::OPEN_READWRITE = SQLITE_OPEN_READWRITE; const int RetroDb::OPEN_READWRITE_CREATE = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE; -RetroDb::RetroDb(const std::string &dbPath, int flags, const std::string& key) : mDb(NULL), mKey(key) { +RetroDb::RetroDb(const std::string& dbPath, int flags, const std::string& key): + mDb(nullptr), mKey(key) +{ + bool alreadyExists = RsDirUtil::fileExists(dbPath); - int rc = sqlite3_open_v2(dbPath.c_str(), &mDb, flags, NULL); - - if(rc){ - std::cerr << "Can't open database, Error code: " << sqlite3_errmsg(mDb) - << std::endl; - sqlite3_close(mDb); - mDb = NULL; - return; - } - -#ifdef ENABLE_ENCRYPTED_DB - if(!mKey.empty()) - { - rc = sqlite3_key(mDb, mKey.c_str(), mKey.size()); - - if(rc){ - std::cerr << "Can't key database: " << sqlite3_errmsg(mDb) - << std::endl; - - sqlite3_close(mDb); - mDb = NULL; - return; - } - } - - char *err = NULL; - rc = sqlite3_exec(mDb, "PRAGMA cipher_migrate;", NULL, NULL, &err); - if (rc != SQLITE_OK) - { - std::cerr << "RetroDb::RetroDb(): Error upgrading database, error code: " << rc; - if (err) - { - std::cerr << ", " << err; - } - std::cerr << std::endl; - sqlite3_free(err); - } - - //Test DB for correct sqlcipher version - if (sqlite3_exec(mDb, "PRAGMA user_version;", NULL, NULL, NULL) != SQLITE_OK) + int rc = sqlite3_open_v2(dbPath.c_str(), &mDb, flags, nullptr); + if(rc) { - std::cerr << "RetroDb::RetroDb(): Failed to open database: " << dbPath << std::endl << "Trying with settings for sqlcipher version 3..."; - //Reopening the database with correct settings - rc = sqlite3_close(mDb); - mDb = NULL; - if(!rc) - rc = sqlite3_open_v2(dbPath.c_str(), &mDb, flags, NULL); - if(!rc && !mKey.empty()) - rc = sqlite3_key(mDb, mKey.c_str(), mKey.size()); - if(!rc) - rc = sqlite3_exec(mDb, "PRAGMA kdf_iter = 64000;", NULL, NULL, NULL); - if (!rc && (sqlite3_exec(mDb, "PRAGMA user_version;", NULL, NULL, NULL) == SQLITE_OK)) + RsErr() << __PRETTY_FUNCTION__ << " Can't open database, Error: " + << rc << " " << sqlite3_errmsg(mDb) << std::endl; + closeDb(); + print_stacktrace(); + return; + } + + if(alreadyExists) + { + /* If the database has been created by a RetroShare compiled without + * SQLCipher, open it as a plain SQLite database instead of failing + * miserably. If RetroShare has been compiled without SQLCipher but the + * database seems encrypted print a meaningful error message instead of + * crashing miserably. + * At some point we could implement a migration SQLite <-> SQLCipher + * mecanism and suggest it to the user, or give the option to the user + * to choice between plain SQLite or SQLCipher database, is some cases + * such as encrypted FS it might make sense to keep SQLite even if + * SQLCipher is availble for performance, as encryption is already + * provided at FS level. */ + + rc = sqlite3_exec( mDb, "PRAGMA schema_version;", + nullptr, nullptr, nullptr ); + if( rc == SQLITE_OK ) { - std::cerr << "\tSuccess" << std::endl; - } else { - std::cerr << "\tFailed, giving up" << std::endl; - sqlite3_close(mDb); - mDb = NULL; +#ifndef NO_SQLCIPHER + RsWarn() << __PRETTY_FUNCTION__ << " The database is not encrypted: " + << dbPath << std::endl; +#endif // ndef NO_SQLCIPHER + + return; + } + else + { +#ifdef NO_SQLCIPHER + RsErr() << __PRETTY_FUNCTION__ << " Error quering schema version." + << " Are you trying to open an encrypted database without " + << "compiling SQLCipher support?" << std::endl << std::endl; + print_stacktrace(); + closeDb(); +#else // def NO_SQLCIPHER + RsInfo() << __PRETTY_FUNCTION__ << " The database seems encrypted: " + << dbPath << std::endl; +#endif // def NO_SQLCIPHER + } + } + +#ifndef NO_SQLCIPHER + if(!mKey.empty()) + { + rc = sqlite3_key(mDb, mKey.c_str(), static_cast(mKey.size())); + + if(rc) + { + RsErr() << __PRETTY_FUNCTION__ << " Can't key database: " << rc + << " " << sqlite3_errmsg(mDb) << std::endl; + closeDb(); return; } } -#endif + + char* err = nullptr; + rc = sqlite3_exec(mDb, "PRAGMA cipher_migrate;", nullptr, nullptr, &err); + if (rc != SQLITE_OK) + { + RsErr() << __PRETTY_FUNCTION__ << " Error upgrading database, error " + << "code: " << rc << " " << err << std::endl; + sqlite3_free(err); + } + + // Test DB for correct sqlcipher version + if(sqlite3_exec( + mDb, "PRAGMA user_version;", + nullptr, nullptr, nullptr ) != SQLITE_OK) + { + RsWarn() << __PRETTY_FUNCTION__ << " Failed to open database: " + << dbPath << std::endl; + + //Reopening the database with correct settings + closeDb(); + if(!rc) rc = sqlite3_open_v2(dbPath.c_str(), &mDb, flags, nullptr); + if(!rc && !mKey.empty()) + rc = sqlite3_key(mDb, mKey.c_str(), static_cast(mKey.size())); + if(!rc) + rc = sqlite3_exec( mDb, "PRAGMA kdf_iter = 64000;", + nullptr, nullptr, nullptr ); + if (!rc && (sqlite3_exec( mDb, "PRAGMA user_version;", + nullptr, nullptr, nullptr ) == SQLITE_OK)) + { + RsInfo() << __PRETTY_FUNCTION__ << " Re-trying with settings for " + << "sqlcipher version 3 successed" << std::endl; + } + else + { + RsErr() << __PRETTY_FUNCTION__ << " Re-trying with settings for " + << "sqlcipher version 3 failed, giving up" << std::endl; + closeDb(); + return; + } + } +#endif // ndef NO_SQLCIPHER } -RetroDb::~RetroDb(){ +RetroDb::~RetroDb() { closeDb(); } - sqlite3_close(mDb); // no-op if mDb is NULL (https://www.sqlite.org/c3ref/close.html) - mDb = NULL ; -} - -void RetroDb::closeDb(){ - - int rc= sqlite3_close(mDb); - mDb = NULL ; - -#ifdef RETRODB_DEBUG - std::cerr << "RetroDb::closeDb(): Error code on close: " << rc << std::endl; -#else - (void)rc; -#endif +void RetroDb::closeDb() +{ + // no-op if mDb is nullptr (https://www.sqlite.org/c3ref/close.html) + int rc = sqlite3_close(mDb); + mDb = nullptr; + Dbg2() << __PRETTY_FUNCTION__ << " sqlite3_close return: " << rc + << std::endl; } #define TIME_LIMIT 3 diff --git a/libretroshare/src/util/retrodb.h b/libretroshare/src/util/retrodb.h index 8cf772c44..b48415152 100644 --- a/libretroshare/src/util/retrodb.h +++ b/libretroshare/src/util/retrodb.h @@ -19,8 +19,7 @@ * along with this program. If not, see . * * * *******************************************************************************/ -#ifndef RSSQLITE_H -#define RSSQLITE_H +#pragma once #ifdef NO_SQLCIPHER #include @@ -32,9 +31,10 @@ #include #include #include -#include "rsdbbind.h" -#include "contentvalue.h" +#include "util/rsdebug.h" +#include "util/rsdbbind.h" +#include "util/contentvalue.h" class RetroCursor; @@ -202,6 +202,8 @@ private: sqlite3* mDb; const std::string mKey; + + RS_SET_CONTEXT_DEBUG_LEVEL(3) }; /*! @@ -318,5 +320,3 @@ public: private: sqlite3_stmt* mStmt; }; - -#endif // RSSQLITE_H