From 88748bd890c363241f71d1c780b6fba6cbf4e8b2 Mon Sep 17 00:00:00 2001 From: Alexander Peshkov Date: Fri, 3 Jun 2016 15:52:46 +0300 Subject: [PATCH] Fixed CORE-5213 --- lang_helpers/gds_codes.ftn | 2 + lang_helpers/gds_codes.pas | 2 + src/include/gen/codetext.h | 1 + src/include/gen/iberror.h | 6 +- src/include/gen/msgs.h | 1 + src/include/gen/sql_code.h | 1 + src/include/gen/sql_state.h | 1 + src/jrd/CryptoManager.cpp | 120 ++++++++++++++++++++++++++++++++---- src/jrd/CryptoManager.h | 15 ++++- src/jrd/ods.h | 4 +- src/msgs/facilities2.sql | 2 +- src/msgs/messages2.sql | 1 + src/msgs/system_errors2.sql | 1 + 13 files changed, 138 insertions(+), 19 deletions(-) diff --git a/lang_helpers/gds_codes.ftn b/lang_helpers/gds_codes.ftn index c4847683e19..4b4b861f688 100644 --- a/lang_helpers/gds_codes.ftn +++ b/lang_helpers/gds_codes.ftn @@ -1636,6 +1636,8 @@ C -- PARAMETER (GDS__wrong_prvlg = 335545111) INTEGER*4 GDS__miss_prvlg PARAMETER (GDS__miss_prvlg = 335545112) + INTEGER*4 GDS__crypt_checksum + PARAMETER (GDS__crypt_checksum = 335545113) INTEGER*4 GDS__gfix_db_name PARAMETER (GDS__gfix_db_name = 335740929) INTEGER*4 GDS__gfix_invalid_sw diff --git a/lang_helpers/gds_codes.pas b/lang_helpers/gds_codes.pas index e1e6a98c860..bfdd9c1f64e 100644 --- a/lang_helpers/gds_codes.pas +++ b/lang_helpers/gds_codes.pas @@ -1631,6 +1631,8 @@ gds_wrong_prvlg = 335545111; isc_miss_prvlg = 335545112; gds_miss_prvlg = 335545112; + isc_crypt_checksum = 335545113; + gds_crypt_checksum = 335545113; isc_gfix_db_name = 335740929; gds_gfix_db_name = 335740929; isc_gfix_invalid_sw = 335740930; diff --git a/src/include/gen/codetext.h b/src/include/gen/codetext.h index 5ebe00f292c..903a0b2154c 100644 --- a/src/include/gen/codetext.h +++ b/src/include/gen/codetext.h @@ -814,6 +814,7 @@ static const struct { {"max_idx_depth", 335545110}, {"wrong_prvlg", 335545111}, {"miss_prvlg", 335545112}, + {"crypt_checksum", 335545113}, {"gfix_db_name", 335740929}, {"gfix_invalid_sw", 335740930}, {"gfix_incmp_sw", 335740932}, diff --git a/src/include/gen/iberror.h b/src/include/gen/iberror.h index 57ccbdc588d..f7816b02de2 100644 --- a/src/include/gen/iberror.h +++ b/src/include/gen/iberror.h @@ -848,6 +848,7 @@ const ISC_STATUS isc_encrypt_error = 335545109L; const ISC_STATUS isc_max_idx_depth = 335545110L; const ISC_STATUS isc_wrong_prvlg = 335545111L; const ISC_STATUS isc_miss_prvlg = 335545112L; +const ISC_STATUS isc_crypt_checksum = 335545113L; const ISC_STATUS isc_gfix_db_name = 335740929L; const ISC_STATUS isc_gfix_invalid_sw = 335740930L; const ISC_STATUS isc_gfix_incmp_sw = 335740932L; @@ -1319,7 +1320,7 @@ const ISC_STATUS isc_trace_switch_user_only = 337182757L; const ISC_STATUS isc_trace_switch_param_miss = 337182758L; const ISC_STATUS isc_trace_param_act_notcompat = 337182759L; const ISC_STATUS isc_trace_mandatory_switch_miss = 337182760L; -const ISC_STATUS isc_err_max = 1263; +const ISC_STATUS isc_err_max = 1264; #else /* c definitions */ @@ -2137,6 +2138,7 @@ const ISC_STATUS isc_err_max = 1263; #define isc_max_idx_depth 335545110L #define isc_wrong_prvlg 335545111L #define isc_miss_prvlg 335545112L +#define isc_crypt_checksum 335545113L #define isc_gfix_db_name 335740929L #define isc_gfix_invalid_sw 335740930L #define isc_gfix_incmp_sw 335740932L @@ -2608,7 +2610,7 @@ const ISC_STATUS isc_err_max = 1263; #define isc_trace_switch_param_miss 337182758L #define isc_trace_param_act_notcompat 337182759L #define isc_trace_mandatory_switch_miss 337182760L -#define isc_err_max 1263 +#define isc_err_max 1264 #endif diff --git a/src/include/gen/msgs.h b/src/include/gen/msgs.h index 30e91e9aac7..bedc39178dd 100644 --- a/src/include/gen/msgs.h +++ b/src/include/gen/msgs.h @@ -817,6 +817,7 @@ Data source : @4"}, /* eds_statement */ {335545110, "Maximum index depth (@1 levels) is reached"}, /* max_idx_depth */ {335545111, "System privilege @1 does not exist"}, /* wrong_prvlg */ {335545112, "Unable to perform operation: system privilege @1 is missing"}, /* miss_prvlg */ + {335545113, "Invalid or missing checksum of encrypted database"}, /* crypt_checksum */ {335740929, "data base file name (@1) already given"}, /* gfix_db_name */ {335740930, "invalid switch @1"}, /* gfix_invalid_sw */ {335740932, "incompatible switch combination"}, /* gfix_incmp_sw */ diff --git a/src/include/gen/sql_code.h b/src/include/gen/sql_code.h index 4c67dba39ac..ee84e1dc3d6 100644 --- a/src/include/gen/sql_code.h +++ b/src/include/gen/sql_code.h @@ -813,6 +813,7 @@ static const struct { {335545110, -904}, /* 790 max_idx_depth */ {335545111, -901}, /* 791 wrong_prvlg */ {335545112, -902}, /* 792 miss_prvlg */ + {335545113, -902}, /* 793 crypt_checksum */ {335740929, -901}, /* 1 gfix_db_name */ {335740930, -901}, /* 2 gfix_invalid_sw */ {335740932, -901}, /* 4 gfix_incmp_sw */ diff --git a/src/include/gen/sql_state.h b/src/include/gen/sql_state.h index 95a8c33acbe..5e1ba1acff0 100644 --- a/src/include/gen/sql_state.h +++ b/src/include/gen/sql_state.h @@ -813,6 +813,7 @@ static const struct { {335545110, "54000"}, // 790 max_idx_depth {335545111, "0A000"}, // 791 wrong_prvlg {335545112, "28000"}, // 792 miss_prvlg + {335545113, "XX000"}, // 793 crypt_checksum {335740929, "00000"}, // 1 gfix_db_name {335740930, "00000"}, // 2 gfix_invalid_sw {335740932, "00000"}, // 4 gfix_incmp_sw diff --git a/src/jrd/CryptoManager.cpp b/src/jrd/CryptoManager.cpp index 0ab5ec7f1f7..20b1164d18b 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); } @@ -224,17 +235,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), @@ -344,6 +344,9 @@ namespace Jrd { (Arg::Gds(isc_bad_crypt_key) << keyName).raise(); } } + + if (flags & CRYPT_HDR_INIT) + checkDigitalSignature(hdr); } void CryptoManager::loadPlugin(const char* pluginName) @@ -526,6 +529,8 @@ namespace Jrd { header->hdr_crypt_page = 1; header->hdr_flags |= Ods::hdr_crypt_process; process = true; + + digitalySignDatabase(hdr); } catch (const Exception&) { @@ -865,6 +870,8 @@ namespace Jrd { hdr.setClumplets(hc); } } + + digitalySignDatabase(hdr); } bool CryptoManager::read(thread_db* tdbb, FbStatusVector* sv, Ods::pag* page, IOCallback* io) @@ -1172,4 +1179,93 @@ 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)) + { + ClumpletWriter hc(ClumpletWriter::UnTagged, hdr->hdr_page_size); + hdr.getClumplets(hc); + if (!hc.find(Ods::HDR_crypt_checksum)) + Arg::Gds(isc_crypt_checksum).raise(); + + string sig1, sig2; + hc.getString(sig1); + calcDigitalSignature(sig2, hdr); + if (sig1 != sig2) + Arg::Gds(isc_crypt_checksum).raise(); + } + } + } // namespace Jrd diff --git a/src/jrd/CryptoManager.h b/src/jrd/CryptoManager.h index b9d7e211143..0a5789e1b21 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 @@ class CryptoManager FB_FINAL : public Firebird::PermanentStorage, public BarSync 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 3c0345b20f5..a042bc16539 100644 --- a/src/jrd/ods.h +++ b/src/jrd/ods.h @@ -413,7 +413,6 @@ struct header_page ULONG hdr_oldest_snapshot; // Oldest snapshot of active transactions SLONG hdr_backup_pages; // The amount of pages in files locked for backup ULONG hdr_crypt_page; // Page at which processing is in progress - ULONG hdr_top_crypt; // Last page to crypt TEXT hdr_crypt_plugin[32]; // Name of plugin used to crypt this DB SLONG hdr_att_high; // High word of the next attachment counter USHORT hdr_tra_high[4]; // High words of the transaction counters @@ -433,7 +432,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 @@ -444,7 +443,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 diff --git a/src/msgs/facilities2.sql b/src/msgs/facilities2.sql index 854ce226068..e4c0ceba7af 100644 --- a/src/msgs/facilities2.sql +++ b/src/msgs/facilities2.sql @@ -1,7 +1,7 @@ /* MAX_NUMBER is the next number to be used, always one more than the highest message number. */ set bulk_insert INSERT INTO FACILITIES (LAST_CHANGE, FACILITY, FAC_CODE, MAX_NUMBER) VALUES (?, ?, ?, ?); -- -('2016-05-26 12:14:49', 'JRD', 0, 793) +('2016-06-03 14:53:38', 'JRD', 0, 794) ('2015-03-17 18:33:00', 'QLI', 1, 533) ('2015-01-07 18:01:51', 'GFIX', 3, 134) ('1996-11-07 13:39:40', 'GPRE', 4, 1) diff --git a/src/msgs/messages2.sql b/src/msgs/messages2.sql index 2af91b55498..626ba5d7dd1 100644 --- a/src/msgs/messages2.sql +++ b/src/msgs/messages2.sql @@ -900,6 +900,7 @@ Data source : @4', NULL, NULL) ('max_idx_depth', NULL, 'btr.cpp', NULL, 0, 790, NULL, 'Maximum index depth (@1 levels) is reached', NULL, NULL); ('wrong_prvlg', 'SCL_convert_privilege', 'scl.epp', NULL, 0, 791, NULL, 'System privilege @1 does not exist', NULL, NULL); ('miss_prvlg', 'validateAccess', 'jrd.cpp', NULL, 0, 792, NULL, 'Unable to perform operation: system privilege @1 is missing', NULL, NULL); +('crypt_checksum', 'CryptoManager::checkDigitalSignature', 'CryptoManager.cpp', NULL, 0, 793, NULL, 'Invalid or missing checksum of encrypted database', NULL, NULL); -- QLI (NULL, NULL, NULL, NULL, 1, 0, NULL, 'expected type', NULL, NULL); (NULL, NULL, NULL, NULL, 1, 1, NULL, 'bad block type', NULL, NULL); diff --git a/src/msgs/system_errors2.sql b/src/msgs/system_errors2.sql index f29969ad44a..282a7d7f88e 100644 --- a/src/msgs/system_errors2.sql +++ b/src/msgs/system_errors2.sql @@ -799,6 +799,7 @@ set bulk_insert INSERT INTO SYSTEM_ERRORS (SQL_CODE, SQL_CLASS, SQL_SUBCLASS, FA (-904, '54', '000', 0, 790, 'max_idx_depth', NULL, NULL) (-901, '0A', '000', 0, 791, 'wrong_prvlg', NULL, NULL) (-902, '28', '000', 0, 792, 'miss_prvlg', NULL, NULL) +(-902, 'XX', '000', 0, 793, 'crypt_checksum', NULL, NULL) -- GFIX (-901, '00', '000', 3, 1, 'gfix_db_name', NULL, NULL) (-901, '00', '000', 3, 2, 'gfix_invalid_sw', NULL, NULL)