From 5fbbae63faa582eb305fbcfd07fb38488fcee113 Mon Sep 17 00:00:00 2001 From: Alexander Peshkov Date: Fri, 3 Jun 2016 16:03:03 +0300 Subject: [PATCH] Backported fix for CORE-5213 --- src/jrd/CryptoManager.cpp | 124 ++++++++++++++++++++++++++++++++++---- src/jrd/CryptoManager.h | 15 ++++- src/jrd/ods.h | 3 +- 3 files changed, 127 insertions(+), 15 deletions(-) diff --git a/src/jrd/CryptoManager.cpp b/src/jrd/CryptoManager.cpp index bc6487b74a..cada593ce7 100644 --- a/src/jrd/CryptoManager.cpp +++ b/src/jrd/CryptoManager.cpp @@ -63,6 +63,17 @@ namespace { return 0; } + const UCHAR CRYPT_RELEASE = LCK_SR; + const UCHAR CRYPT_NORMAL = LCK_PR; + const UCHAR CRYPT_CHANGE = LCK_PW; + const UCHAR CRYPT_INIT = LCK_EX; + + const int MAX_PLUGIN_NAME_LEN = 31; +} + + +namespace Jrd { + class Header { protected: @@ -88,7 +99,7 @@ namespace { // This routine is getting clumplets from header page but is not ready to handle continuation // Fortunately, modern pages of size 4k and bigger can fit everything on one page. - void getClumplets(ClumpletWriter& writer) + void getClumplets(ClumpletWriter& writer) const { writer.reset(header->hdr_data, header->hdr_end - HDR_SIZE); } @@ -233,17 +244,6 @@ namespace { AutoPtr > buffer; }; - const UCHAR CRYPT_RELEASE = LCK_SR; - const UCHAR CRYPT_NORMAL = LCK_PR; - const UCHAR CRYPT_CHANGE = LCK_PW; - const UCHAR CRYPT_INIT = LCK_EX; - - const int MAX_PLUGIN_NAME_LEN = 31; -} - - -namespace Jrd { - CryptoManager::CryptoManager(thread_db* tdbb) : PermanentStorage(*tdbb->getDatabase()->dbb_permanent), sync(this), @@ -353,6 +353,9 @@ namespace Jrd { (Arg::Gds(isc_bad_crypt_key) << keyName).raise(); } } + + if (flags & CRYPT_HDR_INIT) + checkDigitalSignature(hdr); } void CryptoManager::loadPlugin(const char* pluginName) @@ -533,6 +536,8 @@ namespace Jrd { header->hdr_crypt_page = 1; header->hdr_flags |= Ods::hdr_crypt_process; process = true; + + digitalySignDatabase(hdr); } catch (const Exception&) { @@ -870,6 +875,8 @@ namespace Jrd { hdr.setClumplets(hc); } } + + digitalySignDatabase(hdr); } bool CryptoManager::read(thread_db* tdbb, FbStatusVector* sv, Ods::pag* page, IOCallback* io) @@ -1177,4 +1184,97 @@ namespace Jrd { st.check(); } + void CryptoManager::addClumplet(string& signature, ClumpletReader& block, UCHAR tag) + { + if (block.find(tag)) + { + string tmp; + block.getString(tmp); + signature += ' '; + signature += tmp; + } + } + + void CryptoManager::calcDigitalSignature(string& signature, const Header& hdr) + { + /* + We use the following items to calculate digital signature (hash of encrypted string) + for database: + hdr_flags & (hdr_crypt_process | hdr_encrypted) + hdr_crypt_page + hdr_crypt_plugin + HDR_crypt_key + HDR_crypt_hash + */ + + signature.printf("%d %d %d %s", + hdr->hdr_flags & Ods::hdr_crypt_process ? 1 : 0, + hdr->hdr_flags & Ods::hdr_encrypted ? 1 : 0, + hdr->hdr_crypt_page, + hdr->hdr_crypt_plugin); + + ClumpletWriter hc(ClumpletWriter::UnTagged, hdr->hdr_page_size); + hdr.getClumplets(hc); + + addClumplet(signature, hc, Ods::HDR_crypt_key); + addClumplet(signature, hc, Ods::HDR_crypt_hash); + + const unsigned QUANTUM = 16; + signature += string(QUANTUM - 1, '$'); + unsigned len = signature.length(); + len &= ~(QUANTUM - 1); + + loadPlugin(hdr->hdr_crypt_plugin); + + string enc; + FbLocalStatus sv; + cryptPlugin->encrypt(&sv, len, signature.c_str(), enc.getBuffer(len)); + if (sv->getState() & IStatus::STATE_ERRORS) + Arg::StatusVector(&sv).raise(); + + Sha1::hashBased64(signature, enc); + } + + + void CryptoManager::digitalySignDatabase(CchHdr& hdr) + { + ClumpletWriter hc(ClumpletWriter::UnTagged, hdr->hdr_page_size); + hdr.getClumplets(hc); + + bool wf = hc.find(Ods::HDR_crypt_checksum); + hc.deleteWithTag(Ods::HDR_crypt_checksum); + + if (hdr->hdr_flags & (Ods::hdr_crypt_process | Ods::hdr_encrypted)) + { + wf = true; + string signature; + calcDigitalSignature(signature, hdr); + hc.insertString(Ods::HDR_crypt_checksum, signature); + } + + if (wf) + hdr.setClumplets(hc); + } + + void CryptoManager::checkDigitalSignature(const Header& hdr) + { + // if (hdr->hdr_flags & (Ods::hdr_crypt_process | Ods::hdr_encrypted)) + // Restricted check to ensure compatibility with 3.0.0 databases - only for active crypt process + if (hdr->hdr_flags & Ods::hdr_crypt_process) + { + const char* message = "Invalid or missing checksum of encrypted database"; + + ClumpletWriter hc(ClumpletWriter::UnTagged, hdr->hdr_page_size); + hdr.getClumplets(hc); + if (!hc.find(Ods::HDR_crypt_checksum)) + (Arg::Gds(isc_random) << message).raise(); + + string sig1, sig2; + hc.getString(sig1); + calcDigitalSignature(sig2, hdr); + if (sig1 != sig2) + (Arg::Gds(isc_random) << message).raise(); + } + } + } // namespace Jrd diff --git a/src/jrd/CryptoManager.h b/src/jrd/CryptoManager.h index b9d7e21114..0a5789e1b2 100644 --- a/src/jrd/CryptoManager.h +++ b/src/jrd/CryptoManager.h @@ -46,7 +46,15 @@ class Config; namespace Ods { - struct pag; + +struct pag; + +} + +namespace Firebird { + +class ClumpletReader; + } namespace Jrd { @@ -363,6 +371,11 @@ private: static const unsigned CRYPT_HDR_INIT = 0x01; static const unsigned CRYPT_HDR_NOWAIT = 0x02; + void addClumplet(Firebird::string& value, Firebird::ClumpletReader& block, UCHAR tag); + void calcDigitalSignature(Firebird::string& signature, const class Header& hdr); + void digitalySignDatabase(class CchHdr& hdr); + void checkDigitalSignature(const class Header& hdr); + BarSync sync; Firebird::MetaName keyName; ULONG currentPage; diff --git a/src/jrd/ods.h b/src/jrd/ods.h index ce90ebafa9..79e3cc38e1 100644 --- a/src/jrd/ods.h +++ b/src/jrd/ods.h @@ -426,7 +426,7 @@ const UCHAR HDR_root_file_name = 1; // Original name of root file const UCHAR HDR_file = 2; // Secondary file const UCHAR HDR_last_page = 3; // Last logical page number of file const UCHAR HDR_sweep_interval = 4; // Transactions between sweeps -const UCHAR HDR_password_file_key = 5; // Key to compare to password db +const UCHAR HDR_crypt_checksum = 5; // Checksum of critical crypt parameters const UCHAR HDR_difference_file = 6; // Delta file that is used during backup lock const UCHAR HDR_backup_guid = 7; // UID generated on each switch into backup mode const UCHAR HDR_crypt_key = 8; // Name of a key used to crypt database @@ -437,7 +437,6 @@ const UCHAR HDR_max = 10; // Maximum HDR_clump value const USHORT hdr_active_shadow = 0x1; // 1 file is an active shadow file const USHORT hdr_force_write = 0x2; // 2 database is forced write -// const USHORT hdr_no_checksums = 0x4; // 4 don't calculate checksums, not used since ODS 12 const USHORT hdr_crypt_process = 0x4; // 4 Encryption status is changing now const USHORT hdr_no_reserve = 0x8; // 8 don't reserve space for versions const USHORT hdr_SQL_dialect_3 = 0x10; // 16 database SQL dialect 3