diff --git a/examples/dbcrypt/CryptKeyHolder.cpp b/examples/dbcrypt/CryptKeyHolder.cpp index 3219c285d9..b234312a74 100644 --- a/examples/dbcrypt/CryptKeyHolder.cpp +++ b/examples/dbcrypt/CryptKeyHolder.cpp @@ -85,7 +85,7 @@ class CryptKeyHolder : public IKeyHolderPluginImpladdRef(); } @@ -139,12 +139,12 @@ private: { public: explicit CallbackInterface(CryptKeyHolder* p) - : parent(p) + : holder(p) { } unsigned int callback(unsigned int, const void*, unsigned int length, void* buffer) { - UCHAR k = parent->getKey(); + UCHAR k = holder->getKey(); if (!k) { return 0; @@ -157,16 +157,38 @@ private: return 1; } - IPluginModule* getModule() + private: + CryptKeyHolder* holder; + }; + + class NamedCallback : public ICryptKeyCallbackImpl + { + public: + NamedCallback(NamedCallback *n, const char* nm, UCHAR k) + : next(n), key(k) { - return &module; + strncpy(name, nm, sizeof(name)); + name[sizeof(name) - 1] = 0; } - private: - CryptKeyHolder* parent; + unsigned int callback(unsigned int, const void*, unsigned int length, void* buffer) + { + memcpy(buffer, &key, 1); + return 1; + } + + ~NamedCallback() + { + delete next; + } + + char name[32]; + NamedCallback* next; + UCHAR key; }; CallbackInterface callbackInterface; + NamedCallback *named; IPluginConfig* config; UCHAR key; @@ -174,18 +196,21 @@ private: AtomicCounter refCounter; IReferenceCounted* owner; - void noKeyError(CheckStatusWrapper* status); + IConfigEntry* getEntry(CheckStatusWrapper* status, const char* entryName); }; -void CryptKeyHolder::noKeyError(CheckStatusWrapper* status) +IConfigEntry* CryptKeyHolder::getEntry(CheckStatusWrapper* status, const char* entryName) { - ISC_STATUS_ARRAY vector; - vector[0] = isc_arg_gds; - vector[1] = isc_random; - vector[2] = isc_arg_string; - vector[3] = (ISC_STATUS) "Key not set"; - vector[4] = isc_arg_end; - status->setErrors(vector); + IConfig* def = config->getDefaultConfig(status); + if (status->getState() & Firebird::IStatus::STATE_ERRORS) + return NULL; + + IConfigEntry* confEntry = def->find(status, entryName); + def->release(); + if (status->getState() & Firebird::IStatus::STATE_ERRORS) + return NULL; + + return confEntry; } int CryptKeyHolder::keyCallback(CheckStatusWrapper* status, ICryptKeyCallback* callback) @@ -195,14 +220,7 @@ int CryptKeyHolder::keyCallback(CheckStatusWrapper* status, ICryptKeyCallback* c if (key != 0) return 1; - IConfig* def = config->getDefaultConfig(status); - if (status->getState() & Firebird::IStatus::STATE_ERRORS) - return 1; - - IConfigEntry* confEntry = def->find(status, "Auto"); - def->release(); - if (status->getState() & Firebird::IStatus::STATE_ERRORS) - return 1; + IConfigEntry* confEntry = getEntry(status, "Auto"); if (confEntry) { @@ -226,12 +244,33 @@ int CryptKeyHolder::keyCallback(CheckStatusWrapper* status, ICryptKeyCallback* c ICryptKeyCallback* CryptKeyHolder::keyHandle(CheckStatusWrapper* status, const char* keyName) { - if (strcmp(keyName, "sample") != 0) + if (keyName[0] == 0) + return &callbackInterface; + + for (NamedCallback* n = named; n; n = n->next) { - return NULL; + if (strcmp(keyName, n->name) == 0) + return n; } - return &callbackInterface; + char kn[40]; + strcpy(kn, "Key"); + strncat(kn, keyName, sizeof(kn)); + kn[sizeof(kn) - 1] = 0; + + IConfigEntry* confEntry = getEntry(status, kn); + if (confEntry) + { + UCHAR k = confEntry->getIntValue(); + confEntry->release(); + if (k > 0 && k < 256) + { + named = new NamedCallback(named, keyName, static_cast(k)); + return named; + } + } + + return NULL; } class Factory : public IPluginFactoryImpl diff --git a/examples/dbcrypt/DbCrypt.cpp b/examples/dbcrypt/DbCrypt.cpp index 01f5b91c3c..251fbbbc31 100644 --- a/examples/dbcrypt/DbCrypt.cpp +++ b/examples/dbcrypt/DbCrypt.cpp @@ -95,7 +95,8 @@ public: // ICryptPlugin implementation void encrypt(CheckStatusWrapper* status, unsigned int length, const void* from, void* to); void decrypt(CheckStatusWrapper* status, unsigned int length, const void* from, void* to); - void setKey(CheckStatusWrapper* status, unsigned int length, IKeyHolderPlugin** sources); + void setKey(CheckStatusWrapper* status, const char* keyName, + unsigned int length, IKeyHolderPlugin** sources); int release() { @@ -129,6 +130,7 @@ public: private: IPluginConfig* config; + char savedKeyName[32]; UCHAR key; AtomicCounter refCounter; @@ -139,11 +141,20 @@ private: void DbCrypt::noKeyError(CheckStatusWrapper* status) { + char msg[100]; + strcpy(msg, "Crypt key "); + if (savedKeyName[0]) + { + strcat(msg, savedKeyName); + strcat(msg, " "); + } + strcat(msg, "not set"); + ISC_STATUS_ARRAY vector; vector[0] = isc_arg_gds; vector[1] = isc_random; vector[2] = isc_arg_string; - vector[3] = (ISC_STATUS)"Key not set"; + vector[3] = (ISC_STATUS)msg; vector[4] = isc_arg_end; status->setErrors(vector); } @@ -186,13 +197,17 @@ void DbCrypt::decrypt(CheckStatusWrapper* status, unsigned int length, const voi } } -void DbCrypt::setKey(CheckStatusWrapper* status, unsigned int length, IKeyHolderPlugin** sources) +void DbCrypt::setKey(CheckStatusWrapper* status, const char* keyName, + unsigned int length, IKeyHolderPlugin** sources) { status->init(); if (key != 0) return; + strncpy(savedKeyName, keyName ? keyName : "", sizeof(savedKeyName)); + savedKeyName[sizeof(savedKeyName) - 1] = 0; + IConfig* def = config->getDefaultConfig(status); if (status->getState() & Firebird::IStatus::STATE_ERRORS) return; @@ -230,7 +245,7 @@ void DbCrypt::setKey(CheckStatusWrapper* status, unsigned int length, IKeyHolder for (unsigned n = 0; n < length; ++n) { - ICryptKeyCallback* callback = sources[n]->keyHandle(status, "sample"); + ICryptKeyCallback* callback = sources[n]->keyHandle(status, savedKeyName); if (status->getState() & Firebird::IStatus::STATE_ERRORS) return; diff --git a/lang_helpers/gds_codes.ftn b/lang_helpers/gds_codes.ftn index 567cbc9ed6..47de17742d 100644 --- a/lang_helpers/gds_codes.ftn +++ b/lang_helpers/gds_codes.ftn @@ -1626,6 +1626,10 @@ C -- PARAMETER (GDS__login_error = 335545106) INTEGER*4 GDS__already_opened PARAMETER (GDS__already_opened = 335545107) + INTEGER*4 GDS__bad_crypt_key + PARAMETER (GDS__bad_crypt_key = 335545108) + INTEGER*4 GDS__encrypt_error + PARAMETER (GDS__encrypt_error = 335545109) 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 45501e24b8..24d769c1a4 100644 --- a/lang_helpers/gds_codes.pas +++ b/lang_helpers/gds_codes.pas @@ -1621,6 +1621,10 @@ const gds_login_error = 335545106; isc_already_opened = 335545107; gds_already_opened = 335545107; + isc_bad_crypt_key = 335545108; + gds_bad_crypt_key = 335545108; + isc_encrypt_error = 335545109; + gds_encrypt_error = 335545109; isc_gfix_db_name = 335740929; gds_gfix_db_name = 335740929; isc_gfix_invalid_sw = 335740930; diff --git a/src/common/classes/ClumpletReader.cpp b/src/common/classes/ClumpletReader.cpp index 003dcae650..0f7a84ddbb 100644 --- a/src/common/classes/ClumpletReader.cpp +++ b/src/common/classes/ClumpletReader.cpp @@ -29,6 +29,7 @@ #include "firebird.h" #include "../common/classes/ClumpletReader.h" +#include "../common/classes/MetaName.h" #include "fb_exception.h" #include "../jrd/ibase.h" @@ -784,6 +785,14 @@ string& ClumpletReader::getString(string& str) const return str; } +MetaName& ClumpletReader::getString(MetaName& str) const +{ + const UCHAR* ptr = getBytes(); + const FB_SIZE_T length = getClumpLength(); + str.assign(reinterpret_cast(ptr), length); + return str; +} + PathName& ClumpletReader::getPath(PathName& str) const { const UCHAR* ptr = getBytes(); diff --git a/src/common/classes/ClumpletReader.h b/src/common/classes/ClumpletReader.h index 94aa37b0a9..d366db1535 100644 --- a/src/common/classes/ClumpletReader.h +++ b/src/common/classes/ClumpletReader.h @@ -40,6 +40,8 @@ namespace Firebird { +class MetaName; + // This class provides read access for clumplet structure // Note: it doesn't make a copy of buffer it reads class ClumpletReader : protected AutoStorage @@ -100,6 +102,7 @@ public: bool getBoolean() const; SINT64 getBigInt() const; string& getString(string& str) const; + MetaName& getString(MetaName& str) const; PathName& getPath(PathName& str) const; void getData(UCharBuffer& data) const; const UCHAR* getBytes() const; diff --git a/src/common/classes/ClumpletWriter.cpp b/src/common/classes/ClumpletWriter.cpp index edffd72d87..545e5823ee 100644 --- a/src/common/classes/ClumpletWriter.cpp +++ b/src/common/classes/ClumpletWriter.cpp @@ -30,6 +30,7 @@ #include "firebird.h" #include "../common/classes/ClumpletWriter.h" +#include "../common/classes/MetaName.h" #include "fb_exception.h" #include "../jrd/ibase.h" @@ -183,7 +184,7 @@ void ClumpletWriter::reset(const UCHAR* buffer, const FB_SIZE_T buffLen) } else { - UCHAR tag = (kind == SpbStart || kind == UnTagged || kind == WideUnTagged) ? getBufferTag() : 0; + UCHAR tag = (kind == SpbStart || kind == UnTagged || kind == WideUnTagged) ? 0 : getBufferTag(); initNewBuffer(tag); } rewind(); @@ -250,16 +251,26 @@ void ClumpletWriter::insertString(UCHAR tag, const string& str) insertString(tag, str.c_str(), str.length()); } -void ClumpletWriter::insertPath(UCHAR tag, const PathName& str) +void ClumpletWriter::insertString(UCHAR tag, const MetaName& str) { insertString(tag, str.c_str(), str.length()); } +void ClumpletWriter::insertString(UCHAR tag, const char* str) +{ + insertString(tag, str, strlen(str)); +} + void ClumpletWriter::insertString(UCHAR tag, const char* str, FB_SIZE_T length) { insertBytesLengthCheck(tag, str, length); } +void ClumpletWriter::insertPath(UCHAR tag, const PathName& str) +{ + insertString(tag, str.c_str(), str.length()); +} + void ClumpletWriter::insertBytes(UCHAR tag, const void* bytes, FB_SIZE_T length) { insertBytesLengthCheck(tag, bytes, length); diff --git a/src/common/classes/ClumpletWriter.h b/src/common/classes/ClumpletWriter.h index 2341e9899d..82029e3dc8 100644 --- a/src/common/classes/ClumpletWriter.h +++ b/src/common/classes/ClumpletWriter.h @@ -73,7 +73,9 @@ public: void insertBigInt(UCHAR tag, const SINT64 value); void insertBytes(UCHAR tag, const void* bytes, FB_SIZE_T length); void insertString(UCHAR tag, const string& str); + void insertString(UCHAR tag, const MetaName& str); void insertPath(UCHAR tag, const PathName& str); + void insertString(UCHAR tag, const char* str); void insertString(UCHAR tag, const char* str, FB_SIZE_T length); void insertByte(UCHAR tag, const UCHAR byte); void insertTag(UCHAR tag); diff --git a/src/dsql/DdlNodes.epp b/src/dsql/DdlNodes.epp index 39d6a4a136..adad42fc6c 100644 --- a/src/dsql/DdlNodes.epp +++ b/src/dsql/DdlNodes.epp @@ -11577,6 +11577,7 @@ string AlterDatabaseNode::internalPrint(NodePrinter& printer) const NODE_PRINT(printer, setDefaultCollation); NODE_PRINT(printer, files); NODE_PRINT(printer, cryptPlugin); + NODE_PRINT(printer, keyName); return "AlterDatabaseNode"; } @@ -11660,7 +11661,7 @@ void AlterDatabaseNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratc if (clauses & CLAUSE_CRYPT) { Database* const db = tdbb->getDatabase(); - db->dbb_crypto_manager->prepareChangeCryptState(tdbb, cryptPlugin); + db->dbb_crypto_manager->prepareChangeCryptState(tdbb, cryptPlugin, keyName); DFW_post_work(transaction, dfw_db_crypt, cryptPlugin.c_str(), 0); } diff --git a/src/dsql/DdlNodes.h b/src/dsql/DdlNodes.h index 79a3088bf3..ef2e8dda2a 100644 --- a/src/dsql/DdlNodes.h +++ b/src/dsql/DdlNodes.h @@ -2228,7 +2228,8 @@ public: setDefaultCharSet(p), setDefaultCollation(p), files(p), - cryptPlugin(p) + cryptPlugin(p), + keyName(p) { } @@ -2263,6 +2264,7 @@ public: Firebird::MetaName setDefaultCollation; Firebird::Array > files; Firebird::MetaName cryptPlugin; + Firebird::MetaName keyName; }; diff --git a/src/dsql/parse.y b/src/dsql/parse.y index 0f2c570408..5a186beeb7 100644 --- a/src/dsql/parse.y +++ b/src/dsql/parse.y @@ -3961,7 +3961,7 @@ db_alter_clause($alterDatabaseNode) { $alterDatabaseNode->clauses |= AlterDatabaseNode::CLAUSE_END_BACKUP; } | SET DEFAULT CHARACTER SET symbol_character_set_name { $alterDatabaseNode->setDefaultCharSet = *$5; } - | ENCRYPT WITH valid_symbol_name + | ENCRYPT WITH valid_symbol_name crypt_key_clause($alterDatabaseNode) { setClauseFlag($alterDatabaseNode->clauses, AlterDatabaseNode::CLAUSE_CRYPT, "CRYPT"); $alterDatabaseNode->cryptPlugin = *$3; @@ -3974,6 +3974,12 @@ db_alter_clause($alterDatabaseNode) { $alterDatabaseNode->linger = 0; } ; +%type crypt_key_clause() +crypt_key_clause($alterDatabaseNode) + : /* nothing */ + | KEY valid_symbol_name + { $alterDatabaseNode->keyName = *$2; } + ; // ALTER TRIGGER diff --git a/src/include/firebird/FirebirdInterface.idl b/src/include/firebird/FirebirdInterface.idl index a319cfef0e..309ea55c03 100644 --- a/src/include/firebird/FirebirdInterface.idl +++ b/src/include/firebird/FirebirdInterface.idl @@ -720,11 +720,11 @@ interface KeyHolderPlugin : PluginBase interface DbCryptPlugin : PluginBase { // When database crypt plugin is loaded, setKey() is called to provide information - // about key holders, available for a given database. + // about key holders, available for a given database and key name for database. // It's supposed that crypt plugin will invoke keyHandle() function from them // to access callback interface for getting actual crypt key. // If crypt plugin fails to find appropriate key in sources, it should raise error. - void setKey(Status status, uint length, KeyHolderPlugin* sources); + void setKey(Status status, const string keyName, uint length, KeyHolderPlugin* sources); void encrypt(Status status, uint length, const void* from, void* to); void decrypt(Status status, uint length, const void* from, void* to); } diff --git a/src/include/firebird/IdlFbInterfaces.h b/src/include/firebird/IdlFbInterfaces.h index 77e7137c68..853daa626f 100644 --- a/src/include/firebird/IdlFbInterfaces.h +++ b/src/include/firebird/IdlFbInterfaces.h @@ -2871,7 +2871,7 @@ namespace Firebird public: struct VTable : public IPluginBase::VTable { - void (CLOOP_CARG *setKey)(IDbCryptPlugin* self, IStatus* status, unsigned length, IKeyHolderPlugin** sources) throw(); + void (CLOOP_CARG *setKey)(IDbCryptPlugin* self, IStatus* status, const char* keyName, unsigned length, IKeyHolderPlugin** sources) throw(); void (CLOOP_CARG *encrypt)(IDbCryptPlugin* self, IStatus* status, unsigned length, const void* from, void* to) throw(); void (CLOOP_CARG *decrypt)(IDbCryptPlugin* self, IStatus* status, unsigned length, const void* from, void* to) throw(); }; @@ -2889,10 +2889,10 @@ namespace Firebird public: static const unsigned VERSION = 4; - template void setKey(StatusType* status, unsigned length, IKeyHolderPlugin** sources) + template void setKey(StatusType* status, const char* keyName, unsigned length, IKeyHolderPlugin** sources) { StatusType::clearException(status); - static_cast(this->cloopVTable)->setKey(this, status, length, sources); + static_cast(this->cloopVTable)->setKey(this, status, keyName, length, sources); StatusType::checkException(status); } @@ -11093,13 +11093,13 @@ namespace Firebird this->cloopVTable = &vTable; } - static void CLOOP_CARG cloopsetKeyDispatcher(IDbCryptPlugin* self, IStatus* status, unsigned length, IKeyHolderPlugin** sources) throw() + static void CLOOP_CARG cloopsetKeyDispatcher(IDbCryptPlugin* self, IStatus* status, const char* keyName, unsigned length, IKeyHolderPlugin** sources) throw() { StatusType status2(status); try { - static_cast(self)->Name::setKey(&status2, length, sources); + static_cast(self)->Name::setKey(&status2, keyName, length, sources); } catch (...) { @@ -11199,7 +11199,7 @@ namespace Firebird { } - virtual void setKey(StatusType* status, unsigned length, IKeyHolderPlugin** sources) = 0; + virtual void setKey(StatusType* status, const char* keyName, unsigned length, IKeyHolderPlugin** sources) = 0; virtual void encrypt(StatusType* status, unsigned length, const void* from, void* to) = 0; virtual void decrypt(StatusType* status, unsigned length, const void* from, void* to) = 0; }; diff --git a/src/include/gen/codetext.h b/src/include/gen/codetext.h index 715deda6c1..a62c0a4a15 100644 --- a/src/include/gen/codetext.h +++ b/src/include/gen/codetext.h @@ -809,6 +809,8 @@ static const struct { {"map_down", 335545105}, {"login_error", 335545106}, {"already_opened", 335545107}, + {"bad_crypt_key", 335545108}, + {"encrypt_error", 335545109}, {"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 ea4aba3e63..e2c9bf8b75 100644 --- a/src/include/gen/iberror.h +++ b/src/include/gen/iberror.h @@ -843,6 +843,8 @@ const ISC_STATUS isc_invalid_attachment_charset = 335545104L; const ISC_STATUS isc_map_down = 335545105L; const ISC_STATUS isc_login_error = 335545106L; const ISC_STATUS isc_already_opened = 335545107L; +const ISC_STATUS isc_bad_crypt_key = 335545108L; +const ISC_STATUS isc_encrypt_error = 335545109L; const ISC_STATUS isc_gfix_db_name = 335740929L; const ISC_STATUS isc_gfix_invalid_sw = 335740930L; const ISC_STATUS isc_gfix_incmp_sw = 335740932L; @@ -1306,7 +1308,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 = 1250; +const ISC_STATUS isc_err_max = 1252; #else /* c definitions */ @@ -2119,6 +2121,8 @@ const ISC_STATUS isc_err_max = 1250; #define isc_map_down 335545105L #define isc_login_error 335545106L #define isc_already_opened 335545107L +#define isc_bad_crypt_key 335545108L +#define isc_encrypt_error 335545109L #define isc_gfix_db_name 335740929L #define isc_gfix_invalid_sw 335740930L #define isc_gfix_incmp_sw 335740932L @@ -2582,7 +2586,7 @@ const ISC_STATUS isc_err_max = 1250; #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 1250 +#define isc_err_max 1252 #endif diff --git a/src/include/gen/msgs.h b/src/include/gen/msgs.h index 37cb197dd5..2e482baac3 100644 --- a/src/include/gen/msgs.h +++ b/src/include/gen/msgs.h @@ -812,6 +812,8 @@ Data source : @4"}, /* eds_statement */ {335545105, "Some database(s) were shutdown when trying to read mapping data"}, /* map_down */ {335545106, "Error occurred during login, please check server firebird.log for details"}, /* login_error */ {335545107, "Database already opened with engine instance, incompatible with current"}, /* already_opened */ + {335545108, "Invalid crypt key @1"}, /* bad_crypt_key */ + {335545109, "Page requires encyption but crypt plugin is missing"}, /* encrypt_error */ {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 23ea0c7a79..1dde5b3a40 100644 --- a/src/include/gen/sql_code.h +++ b/src/include/gen/sql_code.h @@ -808,6 +808,8 @@ static const struct { {335545105, -901}, /* 785 map_down */ {335545106, -902}, /* 786 login_error */ {335545107, -902}, /* 787 already_opened */ + {335545108, -902}, /* 788 bad_crypt_key */ + {335545109, -901}, /* 789 encrypt_error */ {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 a2d5cf89ae..d6b48d9eeb 100644 --- a/src/include/gen/sql_state.h +++ b/src/include/gen/sql_state.h @@ -743,7 +743,7 @@ static const struct { {335545040, "22001"}, // 720 cp_name_too_long {335545041, "42818"}, // 721 cp_process_active {335545042, "42818"}, // 722 cp_already_crypted - {335545043, "39000"}, // 723 decrypt_error + {335545043, "XX000"}, // 723 decrypt_error {335545044, "39000"}, // 724 no_providers {335545045, "42818"}, // 725 null_spb {335545046, "42000"}, // 726 max_args_exceeded @@ -808,6 +808,8 @@ static const struct { {335545105, "08004"}, // 785 map_down {335545106, "08006"}, // 786 login_error {335545107, "08006"}, // 787 already_opened + {335545108, "08006"}, // 788 bad_crypt_key + {335545109, "XX000"}, // 789 encrypt_error {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 3acda63a6f..b7a1538a82 100644 --- a/src/jrd/CryptoManager.cpp +++ b/src/jrd/CryptoManager.cpp @@ -48,6 +48,8 @@ #include "../common/isc_proto.h" #include "../common/classes/GetPlugins.h" #include "../common/classes/RefMutex.h" +#include "../common/classes/ClumpletWriter.h" +#include "../common/sha.h" using namespace Firebird; @@ -83,23 +85,16 @@ namespace { return header; } - // This routine is looking for a clumplet on header page but is not ready to handle continuation - // Fortunately, modern pages of size 4k and bigger can contain everything on one page. - bool searchEntry(UCHAR type, UCHAR& out_len, const UCHAR* &entry) + // 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) { - const UCHAR* end = ((const UCHAR*) header) + header->hdr_page_size; - for (const UCHAR* p = header->hdr_data; (p < end - 2) && (*p != Ods::HDR_end); p += 2u + p[1]) - { - if (*p == type) - { - out_len = p[1]; - entry = p + 2; - if (entry + out_len > end) - out_len = end - entry; - return true; - } - } - return false; + const UCHAR* p = header->hdr_data; + const UCHAR* const end = reinterpret_cast(header) + header->hdr_page_size; + while ((p < end - 2) && (*p != Ods::HDR_end)) + p += 2u + p[1]; + + writer.reset(header->hdr_data, p - header->hdr_data); } private: @@ -112,7 +107,8 @@ namespace { public: CchHdr(Jrd::thread_db* p_tdbb, USHORT lockType) : window(Jrd::HEADER_PAGE_NUMBER), - tdbb(p_tdbb) + tdbb(p_tdbb), + wrtFlag(false) { void* h = CCH_FETCH(tdbb, &window, lockType, pag_header); if (!h) @@ -124,10 +120,30 @@ namespace { Ods::header_page* write() { - CCH_MARK_MUST_WRITE(tdbb, &window); + if (!wrtFlag) + { + CCH_MARK_MUST_WRITE(tdbb, &window); + wrtFlag = true; + } return const_cast(operator->()); } + void setClumplets(const ClumpletWriter& writer) + { + Ods::header_page* hdr = write(); + UCHAR* const to = hdr->hdr_data; + UCHAR* const end = reinterpret_cast(hdr) + hdr->hdr_page_size; + const unsigned limit = (end - to) - 1; + + const unsigned length = writer.getBufferLength(); + fb_assert(length <= limit); + if (length > limit) + (Arg::Gds(isc_random) << "HDR page clumplets overflow").raise(); + + memcpy(to, writer.getBuffer(), length); + to[length] = Ods::HDR_end; + } + ~CchHdr() { CCH_RELEASE(tdbb, &window); @@ -136,6 +152,7 @@ namespace { private: Jrd::WIN window; Jrd::thread_db* tdbb; + bool wrtFlag; }; class PhysHdr : public Header @@ -222,6 +239,7 @@ namespace Jrd { CryptoManager::CryptoManager(thread_db* tdbb) : PermanentStorage(*tdbb->getDatabase()->dbb_permanent), sync(this), + keyName(getPool()), keyHolderPlugins(getPool()), cryptThreadId(0), cryptPlugin(NULL), @@ -310,9 +328,27 @@ namespace Jrd { PhysHdr hdr(tdbb); crypt = hdr->hdr_flags & Ods::hdr_encrypted; process = hdr->hdr_flags & Ods::hdr_crypt_process; - if (crypt || process) + + if ((crypt || process) && !cryptPlugin) { + ClumpletWriter hc(ClumpletWriter::UnTagged, hdr->hdr_page_size); + hdr.getClumplets(hc); + if (hc.find(Ods::HDR_crypt_key)) + hc.getString(keyName); + else + keyName = ""; + loadPlugin(hdr->hdr_crypt_plugin); + + string valid; + calcValidation(valid); + if (hc.find(Ods::HDR_crypt_hash)) + { + string hash; + hc.getString(hash); + if (hash != valid) + (Arg::Gds(isc_bad_crypt_key) << keyName).raise(); + } } } @@ -337,12 +373,13 @@ namespace Jrd { // do not assign cryptPlugin directly before key init complete IDbCryptPlugin* p = cryptControl.plugin(); - keyHolderPlugins.init(p); + keyHolderPlugins.init(p, keyName.c_str()); cryptPlugin = p; cryptPlugin->addRef(); } - void CryptoManager::prepareChangeCryptState(thread_db* tdbb, const Firebird::MetaName& plugName) + void CryptoManager::prepareChangeCryptState(thread_db* tdbb, const MetaName& plugName, + const MetaName& key) { if (plugName.length() > MAX_PLUGIN_NAME_LEN) { @@ -371,12 +408,28 @@ namespace Jrd { if (cryptPlugin) (Arg::Gds(isc_cp_already_crypted)).raise(); + keyName = key; loadPlugin(plugName.c_str()); } } } - void CryptoManager::changeCryptState(thread_db* tdbb, const Firebird::string& plugName) + void CryptoManager::calcValidation(string& valid) + { + // crypt verifier + const char* sample = "0123456789ABCDEF"; + char result[16]; + FbLocalStatus sv; + cryptPlugin->encrypt(&sv, sizeof(result), sample, result); + if (sv->getState() & IStatus::STATE_ERRORS) + Arg::StatusVector(&sv).raise(); + + // calculate it's hash + const string verifier(result, sizeof(result)); + Sha1::hashBased64(valid, verifier); + } + + void CryptoManager::changeCryptState(thread_db* tdbb, const string& plugName) { if (plugName.length() > 31) { @@ -423,17 +476,31 @@ namespace Jrd { // Write modified header page Ods::header_page* header = hdr.write(); + ClumpletWriter hc(ClumpletWriter::UnTagged, header->hdr_page_size); + hdr.getClumplets(hc); + if (crypt) { header->hdr_flags |= Ods::hdr_encrypted; plugName.copyTo(header->hdr_crypt_plugin, sizeof(header->hdr_crypt_plugin)); + string hash; + calcValidation(hash); + hc.insertString(Ods::HDR_crypt_hash, hash); } else { header->hdr_flags &= ~Ods::hdr_encrypted; + hc.deleteWithTag(Ods::HDR_crypt_hash); } - // Set hdr_crypt_page for crypt thread + if (crypt && keyName.hasData()) + hc.insertString(Ods::HDR_crypt_key, keyName); + else + hc.deleteWithTag(Ods::HDR_crypt_key); + + hdr.setClumplets(hc); + + // Setup hdr_crypt_page for crypt thread header->hdr_crypt_page = 1; header->hdr_flags |= Ods::hdr_crypt_process; process = true; @@ -848,8 +915,7 @@ namespace Jrd { fb_assert(cryptPlugin); if (!cryptPlugin) { - (Arg::Gds(isc_decrypt_error) << - Arg::Gds(isc_random) << "Missing crypt plugin").copyTo(sv); + Arg::Gds(isc_encrypt_error).copyTo(sv); return FAILED_CRYPT; } @@ -950,7 +1016,7 @@ namespace Jrd { { IKeyHolderPlugin* keyPlugin = keyControl.plugin(); FbLocalStatus st; - if (keyPlugin->keyCallback(&st, att->att_crypt_callback) == 1) //// FIXME: 1 ??? + if (keyPlugin->keyCallback(&st, att->att_crypt_callback) > 0) { // holder accepted attachment's key HolderAttachments* ha = NULL; @@ -992,7 +1058,7 @@ namespace Jrd { } } - void CryptoManager::KeyHolderPlugins::init(IDbCryptPlugin* crypt) + void CryptoManager::KeyHolderPlugins::init(IDbCryptPlugin* crypt, const char* keyName) { MutexLockGuard g(holdersMutex, FB_FUNCTION); @@ -1005,7 +1071,7 @@ namespace Jrd { } FbLocalStatus st; - crypt->setKey(&st, length, vector); + crypt->setKey(&st, keyName, length, vector); st.check(); } diff --git a/src/jrd/CryptoManager.h b/src/jrd/CryptoManager.h index 5655f0d9e9..a6e25a4798 100644 --- a/src/jrd/CryptoManager.h +++ b/src/jrd/CryptoManager.h @@ -35,6 +35,7 @@ #include "../common/classes/fb_string.h" #include "../common/classes/objects_array.h" #include "../common/classes/condition.h" +#include "../common/classes/MetaName.h" #include "../common/ThreadStart.h" #include "../jrd/ods.h" #include "../jrd/status.h" @@ -259,7 +260,8 @@ public: void shutdown(thread_db* tdbb); - void prepareChangeCryptState(thread_db* tdbb, const Firebird::MetaName& plugName); + void prepareChangeCryptState(thread_db* tdbb, const Firebird::MetaName& plugName, + const Firebird::MetaName& key); void changeCryptState(thread_db* tdbb, const Firebird::string& plugName); void attach(thread_db* tdbb, Attachment* att); void detach(thread_db* tdbb, Attachment* att); @@ -334,7 +336,7 @@ private: void attach(Attachment* att, Config* config); void detach(Attachment* att); - void init(Firebird::IDbCryptPlugin* crypt); + void init(Firebird::IDbCryptPlugin* crypt, const char* keyName); private: Firebird::Mutex holdersMutex; @@ -351,12 +353,14 @@ private: void loadPlugin(const char* pluginName); ULONG getLastPage(thread_db* tdbb); void writeDbHeader(thread_db* tdbb, ULONG runpage); + void calcValidation(Firebird::string& valid); void lockAndReadHeader(thread_db* tdbb, unsigned flags = 0); static const unsigned CRYPT_HDR_INIT = 0x01; static const unsigned CRYPT_HDR_NOWAIT = 0x02; BarSync sync; + Firebird::MetaName keyName; ULONG currentPage; Firebird::Mutex pluginLoadMtx, cryptThreadMtx; KeyHolderPlugins keyHolderPlugins; diff --git a/src/jrd/ods.h b/src/jrd/ods.h index 87cc0e183a..ce90ebafa9 100644 --- a/src/jrd/ods.h +++ b/src/jrd/ods.h @@ -429,7 +429,9 @@ 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_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_max = 8; // Maximum HDR_clump value +const UCHAR HDR_crypt_key = 8; // Name of a key used to crypt database +const UCHAR HDR_crypt_hash = 9; // Validator of key correctness +const UCHAR HDR_max = 10; // Maximum HDR_clump value // Header page flags diff --git a/src/msgs/facilities2.sql b/src/msgs/facilities2.sql index 6934d83f94..2b9b64d65d 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 (?, ?, ?, ?); -- -('2015-12-30 17:52:25', 'JRD', 0, 788) +('2016-02-03 14:51:49', 'JRD', 0, 790) ('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 36b2af5a4c..ad4dd713a5 100644 --- a/src/msgs/messages2.sql +++ b/src/msgs/messages2.sql @@ -895,6 +895,8 @@ Data source : @4', NULL, NULL) ('map_down', NULL, 'Mapping.cpp', NULL, 0, 785, NULL, 'Some database(s) were shutdown when trying to read mapping data', NULL, NULL); ('login_error', NULL, 'server.cpp', NULL, 0, 786, NULL, 'Error occurred during login, please check server firebird.log for details', NULL, NULL); ('already_opened', 'lockDatabaseFile', 'unix.cpp', NULL, 0, 787, NULL, 'Database already opened with engine instance, incompatible with current', NULL, NULL); +('bad_crypt_key', NULL, 'CryptoManager.cpp', NULL, 0, 788, NULL, 'Invalid crypt key @1', NULL, NULL); +('encrypt_error', NULL, 'CryptoManager.cpp', NULL, 0, 789, NULL, 'Page requires encyption but crypt plugin is missing', 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 53f2cbef00..8d05fe93d8 100644 --- a/src/msgs/system_errors2.sql +++ b/src/msgs/system_errors2.sql @@ -729,7 +729,7 @@ set bulk_insert INSERT INTO SYSTEM_ERRORS (SQL_CODE, SQL_CLASS, SQL_SUBCLASS, FA (-104, '22', '001', 0, 720, 'cp_name_too_long', NULL, NULL) (-901, '42', '818', 0, 721, 'cp_process_active', NULL, NULL) (-901, '42', '818', 0, 722, 'cp_already_crypted', NULL, NULL) -(-902, '39', '000', 0, 723, 'decrypt_error', NULL, NULL) +(-902, 'XX', '000', 0, 723, 'decrypt_error', NULL, NULL) (-902, '39', '000', 0, 724, 'no_providers', NULL, NULL) (-104, '42', '818', 0, 725, 'null_spb', NULL, NULL) (-833, '42', '000', 0, 726, 'max_args_exceeded', NULL, NULL) @@ -794,6 +794,8 @@ set bulk_insert INSERT INTO SYSTEM_ERRORS (SQL_CODE, SQL_CLASS, SQL_SUBCLASS, FA (-901, '08', '004', 0, 785, 'map_down', NULL, NULL) (-902, '08', '006', 0, 786, 'login_error', NULL, NULL) (-902, '08', '006', 0, 787, 'already_opened', NULL, NULL) +(-902, '08', '006', 0, 788, 'bad_crypt_key', NULL, NULL) +(-901, 'XX', '000', 0, 789, 'encrypt_error', NULL, NULL) -- GFIX (-901, '00', '000', 3, 1, 'gfix_db_name', NULL, NULL) (-901, '00', '000', 3, 2, 'gfix_invalid_sw', NULL, NULL) diff --git a/src/utilities/gstat/ppg.cpp b/src/utilities/gstat/ppg.cpp index 53fd212db2..30ffa21997 100644 --- a/src/utilities/gstat/ppg.cpp +++ b/src/utilities/gstat/ppg.cpp @@ -281,6 +281,14 @@ void PPG_print_header(const header_page* header, ULONG page, break; } + case HDR_crypt_key: + uSvc->printf(false, "\tEncryption key name:\t%*.*s\n", p[1], p[1], p + 2); + break; + + case HDR_crypt_hash: + uSvc->printf(false, "\tKey hash:\t%*.*s\n", p[1], p[1], p + 2); + break; + default: if (*p > HDR_max) uSvc->printf(false, "\tUnrecognized option %d, length %d\n", p[0], p[1]);