From aa6cb5d05f3603514c380e99a5d491cac58c8968 Mon Sep 17 00:00:00 2001 From: Alexander Peshkov Date: Wed, 23 May 2018 17:16:04 +0300 Subject: [PATCH] Initial implementation for CORE-5808 (#160) * Implemented CORE-5808 --- lang_helpers/gds_codes.ftn | 2 + lang_helpers/gds_codes.pas | 2 + src/burp/backup.epp | 19 +- src/burp/burp.cpp | 142 ++++- src/burp/burp.h | 66 +- src/burp/burpswi.h | 26 +- src/burp/canonical.cpp | 7 +- src/burp/mvol.cpp | 844 ++++++++++++++++++++++++-- src/burp/mvol_proto.h | 17 +- src/burp/restore.epp | 178 +++++- src/common/UtilSvc.cpp | 1 + src/common/UtilSvc.h | 3 + src/common/classes/ClumpletReader.cpp | 4 +- src/common/classes/GetPlugins.h | 1 + src/common/classes/RefCounted.h | 7 + src/include/consts_pub.h | 7 + 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 | 14 +- src/jrd/CryptoManager.h | 3 +- src/jrd/SystemPrivileges.h | 2 +- src/jrd/inf.cpp | 21 +- src/jrd/inf_pub.h | 4 +- src/jrd/svc.cpp | 8 + src/jrd/svc.h | 3 +- src/msgs/facilities2.sql | 4 +- src/msgs/messages2.sql | 16 + src/msgs/system_errors2.sql | 1 + src/utilities/fbsvcmgr/fbsvcmgr.cpp | 40 ++ 32 files changed, 1297 insertions(+), 155 deletions(-) diff --git a/lang_helpers/gds_codes.ftn b/lang_helpers/gds_codes.ftn index d7a75fbdee..50c565338c 100644 --- a/lang_helpers/gds_codes.ftn +++ b/lang_helpers/gds_codes.ftn @@ -1820,6 +1820,8 @@ C -- PARAMETER (GDS__vld_plugins = 335545203) INTEGER*4 GDS__db_crypt_key PARAMETER (GDS__db_crypt_key = 335545204) + INTEGER*4 GDS__no_keyholder_plugin + PARAMETER (GDS__no_keyholder_plugin = 335545205) 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 0f02600680..f77ae737f7 100644 --- a/lang_helpers/gds_codes.pas +++ b/lang_helpers/gds_codes.pas @@ -1815,6 +1815,8 @@ const gds_vld_plugins = 335545203; isc_db_crypt_key = 335545204; gds_db_crypt_key = 335545204; + isc_no_keyholder_plugin = 335545205; + gds_no_keyholder_plugin = 335545205; isc_gfix_db_name = 335740929; gds_gfix_db_name = 335740929; isc_gfix_invalid_sw = 335740930; diff --git a/src/burp/backup.epp b/src/burp/backup.epp index 3d9d50ee8c..83315a31be 100644 --- a/src/burp/backup.epp +++ b/src/burp/backup.epp @@ -88,18 +88,12 @@ namespace // unnamed, private inline void put(BurpGlobals* tdgbl, const UCHAR c) { - if (--(tdgbl->io_cnt) >= 0) - *(tdgbl->io_ptr)++ = c; - else - MVOL_write(c, &tdgbl->io_cnt, &tdgbl->io_ptr); + tdgbl->put(c); } inline void put(BurpGlobals* tdgbl, const att_type c) { - if (--tdgbl->io_cnt >= 0) - *(tdgbl->io_ptr)++ = UCHAR(c); - else - MVOL_write(UCHAR(c), &tdgbl->io_cnt, &tdgbl->io_ptr); + put(tdgbl, UCHAR(c)); } inline const UCHAR* put_block(BurpGlobals* tdgbl, const UCHAR* p, ULONG n) @@ -221,11 +215,6 @@ int BACKUP_backup(const TEXT* dbb_file, const TEXT* file_name) tdgbl->gbl_database_file_name = dbb_file; - tdgbl->io_ptr = NULL; - tdgbl->io_cnt = 0; - tdgbl->relations = NULL; - tdgbl->runtimeODS = 0; - gds_trans = 0; BURP_verbose(130); @@ -280,7 +269,7 @@ int BACKUP_backup(const TEXT* dbb_file, const TEXT* file_name) tdgbl->action->act_file = tdgbl->gbl_sw_files; } - MVOL_init_write(file_name, &tdgbl->io_cnt, &tdgbl->io_ptr); + MVOL_init_write(file_name); // Write database record @@ -416,7 +405,7 @@ int BACKUP_backup(const TEXT* dbb_file, const TEXT* file_name) // Finish up put(tdgbl, (UCHAR) rec_end); - FB_UINT64 cumul_count = MVOL_fini_write(&tdgbl->io_cnt, &tdgbl->io_ptr); + FB_UINT64 cumul_count = MVOL_fini_write(); tdgbl->action->act_action = ACT_backup_fini; BURP_verbose(176, SafeArg() << cumul_count); // msg 176 closing file, committing, and finishing. %ld bytes written diff --git a/src/burp/burp.cpp b/src/burp/burp.cpp index baec34964b..52d906dbb2 100644 --- a/src/burp/burp.cpp +++ b/src/burp/burp.cpp @@ -726,6 +726,41 @@ int gbak(Firebird::UtilSvc* uSvc) } tdgbl->gbl_sw_sql_role = argv[itr]; break; + case IN_SW_BURP_KEYHOLD: + if (++itr >= argc) + { + BURP_error(381, true); + // KeyHolder parameter missing + } + if (tdgbl->gbl_sw_keyholder) + BURP_error(334, true, SafeArg() << in_sw_tab->in_sw_name); + tdgbl->gbl_sw_keyholder = argv[itr]; + break; + case IN_SW_BURP_CRYPT: + if (++itr >= argc) + { + BURP_error(377, true); + // CryptPlugin parameter missing + } + if (tdgbl->gbl_sw_crypt) + BURP_error(334, true, SafeArg() << in_sw_tab->in_sw_name); + tdgbl->gbl_sw_crypt = argv[itr]; + break; + case IN_SW_BURP_KEYNAME: + if (++itr >= argc) + { + BURP_error(375, true); + // Key name parameter missing + } + if (tdgbl->gbl_sw_keyname) + BURP_error(334, true, SafeArg() << in_sw_tab->in_sw_name); + tdgbl->gbl_sw_keyname = argv[itr]; + break; + case IN_SW_BURP_ZIP: + if (tdgbl->gbl_sw_zip) + BURP_error(334, true, SafeArg() << in_sw_tab->in_sw_name); + tdgbl->gbl_sw_zip = true; + break; case IN_SW_BURP_FA: if (tdgbl->gbl_sw_blk_factor) BURP_error(333, true, SafeArg() << in_sw_tab->in_sw_name << tdgbl->gbl_sw_blk_factor); @@ -1202,6 +1237,8 @@ int gbak(Firebird::UtilSvc* uSvc) } else if (tdgbl->gbl_sw_old_descriptions) errNum = IN_SW_BURP_OL; + else if (tdgbl->gbl_sw_zip) + errNum = IN_SW_BURP_ZIP; if (errNum != IN_SW_BURP_0) { @@ -1815,14 +1852,28 @@ static gbak_action open_files(const TEXT* file1, * **************************************/ BurpGlobals* tdgbl = BurpGlobals::getSpecific(); - FbLocalStatus temp_status; - Firebird::CheckStatusWrapper* status_vector = &temp_status; + FbLocalStatus status_vector; // try to attach the database using the first file_name if (sw_replace != IN_SW_BURP_C && sw_replace != IN_SW_BURP_R) { - tdgbl->db_handle = Firebird::DispatcherPtr()->attachDatabase(status_vector, file1, + Firebird::DispatcherPtr provider; + + // provide crypt key(s) for engine + + if (tdgbl->gbl_sw_keyholder) + { + tdgbl->gbl_database_file_name = file1; + provider->setDbCryptCallback(&status_vector, MVOL_get_crypt(tdgbl)); + if (!status_vector.isSuccess()) + { + BURP_print_status(true, &status_vector); + return QUIT; + } + } + + tdgbl->db_handle = provider->attachDatabase(&status_vector, file1, dpb.getBufferLength(), dpb.getBuffer()); if (!(status_vector->getState() & Firebird::IStatus::STATE_ERRORS)) @@ -1831,10 +1882,10 @@ static gbak_action open_files(const TEXT* file1, { // msg 13 REPLACE specified, but the first file %s is a database BURP_error(13, true, file1); - tdgbl->db_handle->detach(status_vector); + tdgbl->db_handle->detach(&status_vector); if (status_vector->getState() & Firebird::IStatus::STATE_ERRORS) - BURP_print_status(true, status_vector); + BURP_print_status(true, &status_vector); else tdgbl->db_handle = NULL; @@ -1845,14 +1896,58 @@ static gbak_action open_files(const TEXT* file1, // msg 139 Version(s) for database "%s" BURP_print(false, 139, file1); OutputVersion outputVersion("\t%s\n"); - Firebird::UtilInterfacePtr()->getFbVersion(status_vector, tdgbl->db_handle, &outputVersion); + Firebird::UtilInterfacePtr()->getFbVersion(&status_vector, tdgbl->db_handle, &outputVersion); } BURP_verbose(166, file1); // msg 166: readied database %s for backup + + if (tdgbl->gbl_sw_keyholder) + { + unsigned char info[] = {fb_info_crypt_key, fb_info_crypt_plugin}; + unsigned char buffer[(1 + 2 + MAX_SQL_IDENTIFIER_SIZE) * 2 + 2]; + unsigned int len; + + tdgbl->db_handle->getInfo(&status_vector, sizeof(info), info, sizeof(buffer), buffer); + + UCHAR* p = buffer; + while(p) + { + switch(*p++) + { + case fb_info_crypt_key: + len = gds__vax_integer(p, 2); + if (len < sizeof(tdgbl->gbl_hdr_keybuffer)) + { + memcpy(tdgbl->gbl_hdr_keybuffer, p + 2, len); + tdgbl->gbl_hdr_keybuffer[len] = 0; + if (!tdgbl->gbl_sw_keyname) + tdgbl->gbl_sw_keyname = tdgbl->gbl_hdr_keybuffer; + } + break; + + case fb_info_crypt_plugin: + len = gds__vax_integer(p, 2); + if (len < sizeof(tdgbl->gbl_hdr_cryptbuffer)) + { + memcpy(tdgbl->gbl_hdr_cryptbuffer, p + 2, len); + tdgbl->gbl_hdr_cryptbuffer[len] = 0; + if (!tdgbl->gbl_sw_crypt) + tdgbl->gbl_sw_crypt = tdgbl->gbl_hdr_cryptbuffer; + } + break; + + default: + p = NULL; + continue; + } + + p += (2 + len); + } + } } else if (sw_replace == IN_SW_BURP_B || (status_vector->getErrors()[1] != isc_io_error && status_vector->getErrors()[1] != isc_bad_db_format)) { - BURP_print_status(true, status_vector); + BURP_print_status(true, &status_vector); return QUIT; } } @@ -1928,7 +2023,7 @@ static gbak_action open_files(const TEXT* file1, { Firebird::string nm = tdgbl->toSystem(fil->fil_name); #ifdef WIN_NT - if ((fil->fil_fd = MVOL_open(nm.c_str(), MODE_WRITE, CREATE_ALWAYS)) == + if ((fil->fil_fd = NT_tape_open(nm.c_str(), MODE_WRITE, CREATE_ALWAYS)) == INVALID_HANDLE_VALUE) #else if ((fil->fil_fd = os_utils::open(nm.c_str(), MODE_WRITE, open_mask)) == -1) @@ -1974,10 +2069,10 @@ static gbak_action open_files(const TEXT* file1, } else { - tdgbl->db_handle->detach(status_vector); + tdgbl->db_handle->detach(&status_vector); if (status_vector->getState() & Firebird::IStatus::STATE_ERRORS) - BURP_print_status(true, status_vector); + BURP_print_status(true, &status_vector); else tdgbl->db_handle = NULL; } @@ -2038,7 +2133,7 @@ static gbak_action open_files(const TEXT* file1, // open first file Firebird::string nm = tdgbl->toSystem(fil->fil_name); #ifdef WIN_NT - if ((fil->fil_fd = MVOL_open(nm.c_str(), MODE_READ, OPEN_EXISTING)) == + if ((fil->fil_fd = NT_tape_open(nm.c_str(), MODE_READ, OPEN_EXISTING)) == INVALID_HANDLE_VALUE) #else if ((fil->fil_fd = os_utils::open(nm.c_str(), MODE_READ)) == INVALID_HANDLE_VALUE) @@ -2084,7 +2179,7 @@ static gbak_action open_files(const TEXT* file1, tdgbl->action->act_file = fil; Firebird::string nm = tdgbl->toSystem(fil->fil_name); #ifdef WIN_NT - if ((fil->fil_fd = MVOL_open(nm.c_str(), MODE_READ, OPEN_EXISTING)) == + if ((fil->fil_fd = NT_tape_open(nm.c_str(), MODE_READ, OPEN_EXISTING)) == INVALID_HANDLE_VALUE) #else if ((fil->fil_fd = os_utils::open(nm.c_str(), MODE_READ)) == INVALID_HANDLE_VALUE) @@ -2152,17 +2247,32 @@ static gbak_action open_files(const TEXT* file1, if (sw_replace == IN_SW_BURP_C || sw_replace == IN_SW_BURP_R) { - tdgbl->db_handle = Firebird::DispatcherPtr()->attachDatabase(status_vector, *file2, + Firebird::DispatcherPtr provider; + + // provide crypt key(s) for engine + + if (tdgbl->gbl_sw_keyholder) + { + tdgbl->gbl_database_file_name = *file2; + provider->setDbCryptCallback(&status_vector, MVOL_get_crypt(tdgbl)); + if (!status_vector.isSuccess()) + { + BURP_print_status(true, &status_vector); + return QUIT; + } + } + + tdgbl->db_handle = provider->attachDatabase(&status_vector, *file2, dpb.getBufferLength(), dpb.getBuffer()); if (!(status_vector->getState() & Firebird::IStatus::STATE_ERRORS)) { if (sw_replace == IN_SW_BURP_C) { - tdgbl->db_handle->detach(status_vector); + tdgbl->db_handle->detach(&status_vector); if (status_vector->getState() & Firebird::IStatus::STATE_ERRORS) - BURP_print_status(true, status_vector); + BURP_print_status(true, &status_vector); else tdgbl->db_handle = NULL; @@ -2171,7 +2281,7 @@ static gbak_action open_files(const TEXT* file1, } else { - tdgbl->db_handle->dropDatabase(status_vector); + tdgbl->db_handle->dropDatabase(&status_vector); if (status_vector->getState() & Firebird::IStatus::STATE_ERRORS) { diff --git a/src/burp/burp.h b/src/burp/burp.h index c5fbd85ed6..0b0c894f33 100644 --- a/src/burp/burp.h +++ b/src/burp/burp.h @@ -35,6 +35,7 @@ #include "firebird/Message.h" #include "../common/dsc.h" #include "../burp/misc_proto.h" +#include "../burp/mvol_proto.h" #include "../yvalve/gds_proto.h" #include "../common/ThreadData.h" #include "../common/UtilSvc.h" @@ -43,6 +44,7 @@ #include "../common/classes/MetaName.h" #include "../../jrd/SimilarToMatcher.h" #include "../common/status.h" +#include "../common/sha.h" #include "../common/classes/ImplementHelper.h" #ifdef HAVE_UNISTD_H @@ -53,6 +55,14 @@ #include #endif +#if defined(HAVE_ZLIB_H) +#define WIRE_COMPRESS_SUPPORT 1 +#endif + +#ifdef WIRE_COMPRESS_SUPPORT +#include +//#define COMPRESS_DEBUG 1 +#endif // WIRE_COMPRESS_SUPPORT static inline UCHAR* BURP_alloc(ULONG size) { @@ -208,10 +218,6 @@ Version 11: FB4.0. const int ATT_BACKUP_FORMAT = 11; -// format version number for ranges for arrays - -//const int GDS_NDA_VERSION = 1; // Not used - // max array dimension const int MAX_DIMENSION = 16; @@ -234,6 +240,10 @@ enum att_type { att_backup_blksize, // backup block size att_backup_file, // database file name att_backup_volume, // backup volume number + att_backup_keyname, // name of crypt key + att_backup_zip, // zipped backup file + att_backup_hash, // hash of crypt key + att_backup_crypt, // name of crypt plugin // Database attributes @@ -921,6 +931,8 @@ public: // Global switches and data +struct BurpCrypt; + class BurpGlobals : public Firebird::ThreadData { public: @@ -973,6 +985,12 @@ public: bool gbl_sw_mode; bool gbl_sw_mode_val; bool gbl_sw_overwrite; + bool gbl_sw_zip; + const SCHAR* gbl_sw_keyholder; + const SCHAR* gbl_sw_crypt; + const SCHAR* gbl_sw_keyname; + SCHAR gbl_hdr_keybuffer[MAX_SQL_IDENTIFIER_SIZE + 1]; + SCHAR gbl_hdr_cryptbuffer[MAX_SQL_IDENTIFIER_SIZE + 1]; const SCHAR* gbl_sw_sql_role; const SCHAR* gbl_sw_user; const SCHAR* gbl_sw_password; @@ -983,11 +1001,42 @@ public: gfld* gbl_global_fields; unsigned gbl_network_protocol; burp_act* action; + BurpCrypt* gbl_crypt; ULONG io_buffer_size; redirect_vals sw_redirect; bool burp_throw; - UCHAR* io_ptr; - int io_cnt; + + UCHAR* blk_io_ptr; + int blk_io_cnt; + + void put(const UCHAR c) + { + if (gbl_io_cnt <= 0) + MVOL_write(this); + + --gbl_io_cnt; + *gbl_io_ptr++ = c; + } + + UCHAR get() + { + if (gbl_io_cnt <= 0) + MVOL_read(this); + + --gbl_io_cnt; + return *gbl_io_ptr++; + } + +#ifdef WIRE_COMPRESS_SUPPORT + z_stream gbl_stream; +#endif + UCHAR* gbl_io_ptr; + int gbl_io_cnt; + UCHAR* gbl_compress_buffer; + UCHAR* gbl_crypt_buffer; + ULONG gbl_crypt_left; + UCHAR* gbl_decompress; + burp_rel* relations; burp_pkg* packages; burp_prc* procedures; @@ -1010,6 +1059,11 @@ public: SCHAR mvol_old_file [MAX_FILE_NAME_SIZE]; int mvol_volume_count; bool mvol_empty_file; + TEXT mvol_keyname_buffer[MAX_FILE_NAME_SIZE]; + const TEXT* mvol_keyname; + TEXT mvol_crypt_buffer[MAX_FILE_NAME_SIZE]; + const TEXT* mvol_crypt; + TEXT gbl_key_hash[(Firebird::Sha1::HASH_SIZE + 1) * 4 / 3 + 1]; // take into an account base64 Firebird::IAttachment* db_handle; Firebird::ITransaction* tr_handle; Firebird::ITransaction* global_trans; diff --git a/src/burp/burpswi.h b/src/burp/burpswi.h index 786b640ff1..a009e7a5b3 100644 --- a/src/burp/burpswi.h +++ b/src/burp/burpswi.h @@ -91,8 +91,12 @@ const int IN_SW_BURP_FETCHPASS = 45; // fetch default password from file to us const int IN_SW_BURP_VERBINT = 46; // verbose but with specific interval const int IN_SW_BURP_STATS = 47; // print statistics +const int IN_SW_BURP_ZIP = 48; // backup file in .zip format +const int IN_SW_BURP_KEYHOLD = 49; // name of KeyHolder plugin +const int IN_SW_BURP_KEYNAME = 50; // name of crypt key +const int IN_SW_BURP_CRYPT = 51; // name of crypt plugin + /**************************************************************************/ - // used 0BCDEFGILMNOPRSTUVYZ available AHJQWX static const char* const BURP_SW_MODE_RO = "READ_ONLY"; static const char* const BURP_SW_MODE_RW = "READ_WRITE"; @@ -111,6 +115,8 @@ static const Switches::in_sw_tab_t reference_burp_in_sw_table[] = // msg 73: @1CREATE_DATABASE create database from backup file {IN_SW_BURP_CO, isc_spb_bkp_convert, "CONVERT", 0, 0, 0, false, true, 254, 2, NULL, boBackup}, // msg 254: @1CO(NVERT) backup external files as tables + {IN_SW_BURP_CRYPT, isc_spb_bkp_crypt, "CRYPT", 0, 0, 0, false, false, 373, 3, NULL, boGeneral}, + // msg 373:@1CRY(PT) plugin name {IN_SW_BURP_E, isc_spb_bkp_expand, "EXPAND", 0, 0, 0, false, true, 97, 1, NULL, boBackup}, // msg 97: @1EXPAND no data compression {IN_SW_BURP_FA, isc_spb_bkp_factor, "FACTOR", 0, 0, 0, false, false, 181, 2, NULL, boBackup}, @@ -125,10 +131,14 @@ static const Switches::in_sw_tab_t reference_burp_in_sw_table[] = // msg 303: @1FIX_FSS_METADATA fix malformed UNICODE_FSS metadata {IN_SW_BURP_G, isc_spb_bkp_no_garbage_collect, "GARBAGE_COLLECT", 0, 0, 0, false, true, 177, 1, NULL, boBackup}, // msg 177:@1GARBAGE_COLLECT inhibit garbage collection - {IN_SW_BURP_I, isc_spb_res_deactivate_idx, "INACTIVE", 0, 0, 0, false, true, 78, 1, NULL, boRestore}, + {IN_SW_BURP_I, isc_spb_res_deactivate_idx, "INACTIVE", 0, 0, 0, false, true, 78, 1, NULL, boRestore}, // msg 78:@1INACTIVE deactivate indexes during restore - {IN_SW_BURP_IG, isc_spb_bkp_ignore_checksums, "IGNORE", 0, 0, 0, false, true, 178, 2, NULL, boBackup}, + {IN_SW_BURP_IG, isc_spb_bkp_ignore_checksums, "IGNORE", 0, 0, 0, false, true, 178, 2, NULL, boBackup}, // msg 178:@1IGNORE ignore bad checksums + {IN_SW_BURP_KEYHOLD, isc_spb_bkp_keyholder, "KEYHOLDER", 0, 0, 0, false, false, 382, 4, NULL, boGeneral}, + // msg 382:@1KEYHOLDER name of a key holder plugin + {IN_SW_BURP_KEYNAME, isc_spb_bkp_keyname, "KEYNAME", 0, 0, 0, false, false, 372, 4, NULL, boGeneral}, + // msg 372:@1KEYNAME name of a key to be used for encryption {IN_SW_BURP_K, isc_spb_res_no_shadow, "KILL", 0, 0, 0, false, true, 172, 1, NULL, boRestore}, // msg 172:@1KILL restore without creating shadows {IN_SW_BURP_L, isc_spb_bkp_ignore_limbo, "LIMBO", 0, 0, 0, false, true, 98, 1, NULL, boBackup}, @@ -167,13 +177,13 @@ static const Switches::in_sw_tab_t reference_burp_in_sw_table[] = // msg 355: @1SKIP_DATA skip data for table {IN_SW_BURP_STATS, isc_spb_bkp_stat, "STATISTICS", 0, 0, 0, false, false, 361, 2, NULL, boGeneral}, // msg 361: @1ST(ATISTICS) TDRW show statistics: - {-1, 0, " ", 0, 0, 0, false, false, 362, 0, NULL, boGeneral}, + {-1, 0, " ", 0, 0, 0, false, false, 362, 0, NULL, boGeneral}, // msg 362: T time from start - {-1, 0, " ", 0, 0, 0, false, false, 363, 0, NULL, boGeneral}, + {-1, 0, " ", 0, 0, 0, false, false, 363, 0, NULL, boGeneral}, // msg 363: D delta time - {-1, 0, " ", 0, 0, 0, false, false, 364, 0, NULL, boGeneral}, + {-1, 0, " ", 0, 0, 0, false, false, 364, 0, NULL, boGeneral}, // msg 364: R page reads - {-1, 0, " ", 0, 0, 0, false, false, 365, 0, NULL, boGeneral}, + {-1, 0, " ", 0, 0, 0, false, false, 365, 0, NULL, boGeneral}, // msg 365: W page writes {IN_SW_BURP_T, 0, "TRANSPORTABLE", 0, 0, 0, false, false, 175, 1, NULL, boBackup}, // msg 175: @1TRANSPORTABLE transportable backup -- data in XDR format @@ -197,6 +207,8 @@ static const Switches::in_sw_tab_t reference_burp_in_sw_table[] = // msg 109: @1Y redirect/suppress output (file path or OUTPUT_SUPPRESS) {IN_SW_BURP_Z, 0, "Z", 0, 0, 0, false, false, 104, 1, NULL, boGeneral}, // msg 104: @1Z print version number + {IN_SW_BURP_ZIP, isc_spb_bkp_zip, "ZIP", 0, 0, 0, false, true, 374, 3, NULL, boBackup}, + // msg 104: @1ZIP backup file is in zip compressed format /**************************************************************************/ // The next two 'virtual' switches are hidden from user and are needed // for services API diff --git a/src/burp/canonical.cpp b/src/burp/canonical.cpp index 365409929a..33fd60677b 100644 --- a/src/burp/canonical.cpp +++ b/src/burp/canonical.cpp @@ -42,11 +42,11 @@ #include "../common/xdr_proto.h" #include "../common/gdsassert.h" #include "../common/StatusHolder.h" +#include "../common/status.h" #include "fb_types.h" -// TMN: Currently we can't include remote/remote.h because we'd get -// conflicting blk_t definitions (we are gonna fix this, in due time). +using Firebird::FbLocalStatus; static bool_t burp_getbytes(XDR*, SCHAR *, u_int); static bool_t burp_putbytes(XDR*, const SCHAR*, u_int); @@ -426,8 +426,7 @@ static bool_t xdr_slice(XDR* xdrs, lstring* slice, /*USHORT sdl_length,*/ const sdl_info info; { - Firebird::LocalStatus ls; - Firebird::CheckStatusWrapper s(&ls); + FbLocalStatus s; if (SDL_info(&s, sdl, &info, 0)) return FALSE; } diff --git a/src/burp/mvol.cpp b/src/burp/mvol.cpp index 60c710f748..807880ad35 100644 --- a/src/burp/mvol.cpp +++ b/src/burp/mvol.cpp @@ -42,6 +42,7 @@ #include "../burp/burp.h" #include "../burp/burp_proto.h" #include "../burp/mvol_proto.h" +#include "../burp/split/spit.h" #include "../yvalve/gds_proto.h" #include "../common/gdsassert.h" #include "../common/os/os_utils.h" @@ -56,11 +57,17 @@ #include #endif +#include "../common/os/guid.h" +#include "../common/classes/ImplementHelper.h" +#include "../common/classes/GetPlugins.h" +#include "../common/classes/auto.h" #include "../common/classes/SafeArg.h" +#include "../common/StatusHolder.h" +#include "../common/db_alias.h" +#include "../common/status.h" using MsgFormat::SafeArg; - - +using Firebird::FbLocalStatus; const int open_mask = 0666; #ifdef WIN_NT @@ -71,12 +78,22 @@ const char* TERM_INPUT = "/dev/tty"; const char* TERM_OUTPUT = "/dev/tty"; #endif -const int MAX_HEADER_SIZE = 512; +static int mvol_read(int*, UCHAR**); +static FB_UINT64 mvol_fini_read(BurpGlobals*); +static void mvol_init_read(BurpGlobals*, const char*, USHORT*, int*, UCHAR**); +static UCHAR mvol_write(const UCHAR, int*, UCHAR**); +static const UCHAR* mvol_write_block(BurpGlobals*, const UCHAR*, ULONG); +static FB_UINT64 mvol_fini_write(BurpGlobals*, int*, UCHAR**); +static void mvol_init_write(BurpGlobals*, const char*, int*, UCHAR**); +static void brio_fini(BurpGlobals*); + +static const int MAX_HEADER_SIZE = 512; +static const int ZC_BUFSIZE = IO_BUFFER_SIZE; static inline int get(BurpGlobals* tdgbl) { if (tdgbl->mvol_io_cnt <= 0) - MVOL_read(NULL, NULL); + mvol_read(NULL, NULL); return (--tdgbl->mvol_io_cnt >= 0 ? *tdgbl->mvol_io_ptr++ : 255); } @@ -92,6 +109,62 @@ static UCHAR debug_on = 0; // able to turn this on in debug mode const int burp_msg_fac = 12; +#ifdef WIRE_COMPRESS_SUPPORT +namespace { + class ZLib + { + public: + explicit ZLib(Firebird::MemoryPool&) + { +#ifdef WIN_NT + const char* name = "zlib1.dll"; +#else + const char* name = "libz." SHRLIB_EXT ".1"; +#endif + z.reset(ModuleLoader::fixAndLoadModule(name)); + if (z) + symbols(); + } + + int ZEXPORT (*deflateInit_)(z_stream* strm, int level, const char *version, int stream_size); + int ZEXPORT (*inflateInit_)(z_stream* strm, const char *version, int stream_size); + int ZEXPORT (*deflate)(z_stream* strm, int flush); + int ZEXPORT (*inflate)(z_stream* strm, int flush); + void ZEXPORT (*deflateEnd)(z_stream* strm); + void ZEXPORT (*inflateEnd)(z_stream* strm); + + operator bool() { return z.hasData(); } + bool operator!() { return !z.hasData(); } + + private: + Firebird::AutoPtr z; + + void symbols() + { +#define FB_ZSYMB(A) z->findSymbol(STRINGIZE(A), A); if (!A) { z.reset(NULL); return; } + FB_ZSYMB(deflateInit_) + FB_ZSYMB(inflateInit_) + FB_ZSYMB(deflate) + FB_ZSYMB(inflate) + FB_ZSYMB(deflateEnd) + FB_ZSYMB(inflateEnd) +#undef FB_ZSYMB + } + }; + + Firebird::InitInstance zlib; + + void* allocFunc(void*, uInt items, uInt size) + { + return MemoryPool::globalAlloc(items * size ALLOC_ARGS); + } + + void freeFunc(void*, void* address) + { + MemoryPool::globalFree(address); + } +} +#endif // WIRE_COMPRESS_SUPPORT static void bad_attribute(int, USHORT); static void file_not_empty(); @@ -103,7 +176,444 @@ static void put_numeric(SCHAR, int); static bool read_header(DESC, ULONG*, USHORT*, bool); static bool write_header(DESC, ULONG, bool); static DESC next_volume(DESC, ULONG, bool); -static void mvol_read(int*, UCHAR**); +static void os_read(int*, UCHAR**); +static void crypt_write_block(BurpGlobals*, const UCHAR*, FB_SIZE_T, bool); +static ULONG crypt_read_block(BurpGlobals*, UCHAR*, FB_SIZE_T); +static void zip_write_block(BurpGlobals*, const UCHAR*, FB_SIZE_T, bool); +static ULONG unzip_read_block(BurpGlobals*, UCHAR*, FB_SIZE_T); + +// Portion of data passed to crypt plugin +const ULONG CRYPT_STEP = 256; + +class DbInfo FB_FINAL : public Firebird::RefCntIface > +{ +public: + DbInfo(BurpGlobals* bg) + : tdgbl(bg) + { } + + // IDbCryptInfo implementation + const char* getDatabaseFullPath(Firebird::CheckStatusWrapper*) + { + return tdgbl->gbl_database_file_name; + } + + int release() + { + if (--refCounter != 0) + return 1; + + delete this; + return 0; + } + +private: + BurpGlobals* tdgbl; +}; + + +struct BurpCrypt +{ + BurpCrypt() + : crypt_plugin(NULL), db_info(NULL), holder_plugin(NULL), crypt_callback(NULL) + { } + + ~BurpCrypt() + { + if (crypt_plugin) + Firebird::PluginManagerInterfacePtr()->releasePlugin(crypt_plugin); + if (holder_plugin) + Firebird::PluginManagerInterfacePtr()->releasePlugin(holder_plugin); + } + + Firebird::IDbCryptPlugin* crypt_plugin; + Firebird::RefPtr db_info; + Firebird::IKeyHolderPlugin* holder_plugin; + Firebird::ICryptKeyCallback* crypt_callback; +}; + + +//____________________________________________________________ +// +// +static void calc_hash(Firebird::string& valid, Firebird::IDbCryptPlugin* plugin) +{ + // crypt verifier + const char* sample = "0123456789ABCDEF"; + char result[16]; + FbLocalStatus sv; + + plugin->encrypt(&sv, sizeof(result), sample, result); + check(&sv); + + // calculate its hash + const Firebird::string verifier(result, sizeof(result)); + Firebird::Sha1::hashBased64(valid, verifier); +} + +//____________________________________________________________ +// +// +static Firebird::IKeyHolderPlugin* mvol_get_holder(BurpGlobals* tdgbl, Firebird::RefPtr& config) +{ + fb_assert(tdgbl->gbl_sw_keyholder); + + if (!tdgbl->gbl_crypt) + { + Firebird::GetPlugins + keyControl(Firebird::IPluginManager::TYPE_KEY_HOLDER, config, tdgbl->gbl_sw_keyholder); + if (!keyControl.hasData()) + (Firebird::Arg::Gds(isc_no_keyholder_plugin) << tdgbl->gbl_sw_keyholder).raise(); + + BurpCrypt* g = tdgbl->gbl_crypt = FB_NEW BurpCrypt; + g->holder_plugin = keyControl.plugin(); + g->holder_plugin->addRef(); + + // Also do not forget about keys from services manager + Firebird::ICryptKeyCallback* cb = tdgbl->uSvc->getCryptCallback(); + if (cb) + g->holder_plugin->keyCallback(&tdgbl->throwStatus, cb); + } + + return tdgbl->gbl_crypt->holder_plugin; +} + +//____________________________________________________________ +// +// +Firebird::ICryptKeyCallback* MVOL_get_crypt(BurpGlobals* tdgbl) +{ + fb_assert(tdgbl->gbl_sw_keyholder); + + if (!tdgbl->gbl_crypt) + { + // Get per-DB config + Firebird::PathName dummy; + Firebird::RefPtr config; + expandDatabaseName(tdgbl->gbl_database_file_name, dummy, &config); + + mvol_get_holder(tdgbl, config); + } + + BurpCrypt* g = tdgbl->gbl_crypt; + if (!g->crypt_callback) + { + FbLocalStatus status; + + g->crypt_callback = g->holder_plugin->chainHandle(&status); + check(&status); + } + + return g->crypt_callback; +} + +//____________________________________________________________ +// +// +static void start_crypt(BurpGlobals* tdgbl) +{ + fb_assert(tdgbl->gbl_sw_keyholder); + if (tdgbl->gbl_crypt && tdgbl->gbl_crypt->crypt_plugin) + return; + + FbLocalStatus status; + + // Get per-DB config + Firebird::PathName dummy; + Firebird::RefPtr config; + expandDatabaseName(tdgbl->gbl_database_file_name, dummy, &config); + + // Prepare key holders + Firebird::IKeyHolderPlugin* keyHolder = mvol_get_holder(tdgbl, config); + + // Load crypt plugin + if (!tdgbl->mvol_crypt) + tdgbl->mvol_crypt = tdgbl->gbl_sw_crypt; + if (!tdgbl->mvol_crypt) + BURP_error(378, true); + + Firebird::GetPlugins + cryptControl(Firebird::IPluginManager::TYPE_DB_CRYPT, config, tdgbl->mvol_crypt); + if (!cryptControl.hasData()) + (Firebird::Arg::Gds(isc_no_crypt_plugin) << tdgbl->mvol_crypt).raise(); + + Firebird::RefPtr dbInfo(FB_NEW DbInfo(tdgbl)); + Firebird::IDbCryptPlugin* p = cryptControl.plugin(); + p->setInfo(&status, dbInfo); + if (!status.isSuccess()) + { + const ISC_STATUS* v = status->getErrors(); + if (v[0] == isc_arg_gds && v[1] != isc_arg_end && v[1] != isc_interface_version_too_old) + Firebird::status_exception::raise(&status); + } + + // Initialize key in crypt plugin + p->setKey(&status, 1, &keyHolder, tdgbl->mvol_keyname); + check(&status); + + // Validate hash + if (tdgbl->gbl_key_hash[0]) + { + Firebird::string hash; + calc_hash(hash, p); + if (hash != tdgbl->gbl_key_hash) + (Firebird::Arg::Gds(isc_bad_crypt_key) << tdgbl->mvol_keyname).raise(); + } + + // crypt plugin is ready + BurpCrypt* g = tdgbl->gbl_crypt; + g->db_info.moveFrom(dbInfo); + g->crypt_plugin = p; + p->addRef(); +} + +//____________________________________________________________ +// +// +static void crypt_write_block(BurpGlobals* tdgbl, const UCHAR* buffer, FB_SIZE_T buffer_length, bool flash) +{ + if (!(tdgbl->gbl_sw_keyholder)) + { + mvol_write_block(tdgbl, buffer, buffer_length); + return; + } + + start_crypt(tdgbl); + fb_assert(tdgbl->gbl_crypt); + + while (buffer_length) + { + ULONG step = MIN(buffer_length + tdgbl->gbl_crypt_left, ZC_BUFSIZE); + fb_assert(CRYPT_STEP < ZC_BUFSIZE); + + ULONG move = step - tdgbl->gbl_crypt_left; + memcpy(tdgbl->gbl_crypt_buffer + tdgbl->gbl_crypt_left, buffer, move); + buffer_length -= move; + buffer += move; + + tdgbl->gbl_crypt_left = step % CRYPT_STEP; + step -= tdgbl->gbl_crypt_left; + + if (flash && buffer_length == 0 && tdgbl->gbl_crypt_left) + { + step += CRYPT_STEP; + tdgbl->gbl_crypt_left = 0; + } + + FbLocalStatus status; + + for (ULONG offset = 0; offset < step; offset += CRYPT_STEP) + { + UCHAR* b = &tdgbl->gbl_crypt_buffer[offset]; + tdgbl->gbl_crypt->crypt_plugin->encrypt(&status, CRYPT_STEP, b, b); + check(&status); + } + + mvol_write_block(tdgbl, tdgbl->gbl_crypt_buffer, step); + memmove(tdgbl->gbl_crypt_buffer, &tdgbl->gbl_crypt_buffer[step], tdgbl->gbl_crypt_left); + } +} + + +//____________________________________________________________ +// +// +static ULONG crypt_read_block(BurpGlobals* tdgbl, UCHAR* buffer, FB_SIZE_T buffer_length) +{ + ULONG& length = tdgbl->gbl_crypt_left; + + while (length < (tdgbl->gbl_key_hash[0] ? CRYPT_STEP : 1)) + { + // free bytes in gbl_crypt_buffer + ULONG count = ZC_BUFSIZE - length; + UCHAR* endPtr = &tdgbl->gbl_crypt_buffer[length]; + + // If IO buffer empty, reload it + if (tdgbl->blk_io_cnt <= 0) + { + *endPtr++ = mvol_read(&tdgbl->blk_io_cnt, &tdgbl->blk_io_ptr); + --count; + ++length; + } + + // Copy data from the IO buffer + const ULONG n = MIN(count, (ULONG) tdgbl->blk_io_cnt); + memcpy(endPtr, tdgbl->blk_io_ptr, n); + endPtr += n; + length += n; + + // Move IO pointer + tdgbl->blk_io_cnt -= n; + tdgbl->blk_io_ptr += n; + } + + UCHAR* ptr = tdgbl->gbl_crypt_buffer; + buffer_length = MIN(length, buffer_length); + + if (!tdgbl->gbl_key_hash[0]) + { + memcpy(buffer, ptr, buffer_length); + } + else + { + start_crypt(tdgbl); + fb_assert(tdgbl->gbl_crypt); + + ULONG left = buffer_length % CRYPT_STEP; + buffer_length -= left; + + FbLocalStatus status; + + for (ULONG offset = 0; offset < buffer_length; offset += CRYPT_STEP) + { + tdgbl->gbl_crypt->crypt_plugin->decrypt(&status, CRYPT_STEP, &ptr[offset], &buffer[offset]); + check(&status); + } + } + + // Some data may stay in gbl_crypt_buffer for next time + length -= buffer_length; + memmove(ptr, ptr + buffer_length, length); + + return buffer_length; +} + + +//____________________________________________________________ +// +// + +static ULONG unzip_read_block(BurpGlobals* tdgbl, UCHAR* buffer, FB_SIZE_T buffer_length) +{ + if (!tdgbl->gbl_sw_zip) + { + return crypt_read_block(tdgbl, buffer, buffer_length); + } + +#ifdef WIRE_COMPRESS_SUPPORT + z_stream& strm = tdgbl->gbl_stream; + strm.avail_out = buffer_length; + strm.next_out = buffer; + + for (;;) + { + if (strm.avail_in) + { +#ifdef COMPRESS_DEBUG + fprintf(stderr, "Data to inflate %d port %p\n", strm.avail_in, port); +#if COMPRESS_DEBUG > 1 + for (unsigned n = 0; n < strm.avail_in; ++n) fprintf(stderr, "%02x ", strm.next_in[n]); + fprintf(stderr, "\n"); +#endif +#endif + ULONG wasAvail = strm.avail_out; + int ret = zlib().inflate(&strm, Z_NO_FLUSH); + if (ret == Z_DATA_ERROR && wasAvail != strm.avail_out) + ret = Z_OK; + + if (ret != Z_OK) + { +#ifdef COMPRESS_DEBUG + fprintf(stderr, "Inflate error %d\n", ret); +#endif + BURP_error(379, true, SafeArg() << ret); + } +#ifdef COMPRESS_DEBUG + fprintf(stderr, "Inflated data %d\n", buffer_length - strm.avail_out); +#if COMPRESS_DEBUG > 1 + for (unsigned n = 0; n < buffer_length - strm.avail_out; ++n) fprintf(stderr, "%02x ", buffer[n]); + fprintf(stderr, "\n"); +#endif +#endif + if (strm.next_out != buffer) + break; + + UCHAR* compressed = tdgbl->gbl_decompress; + if (strm.next_in != compressed) + { + memmove(compressed, strm.next_in, strm.avail_in); + strm.next_in = compressed; + } + } + else + strm.next_in = tdgbl->gbl_decompress; + + ULONG l = ZC_BUFSIZE - strm.avail_in; + l = crypt_read_block(tdgbl, strm.next_in, l); + strm.avail_in += l; + } + + return buffer_length - strm.avail_out; +#else + (Firebird::Arg::Gds(isc_random) << "No inflate support").raise(); +#endif +} + +static void zip_write_block(BurpGlobals* tdgbl, const UCHAR* buffer, FB_SIZE_T buffer_length, bool flash) +{ + if (!tdgbl->gbl_sw_zip) + { + crypt_write_block(tdgbl, buffer, buffer_length, flash); + return; + } + +#ifdef WIRE_COMPRESS_SUPPORT + z_stream& strm = tdgbl->gbl_stream; + strm.avail_in = buffer_length; + strm.next_in = (Bytef*)buffer; + UCHAR* compressed = tdgbl->gbl_decompress; + + if (!strm.next_out) + { + strm.avail_out = ZC_BUFSIZE; + strm.next_out = (Bytef*)compressed; + } + + bool expectMoreOut = flash; + + while (strm.avail_in || expectMoreOut) + { +#ifdef COMPRESS_DEBUG + fprintf(stderr, "Data to deflate %d port %p\n", strm.avail_in, port); +#if COMPRESS_DEBUG>1 + for (unsigned n = 0; n < strm.avail_in; ++n) fprintf(stderr, "%02x ", strm.next_in[n]); + fprintf(stderr, "\n"); +#endif +#endif + int ret = zlib().deflate(&strm, flash ? Z_FULL_FLUSH : Z_NO_FLUSH); + if (ret == Z_BUF_ERROR) + ret = 0; + if (ret != 0) + { +#ifdef COMPRESS_DEBUG + fprintf(stderr, "Deflate error %d\n", ret); +#endif + BURP_error(380, true, SafeArg() << ret); + } + +#ifdef COMPRESS_DEBUG + fprintf(stderr, "Deflated data %d\n", ZC_BUFSIZE - strm.avail_out); +#if COMPRESS_DEBUG>1 + for (unsigned n = 0; n < port->port_buff_size - strm.avail_out; ++n) + fprintf(stderr, "%02x ", compressed[n]); + fprintf(stderr, "\n"); +#endif +#endif + + expectMoreOut = !strm.avail_out; + if ((ZC_BUFSIZE != strm.avail_out) && (flash || !strm.avail_out)) + { + crypt_write_block(tdgbl, compressed, ZC_BUFSIZE - strm.avail_out, flash); + + strm.avail_out = ZC_BUFSIZE; + strm.next_out = (Bytef*)compressed; + } + } +#else + (Firebird::Arg::Gds(isc_random) << "No deflate support").raise(); +#endif +} + //____________________________________________________________ @@ -113,6 +623,20 @@ FB_UINT64 MVOL_fini_read() { BurpGlobals* tdgbl = BurpGlobals::getSpecific(); +#ifdef WIRE_COMPRESS_SUPPORT + if (tdgbl->gbl_sw_zip) + { + zlib().inflateEnd(&tdgbl->gbl_stream); + } +#endif + + brio_fini(tdgbl); + + return mvol_fini_read(tdgbl); +} + +FB_UINT64 mvol_fini_read(BurpGlobals* tdgbl) +{ if (!tdgbl->stdIoMode) { close_platf(tdgbl->file_desc); @@ -129,8 +653,8 @@ FB_UINT64 MVOL_fini_read() tdgbl->file_desc = INVALID_HANDLE_VALUE; BURP_free(tdgbl->mvol_io_buffer); tdgbl->mvol_io_buffer = NULL; - tdgbl->io_cnt = 0; - tdgbl->io_ptr = NULL; + tdgbl->blk_io_cnt = 0; + tdgbl->blk_io_ptr = NULL; return tdgbl->mvol_cumul_count; } @@ -138,11 +662,27 @@ FB_UINT64 MVOL_fini_read() //____________________________________________________________ // // -FB_UINT64 MVOL_fini_write(int* io_cnt, UCHAR** io_ptr) +FB_UINT64 MVOL_fini_write() { BurpGlobals* tdgbl = BurpGlobals::getSpecific(); - MVOL_write(rec_end, io_cnt, io_ptr); + zip_write_block(tdgbl, tdgbl->gbl_compress_buffer, tdgbl->gbl_io_ptr - tdgbl->gbl_compress_buffer, true); + +#ifdef WIRE_COMPRESS_SUPPORT + if (tdgbl->gbl_sw_zip) + { + zlib().deflateEnd(&tdgbl->gbl_stream); + } +#endif + + brio_fini(tdgbl); + + return mvol_fini_write(tdgbl, &tdgbl->blk_io_cnt, &tdgbl->blk_io_ptr); +} + +FB_UINT64 mvol_fini_write(BurpGlobals* tdgbl, int* io_cnt, UCHAR** io_ptr) +{ + mvol_write(rec_end, io_cnt, io_ptr); flush_platf(tdgbl->file_desc); if (!tdgbl->stdIoMode) @@ -161,8 +701,9 @@ FB_UINT64 MVOL_fini_write(int* io_cnt, UCHAR** io_ptr) BURP_free(tdgbl->mvol_io_header); tdgbl->mvol_io_header = NULL; tdgbl->mvol_io_buffer = NULL; - tdgbl->io_cnt = 0; - tdgbl->io_ptr = NULL; + tdgbl->blk_io_cnt = 0; + tdgbl->blk_io_ptr = NULL; + return tdgbl->mvol_cumul_count; } @@ -175,6 +716,23 @@ void MVOL_init(ULONG io_buf_size) BurpGlobals* tdgbl = BurpGlobals::getSpecific(); tdgbl->mvol_io_buffer_size = io_buf_size; + + tdgbl->gbl_compress_buffer = FB_NEW(UCHAR[ZC_BUFSIZE]); + tdgbl->gbl_crypt_buffer = FB_NEW(UCHAR[ZC_BUFSIZE]); + tdgbl->gbl_decompress = FB_NEW UCHAR[ZC_BUFSIZE]; +} + + +static void brio_fini(BurpGlobals* tdgbl) +{ + delete[] tdgbl->gbl_compress_buffer; + tdgbl->gbl_compress_buffer = NULL; + + delete[] tdgbl->gbl_crypt_buffer; + tdgbl->gbl_crypt_buffer = NULL; + + delete[] tdgbl->gbl_decompress; + tdgbl->gbl_decompress = NULL;; } @@ -182,10 +740,35 @@ void MVOL_init(ULONG io_buf_size) // // Read init record from backup file // -void MVOL_init_read(const char* file_name, USHORT* format, int* cnt, UCHAR** ptr) +void MVOL_init_read(const char* file_name, USHORT* format) { BurpGlobals* tdgbl = BurpGlobals::getSpecific(); + mvol_init_read(tdgbl, file_name, format, &tdgbl->blk_io_cnt, &tdgbl->blk_io_ptr); + + tdgbl->gbl_io_cnt = 0; + tdgbl->gbl_io_ptr = NULL; + + if (tdgbl->gbl_sw_zip) + { + z_stream& strm = tdgbl->gbl_stream; + + strm.zalloc = allocFunc; + strm.zfree = freeFunc; + strm.opaque = Z_NULL; + strm.avail_in = 0; + strm.next_in = Z_NULL; + int ret = zlib().inflateInit(&strm); + if (ret != Z_OK) + { + BURP_error(383, true, SafeArg() << ret); + } + } + +} + +void mvol_init_read(BurpGlobals* tdgbl, const char* file_name, USHORT* format, int* cnt, UCHAR** ptr) +{ tdgbl->mvol_volume_count = 1; tdgbl->mvol_empty_file = true; @@ -225,10 +808,31 @@ void MVOL_init_read(const char* file_name, USHORT* format, int* cnt, UCHAR** ptr // // Write init record to the backup file // -void MVOL_init_write(const char* file_name, int* cnt, UCHAR** ptr) +void MVOL_init_write(const char* file_name) { BurpGlobals* tdgbl = BurpGlobals::getSpecific(); + mvol_init_write(tdgbl, file_name, &tdgbl->blk_io_cnt, &tdgbl->blk_io_ptr); + + tdgbl->gbl_io_cnt = ZC_BUFSIZE; + tdgbl->gbl_io_ptr = tdgbl->gbl_compress_buffer; + + if (tdgbl->gbl_sw_zip) + { + z_stream& strm = tdgbl->gbl_stream; + + strm.zalloc = allocFunc; + strm.zfree = freeFunc; + strm.opaque = Z_NULL; + int ret = zlib().deflateInit(&strm, Z_DEFAULT_COMPRESSION); + if (ret != Z_OK) + BURP_error(384, true, SafeArg() << ret); + strm.next_out = Z_NULL; + } +} + +void mvol_init_write(BurpGlobals* tdgbl, const char* file_name, int* cnt, UCHAR** ptr) +{ tdgbl->mvol_volume_count = 1; tdgbl->mvol_empty_file = true; @@ -268,7 +872,14 @@ void MVOL_init_write(const char* file_name, int* cnt, UCHAR** ptr) // // Read a buffer's worth of data. (common) // -int MVOL_read(int* cnt, UCHAR** ptr) +void MVOL_read(BurpGlobals* tdgbl) +{ + // Setup our pointer + tdgbl->gbl_io_ptr = tdgbl->gbl_compress_buffer; + tdgbl->gbl_io_cnt = unzip_read_block(tdgbl, tdgbl->gbl_io_ptr, ZC_BUFSIZE); +} + +int mvol_read(int* cnt, UCHAR** ptr) { BurpGlobals* tdgbl = BurpGlobals::getSpecific(); @@ -285,7 +896,7 @@ int MVOL_read(int* cnt, UCHAR** ptr) } else { - mvol_read(cnt, ptr); + os_read(cnt, ptr); } tdgbl->mvol_cumul_count += tdgbl->mvol_io_cnt; @@ -305,7 +916,7 @@ int MVOL_read(int* cnt, UCHAR** ptr) // // Read a buffer's worth of data. (non-WIN_NT) // -static void mvol_read(int* cnt, UCHAR** ptr) +static void os_read(int* cnt, UCHAR** ptr) { BurpGlobals* tdgbl = BurpGlobals::getSpecific(); @@ -348,11 +959,11 @@ static void mvol_read(int* cnt, UCHAR** ptr) // // Read a buffer's worth of data. (WIN_NT) // -static void mvol_read(int* cnt, UCHAR** ptr) +static void os_read(int* cnt, UCHAR** ptr) { BurpGlobals* tdgbl = BurpGlobals::getSpecific(); - fb_assert(tdgbl->io_cnt <= 0); + fb_assert(tdgbl->blk_io_cnt <= 0); for (;;) { @@ -391,33 +1002,24 @@ static void mvol_read(int* cnt, UCHAR** ptr) // UCHAR* MVOL_read_block(BurpGlobals* tdgbl, UCHAR* ptr, ULONG count) { - // To handle tape drives & Multi-volume boundaries, use the normal - // read function, instead of doing a more optimal bulk read. - - while (count) { // If buffer empty, reload it - if (tdgbl->io_cnt <= 0) - { - *ptr++ = MVOL_read(&tdgbl->io_cnt, &tdgbl->io_ptr); + if (tdgbl->gbl_io_cnt <= 0) + MVOL_read(tdgbl); - // One byte was "read" by MVOL_read - count--; - } - - const ULONG n = MIN(count, (ULONG) tdgbl->io_cnt); + const ULONG n = MIN(count, (ULONG) tdgbl->gbl_io_cnt); // Copy data from the IO buffer - memcpy(ptr, tdgbl->io_ptr, n); + memcpy(ptr, tdgbl->gbl_io_ptr, n); ptr += n; // Skip ahead in current buffer count -= n; - tdgbl->io_cnt -= n; - tdgbl->io_ptr += n; + tdgbl->gbl_io_cnt -= n; + tdgbl->gbl_io_ptr += n; } return ptr; @@ -438,21 +1040,18 @@ void MVOL_skip_block( BurpGlobals* tdgbl, ULONG count) while (count) { // If buffer empty, reload it - if (tdgbl->io_cnt <= 0) + if (tdgbl->gbl_io_cnt <= 0) { - MVOL_read(&tdgbl->io_cnt, &tdgbl->io_ptr); - - // One byte was "read" by MVOL_read - count--; + MVOL_read(tdgbl); } - const ULONG n = MIN(count, (ULONG) tdgbl->io_cnt); + const ULONG n = MIN(count, (ULONG) tdgbl->gbl_io_cnt); // Skip ahead in current buffer count -= n; - tdgbl->io_cnt -= n; - tdgbl->io_ptr += n; + tdgbl->gbl_io_cnt -= n; + tdgbl->gbl_io_ptr += n; } } @@ -463,7 +1062,7 @@ void MVOL_skip_block( BurpGlobals* tdgbl, ULONG count) // detect if it's a tape, rewind if so // and set the buffer size // -DESC MVOL_open(const char* name, ULONG mode, ULONG create) +DESC NT_tape_open(const char* name, ULONG mode, ULONG create) { HANDLE handle; TAPE_GET_MEDIA_PARAMETERS param; @@ -526,7 +1125,18 @@ DESC MVOL_open(const char* name, ULONG mode, ULONG create) // // Write a buffer's worth of data. // -UCHAR MVOL_write(const UCHAR c, int* io_cnt, UCHAR** io_ptr) +void MVOL_write(BurpGlobals* tdgbl) +{ + fb_assert(tdgbl->gbl_io_ptr >= tdgbl->gbl_compress_buffer); + fb_assert(tdgbl->gbl_io_ptr <= tdgbl->gbl_compress_buffer + ZC_BUFSIZE); + + zip_write_block(tdgbl, tdgbl->gbl_compress_buffer, tdgbl->gbl_io_ptr - tdgbl->gbl_compress_buffer, false); + + tdgbl->gbl_io_cnt = ZC_BUFSIZE; + tdgbl->gbl_io_ptr = tdgbl->gbl_compress_buffer; +} + +UCHAR mvol_write(const UCHAR c, int* io_cnt, UCHAR** io_ptr) { const UCHAR* ptr; ULONG cnt = 0; @@ -746,6 +1356,34 @@ UCHAR MVOL_write(const UCHAR c, int* io_cnt, UCHAR** io_ptr) // Return a pointer to the first position NOT written from. // const UCHAR* MVOL_write_block(BurpGlobals* tdgbl, const UCHAR* ptr, ULONG count) +{ + while (count) + { + // If buffer full, write it + if (tdgbl->gbl_io_cnt <= 0) + { + zip_write_block(tdgbl, tdgbl->gbl_compress_buffer, tdgbl->gbl_io_ptr - tdgbl->gbl_compress_buffer, false); + + tdgbl->gbl_io_ptr = tdgbl->gbl_compress_buffer; + tdgbl->gbl_io_cnt = ZC_BUFSIZE; + } + + const ULONG n = MIN(count, (ULONG) tdgbl->gbl_io_cnt); + + // Copy data to the IO buffer + memcpy(tdgbl->gbl_io_ptr, ptr, n); + ptr += n; + count -= n; + + // Skip ahead in current buffer + tdgbl->gbl_io_cnt -= n; + tdgbl->gbl_io_ptr += n; + } + + return ptr; +} + +const UCHAR* mvol_write_block(BurpGlobals* tdgbl, const UCHAR* ptr, ULONG count) { // To handle tape drives & Multi-volume boundaries, use the normal // write function, instead of doing a more optimal bulk write. @@ -753,26 +1391,26 @@ const UCHAR* MVOL_write_block(BurpGlobals* tdgbl, const UCHAR* ptr, ULONG count) while (count) { // If buffer full, dump it - if (tdgbl->io_cnt <= 0) + if (tdgbl->blk_io_cnt <= 0) { - MVOL_write(*ptr++, &tdgbl->io_cnt, &tdgbl->io_ptr); + mvol_write(*ptr++, &tdgbl->blk_io_cnt, &tdgbl->blk_io_ptr); - // One byte was written by MVOL_write + // One byte was written by mvol_write count--; } - const ULONG n = MIN(count, (ULONG) tdgbl->io_cnt); + const ULONG n = MIN(count, (ULONG) tdgbl->blk_io_cnt); // Copy data to the IO buffer - memcpy(tdgbl->io_ptr, ptr, n); + memcpy(tdgbl->blk_io_ptr, ptr, n); ptr += n; // Skip ahead in current buffer count -= n; - tdgbl->io_cnt -= n; - tdgbl->io_ptr += n; + tdgbl->blk_io_cnt -= n; + tdgbl->blk_io_ptr += n; } return ptr; @@ -914,7 +1552,7 @@ static DESC next_volume( DESC handle, ULONG mode, bool full_buffer) prompt_for_name(new_file, sizeof(new_file)); #ifdef WIN_NT - new_desc = MVOL_open(new_file, mode, OPEN_ALWAYS); + new_desc = NT_tape_open(new_file, mode, OPEN_ALWAYS); if (new_desc == INVALID_HANDLE_VALUE) #else new_desc = os_utils::open(new_file, mode, open_mask); @@ -1228,17 +1866,13 @@ static bool read_header(DESC handle, ULONG* buffer_size, USHORT* format, bool in } } *p = 0; - if (!init_flag && strcmp(buffer, tdgbl->gbl_database_file_name)) + if (!init_flag && strcmp(buffer, tdgbl->mvol_db_name_buffer)) { - BURP_msg_get(231, msg, SafeArg() << tdgbl->gbl_database_file_name << buffer); + BURP_msg_get(231, msg, SafeArg() << tdgbl->mvol_db_name_buffer << buffer); // Expected backup database %s, found %s\n printf("%s", msg); return false; } - if (init_flag) - { - tdgbl->gbl_database_file_name = tdgbl->mvol_db_name_buffer; - } break; case att_backup_format: @@ -1268,6 +1902,77 @@ static bool read_header(DESC handle, ULONG* buffer_size, USHORT* format, bool in } break; + case att_backup_keyname: + l = get(tdgbl); + p = tdgbl->mvol_keyname_buffer; + maxlen = sizeof(tdgbl->mvol_keyname_buffer) - 1; + if (l) + { + do { + *p++ = get(tdgbl); + } while (--l && --maxlen); + // Discard elements that don't fit in the buffer, possible corrupt backup + if (l > 0 && !maxlen) + { + while (l--) + get(tdgbl); + } + } + *p = 0; + tdgbl->mvol_keyname = tdgbl->mvol_keyname_buffer; + if (!tdgbl->gbl_sw_keyname) + tdgbl->gbl_sw_keyname = tdgbl->mvol_keyname; + break; + + case att_backup_crypt: + l = get(tdgbl); + p = tdgbl->mvol_crypt_buffer; + maxlen = sizeof(tdgbl->mvol_crypt_buffer) - 1; + if (l) + { + do { + *p++ = get(tdgbl); + } while (--l && --maxlen); + // Discard elements that don't fit in the buffer, possible corrupt backup + if (l > 0 && !maxlen) + { + while (l--) + get(tdgbl); + } + } + *p = 0; + tdgbl->mvol_crypt = tdgbl->mvol_crypt_buffer; + if (!tdgbl->gbl_sw_crypt) + tdgbl->gbl_sw_crypt = tdgbl->mvol_crypt; + break; + + case att_backup_zip: + if (get_numeric()) + tdgbl->gbl_sw_zip = true; + break; + + case att_backup_hash: + if (!tdgbl->gbl_sw_keyholder) + BURP_error(376, true); + + l = get(tdgbl); + p = tdgbl->gbl_key_hash; + maxlen = sizeof(tdgbl->gbl_key_hash) - 1; + if (l) + { + do { + *p++ = get(tdgbl); + } while (--l && --maxlen); + // Discard elements that don't fit in the buffer, possible corrupt backup + if (l > 0 && !maxlen) + { + while (l--) + get(tdgbl); + } + } + *p = 0; + break; + default: bad_attribute(attribute, 59); // msg 59 backup } @@ -1297,13 +2002,38 @@ static bool write_header(DESC handle, ULONG backup_buffer_size, bool full_buffer if (tdgbl->gbl_sw_transportable) put_numeric(att_backup_transportable, 1); + if (tdgbl->gbl_sw_zip) + put_numeric(att_backup_zip, 1); + put_numeric(att_backup_blksize, backup_buffer_size); tdgbl->mvol_io_volume = tdgbl->mvol_io_ptr + 2; put_numeric(att_backup_volume, tdgbl->mvol_volume_count); + if (tdgbl->gbl_sw_keyname) + { + tdgbl->mvol_keyname = tdgbl->gbl_sw_keyname; + put_asciz(att_backup_keyname, tdgbl->mvol_keyname); + } + + if (tdgbl->gbl_sw_crypt) + { + tdgbl->mvol_crypt = tdgbl->gbl_sw_crypt; + put_asciz(att_backup_crypt, tdgbl->mvol_crypt); + } + put_asciz(att_backup_file, tdgbl->gbl_database_file_name); put_asciz(att_backup_date, tdgbl->gbl_backup_start_time); + + if (tdgbl->gbl_sw_keyholder) + { + start_crypt(tdgbl); + fb_assert(tdgbl->gbl_crypt && tdgbl->gbl_crypt->crypt_plugin); + Firebird::string hash; + calc_hash(hash, tdgbl->gbl_crypt->crypt_plugin); + put_asciz(att_backup_hash, hash.c_str()); + } + put(tdgbl, att_end); tdgbl->mvol_io_data = tdgbl->mvol_io_ptr; diff --git a/src/burp/mvol_proto.h b/src/burp/mvol_proto.h index e47bbea5ea..2ea1c805a1 100644 --- a/src/burp/mvol_proto.h +++ b/src/burp/mvol_proto.h @@ -24,24 +24,27 @@ #ifndef BURP_MVOL_PROTO_H #define BURP_MVOL_PROTO_H -#include "../burp/burp.h" +#include "firebird/Interface.h" +#include "std_desc.h" +class BurpGlobals; FB_UINT64 MVOL_fini_read(); -FB_UINT64 MVOL_fini_write(int*, UCHAR**); +FB_UINT64 MVOL_fini_write(); void MVOL_init(ULONG); -void MVOL_init_read(const char*, USHORT*, int*, UCHAR**); -void MVOL_init_write(const char*, int*, UCHAR**); +void MVOL_init_read(const char*, USHORT*); +void MVOL_init_write(const char*); bool MVOL_split_hdr_write(); bool MVOL_split_hdr_read(); -int MVOL_read(int*, UCHAR**); +void MVOL_read(BurpGlobals*); UCHAR* MVOL_read_block(BurpGlobals*, UCHAR*, ULONG); void MVOL_skip_block(BurpGlobals*, ULONG); -UCHAR MVOL_write(const UCHAR, int*, UCHAR**); +void MVOL_write(BurpGlobals*); const UCHAR* MVOL_write_block(BurpGlobals*, const UCHAR*, ULONG); +Firebird::ICryptKeyCallback* MVOL_get_crypt(BurpGlobals*); #if defined WIN_NT -DESC MVOL_open(const char*, ULONG, ULONG); +DESC NT_tape_open(const char*, ULONG, ULONG); #endif diff --git a/src/burp/restore.epp b/src/burp/restore.epp index 263b1a9c12..d90ccbc8b7 100644 --- a/src/burp/restore.epp +++ b/src/burp/restore.epp @@ -60,6 +60,7 @@ #include "../burp/OdsDetection.h" #include "../auth/trusted/AuthSspi.h" #include "../common/dsc_proto.h" +#include "../common/ThreadStart.h" using MsgFormat::SafeArg; using Firebird::FbLocalStatus; @@ -104,7 +105,7 @@ enum scan_attr_t void add_access_dpb(BurpGlobals* tdgbl, Firebird::ClumpletWriter& dpb); void add_files(BurpGlobals* tdgbl, const char*); void bad_attribute(scan_attr_t, att_type, USHORT); -void create_database(BurpGlobals* tdgbl, const TEXT*); +void create_database(BurpGlobals* tdgbl, Firebird::IProvider*, const TEXT*); void decompress(BurpGlobals* tdgbl, UCHAR*, ULONG); void eat_blob(BurpGlobals* tdgbl); void eat_text(BurpGlobals* tdgbl); @@ -163,7 +164,7 @@ void realign(BurpGlobals* tdgbl, UCHAR*, const burp_rel*); #ifdef sparc USHORT recompute_length(BurpGlobals* tdgbl, burp_rel*); #endif -bool restore(BurpGlobals* tdgbl, const TEXT*, const TEXT*); +bool restore(BurpGlobals* tdgbl, Firebird::IProvider*, const TEXT*, const TEXT*); void restore_security_class(BurpGlobals* tdgbl, const TEXT*, const TEXT*); USHORT get_view_base_relation_count(BurpGlobals* tdgbl, const TEXT*, USHORT, bool* error); void store_blr_gen_id(BurpGlobals* tdgbl, const TEXT* gen_name, SINT64 value, SINT64 initial_value, @@ -184,13 +185,9 @@ const SSHORT old_sparcs[] = {0, 0, 0, 2, 0, 0, 0, 0, 2, 4, 4, 4, 8, 8, 0, 0, 8, 8, 8}; #endif -//MVOL_read returns int -static inline int get(BurpGlobals* tdgbl) +static inline UCHAR get(BurpGlobals* tdgbl) { - if (--(tdgbl->io_cnt) >= 0) - return *(tdgbl->io_ptr)++; - - return MVOL_read(&tdgbl->io_cnt, &tdgbl->io_ptr); + return tdgbl->get(); } static inline FB_BOOLEAN get_boolean(BurpGlobals* tdgbl) @@ -283,22 +280,11 @@ int RESTORE_restore (const TEXT* file_name, const TEXT* database_name) Firebird::IRequest* req_handle5 = nullptr; BASED_ON RDB$INDICES.RDB$INDEX_NAME index_name; + Firebird::DispatcherPtr provider; BurpGlobals* tdgbl = BurpGlobals::getSpecific(); - - tdgbl->io_ptr = NULL; - tdgbl->io_cnt = 0; - - tdgbl->relations = NULL; - tdgbl->packages = NULL; - tdgbl->procedures = NULL; - tdgbl->miss_privs = NULL; - tdgbl->RESTORE_format = 0; - tdgbl->runtimeODS = 0; - tdgbl->global_trans = 0; - tdgbl->gbl_sw_transportable = tdgbl->gbl_sw_compress = false; - if (!restore(tdgbl, file_name, database_name)) + if (!restore(tdgbl, provider, file_name, database_name)) return FINI_ERROR; BURP_verbose (76); @@ -580,7 +566,7 @@ int RESTORE_restore (const TEXT* file_name, const TEXT* database_name) // set forced writes to the value which was in the header dpb.insertByte(isc_dpb_force_write, tdgbl->hdr_forced_writes ? 1 : 0); - Firebird::IAttachment* db_handle = Firebird::DispatcherPtr()->attachDatabase(&tdgbl->status_vector, database_name, + Firebird::IAttachment* db_handle = provider->attachDatabase(&tdgbl->status_vector, database_name, dpb.getBufferLength(), dpb.getBuffer()); if (tdgbl->status_vector->hasData()) general_on_error(); @@ -608,7 +594,7 @@ int RESTORE_restore (const TEXT* file_name, const TEXT* database_name) dpb.insertByte(isc_dpb_set_db_readonly, 1); - db_handle = Firebird::DispatcherPtr()->attachDatabase(&tdgbl->status_vector, database_name, + db_handle = provider->attachDatabase(&tdgbl->status_vector, database_name, dpb.getBufferLength(), dpb.getBuffer()); if (tdgbl->status_vector->hasData()) general_on_error(); @@ -807,9 +793,32 @@ private: unsigned* version; }; +class EngineVersion : + public Firebird::AutoIface > +{ +public: + EngineVersion(char* v) + : version(v) + { + version[0] = 0; + } + + // IVersionCallback implementation + void callback(Firebird::CheckStatusWrapper*, const char* text) + { + if (!version[0]) + strcpy(version, text); + } + +private: + char* version; +}; + } // anonymous namespace -void create_database(BurpGlobals* tdgbl, const TEXT* file_name) + + +void create_database(BurpGlobals* tdgbl, Firebird::IProvider* provider, const TEXT* file_name) { /************************************** * @@ -836,6 +845,8 @@ void create_database(BurpGlobals* tdgbl, const TEXT* file_name) ULONG page_buffers = 0; USHORT SQL_dialect = 0; + tdgbl->gbl_database_file_name = file_name; + att_type attribute; rec_type record; if (get_record(&record, tdgbl) == rec_physical_db) @@ -974,7 +985,9 @@ void create_database(BurpGlobals* tdgbl, const TEXT* file_name) // start database up shut down, // use single-user mode to avoid conflicts during restore process - dpb.insertByte(isc_dpb_shutdown, isc_dpb_shut_attachment | isc_dpb_shut_single); + // when crypt thread to run use multi-DBO mode + dpb.insertByte(isc_dpb_shutdown, isc_dpb_shut_attachment | + (tdgbl->gbl_sw_keyholder ? isc_dpb_shut_multi : isc_dpb_shut_single)); dpb.insertInt(isc_dpb_shutdown_delay, 0); dpb.insertInt(isc_dpb_overwrite, tdgbl->gbl_sw_overwrite); @@ -988,8 +1001,19 @@ void create_database(BurpGlobals* tdgbl, const TEXT* file_name) fb_strlen(tdgbl->gbl_sw_fix_fss_metadata)); } - DB = Firebird::DispatcherPtr()->createDatabase(&status_vector, file_name, - dpb.getBufferLength(), dpb.getBuffer()); + // provide crypt key(s) for engine + + if (tdgbl->gbl_sw_keyholder) + { + provider->setDbCryptCallback(&status_vector, MVOL_get_crypt(tdgbl)); + if (!status_vector.isSuccess()) + { + BURP_print_status(true, &status_vector); + BURP_exit_local(FINI_ERROR, tdgbl); + } + } + + DB = provider->createDatabase(&status_vector, file_name, dpb.getBufferLength(), dpb.getBuffer()); if (status_vector->hasData()) { BURP_error_redirect(&status_vector, 33, SafeArg() << file_name); @@ -1017,6 +1041,100 @@ void create_database(BurpGlobals* tdgbl, const TEXT* file_name) BURP_verbose (74, SafeArg() << file_name << page_size); // msg 74 created database %s, page_size %ld bytes + + if (tdgbl->gbl_sw_keyholder) + { + // check server version + char buf[256]; + EngineVersion ev(buf); + Firebird::UtilInterfacePtr()->getFbVersion(&status_vector, DB, &ev); + + const char* ptr = strstr(buf, "version \""); + int v = 0; + int c = 1; + if (ptr) + { + while(ptr && *ptr) + { + if (*ptr >='0' && *ptr <= '9') + { + v *= 100; + v += atoi(ptr); + if (c >= 3) + { + if (v < 30004) + ptr = NULL; + break; + } + ++c; + ptr = strchr(ptr, '.'); + } + else + ++ptr; + } + } + if (!(ptr && *ptr)) + { + Firebird::string x; + x.printf("Undefined or too small server version: %s, need at least 3.0.4", buf); + (Firebird::Arg::Gds(isc_random) << x).raise(); + } + + EXEC SQL SET TRANSACTION; + if (gds_status->hasData()) + general_on_error (); + + if (!tdgbl->gbl_sw_crypt) + { + BURP_error(true, 378); + // Unknown crypt plugin name - use -CRYPT switch + } + + Firebird::string sql; + sql.printf("ALTER DATABASE ENCRYPT WITH \"%s\"", tdgbl->gbl_sw_crypt); + if (tdgbl->gbl_sw_keyname) + { + sql += " KEY "; + sql += tdgbl->gbl_sw_keyname; + } + tdgbl->db_handle->execute(gds_status, tdgbl->tr_handle, sql.length(), sql.c_str(), 3, + nullptr, nullptr, nullptr, nullptr); + if (gds_status->hasData()) + general_on_error (); + + COMMIT + ON_ERROR + general_on_error (); + END_ERROR; + + UCHAR buffer[100]; + UCHAR item = fb_info_crypt_state; + bool complete = false; + for (int i = 0; i < 100; ++i) + { + Thread::sleep(100); + + tdgbl->db_handle->getInfo(gds_status, sizeof(item), &item, sizeof(buffer), buffer); + if (gds_status->hasData()) + general_on_error (); + + const UCHAR* d = buffer; + if (*d++ != fb_info_crypt_state) + BURP_error(385, true); + + const int length = gds__vax_integer(d, 2); + d += 2; + const int value = gds__vax_integer(d, length); + if (value & fb_info_crypt_encrypted && !(value & fb_info_crypt_process)) + { + complete = true; + break; + } + } + + if (!complete) + BURP_error(386, true); + } } void decompress(BurpGlobals* tdgbl, UCHAR* buffer, ULONG length) @@ -10348,7 +10466,7 @@ USHORT recompute_length(BurpGlobals* tdgbl, burp_rel* relation) } #endif -bool restore(BurpGlobals* tdgbl, const TEXT* file_name, const TEXT* database_name) +bool restore(BurpGlobals* tdgbl, Firebird::IProvider* provider, const TEXT* file_name, const TEXT* database_name) { /************************************** * @@ -10363,7 +10481,7 @@ bool restore(BurpGlobals* tdgbl, const TEXT* file_name, const TEXT* database_nam // Read burp record first - MVOL_init_read (file_name, &tdgbl->RESTORE_format, &tdgbl->io_cnt, &tdgbl->io_ptr); + MVOL_init_read (file_name, &tdgbl->RESTORE_format); if (tdgbl->gbl_sw_transportable) BURP_verbose (133); @@ -10383,7 +10501,7 @@ bool restore(BurpGlobals* tdgbl, const TEXT* file_name, const TEXT* database_nam BURP_verbose(349, SafeArg() << tdgbl->RESTORE_format); // backup version is @1 - create_database(tdgbl, database_name); + create_database(tdgbl, provider, database_name); EXEC SQL SET TRANSACTION NO_AUTO_UNDO; if (gds_status->hasData()) diff --git a/src/common/UtilSvc.cpp b/src/common/UtilSvc.cpp index 53fdf26a29..a916a2164a 100644 --- a/src/common/UtilSvc.cpp +++ b/src/common/UtilSvc.cpp @@ -145,6 +145,7 @@ public: virtual bool finished() { return false; } virtual void initStatus() { } virtual bool utf8FileNames() { return false; } + virtual Firebird::ICryptKeyCallback* getCryptCallback() { return NULL; } }; diff --git a/src/common/UtilSvc.h b/src/common/UtilSvc.h index f7cb602da9..72f607fe4a 100644 --- a/src/common/UtilSvc.h +++ b/src/common/UtilSvc.h @@ -30,6 +30,8 @@ #ifndef FB_UTILFACE #define FB_UTILFACE +#include "firebird/Interface.h" + #include "../common/classes/alloc.h" #include "../common/classes/array.h" #include "../common/classes/fb_string.h" @@ -74,6 +76,7 @@ public: virtual bool finished() = 0; virtual unsigned int getAuthBlock(const unsigned char** bytes) = 0; virtual bool utf8FileNames() = 0; + virtual Firebird::ICryptKeyCallback* getCryptCallback() = 0; void setDataMode(bool value) { diff --git a/src/common/classes/ClumpletReader.cpp b/src/common/classes/ClumpletReader.cpp index fc8ac78077..0db91732e0 100644 --- a/src/common/classes/ClumpletReader.cpp +++ b/src/common/classes/ClumpletReader.cpp @@ -334,7 +334,9 @@ ClumpletReader::ClumpletType ClumpletReader::getClumpletType(UCHAR tag) const case isc_spb_res_fix_fss_metadata: case isc_spb_bkp_stat: case isc_spb_bkp_skip_data: - //case isc_spb_res_skip_data: // same value + case isc_spb_bkp_keyholder: + case isc_spb_bkp_keyname: + case isc_spb_bkp_crypt: return StringSpb; case isc_spb_bkp_factor: case isc_spb_bkp_length: diff --git a/src/common/classes/GetPlugins.h b/src/common/classes/GetPlugins.h index 096d148fbc..8ee4333561 100644 --- a/src/common/classes/GetPlugins.h +++ b/src/common/classes/GetPlugins.h @@ -30,6 +30,7 @@ #define FB_COMMON_CLASSES_GET_PLUGINS #include "../common/classes/ImplementHelper.h" +#include "../common/classes/auto.h" #include "../common/config/config.h" #include "../common/StatusHolder.h" diff --git a/src/common/classes/RefCounted.h b/src/common/classes/RefCounted.h index 1766a6190b..d8a6027c47 100644 --- a/src/common/classes/RefCounted.h +++ b/src/common/classes/RefCounted.h @@ -130,6 +130,13 @@ namespace Firebird return ptr; } + void moveFrom(RefPtr& r) + { + assign(NULL); + ptr = r.ptr; + r.ptr = NULL; + } + T* operator=(T* p) { return assign(p); diff --git a/src/include/consts_pub.h b/src/include/consts_pub.h index ef478c54de..3e75cb9636 100644 --- a/src/include/consts_pub.h +++ b/src/include/consts_pub.h @@ -393,6 +393,9 @@ #define isc_spb_bkp_length 7 #define isc_spb_bkp_skip_data 8 #define isc_spb_bkp_stat 15 +#define isc_spb_bkp_keyholder 16 +#define isc_spb_bkp_keyname 17 +#define isc_spb_bkp_crypt 18 #define isc_spb_bkp_ignore_checksums 0x01 #define isc_spb_bkp_ignore_limbo 0x02 #define isc_spb_bkp_metadata_only 0x04 @@ -402,6 +405,7 @@ #define isc_spb_bkp_convert 0x40 #define isc_spb_bkp_expand 0x80 #define isc_spb_bkp_no_triggers 0x8000 +#define isc_spb_bkp_zip 0x010000 /******************************************** * Parameters for isc_action_svc_properties * @@ -505,6 +509,9 @@ #define isc_spb_res_access_mode 12 #define isc_spb_res_fix_fss_data 13 #define isc_spb_res_fix_fss_metadata 14 +#define isc_spb_res_keyholder isc_spb_bkp_keyholder +#define isc_spb_res_keyname isc_spb_bkp_keyname +#define isc_spb_res_crypt isc_spb_bkp_crypt #define isc_spb_res_stat isc_spb_bkp_stat #define isc_spb_res_metadata_only isc_spb_bkp_metadata_only #define isc_spb_res_deactivate_idx 0x0100 diff --git a/src/include/gen/codetext.h b/src/include/gen/codetext.h index ba29c37c76..2f2676e8a5 100644 --- a/src/include/gen/codetext.h +++ b/src/include/gen/codetext.h @@ -906,6 +906,7 @@ static const struct { {"hdr_overflow", 335545202}, {"vld_plugins", 335545203}, {"db_crypt_key", 335545204}, + {"no_keyholder_plugin", 335545205}, {"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 d66d0fed4b..aa96cc3856 100644 --- a/src/include/gen/iberror.h +++ b/src/include/gen/iberror.h @@ -940,6 +940,7 @@ const ISC_STATUS isc_map_overflow = 335545201L; const ISC_STATUS isc_hdr_overflow = 335545202L; const ISC_STATUS isc_vld_plugins = 335545203L; const ISC_STATUS isc_db_crypt_key = 335545204L; +const ISC_STATUS isc_no_keyholder_plugin = 335545205L; const ISC_STATUS isc_gfix_db_name = 335740929L; const ISC_STATUS isc_gfix_invalid_sw = 335740930L; const ISC_STATUS isc_gfix_incmp_sw = 335740932L; @@ -1414,7 +1415,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 = 1358; +const ISC_STATUS isc_err_max = 1359; #else /* c definitions */ @@ -2324,6 +2325,7 @@ const ISC_STATUS isc_err_max = 1358; #define isc_hdr_overflow 335545202L #define isc_vld_plugins 335545203L #define isc_db_crypt_key 335545204L +#define isc_no_keyholder_plugin 335545205L #define isc_gfix_db_name 335740929L #define isc_gfix_invalid_sw 335740930L #define isc_gfix_incmp_sw 335740932L @@ -2798,7 +2800,7 @@ const ISC_STATUS isc_err_max = 1358; #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 1358 +#define isc_err_max 1359 #endif diff --git a/src/include/gen/msgs.h b/src/include/gen/msgs.h index eef9a446f5..e13f766493 100644 --- a/src/include/gen/msgs.h +++ b/src/include/gen/msgs.h @@ -909,6 +909,7 @@ Data source : @4"}, /* eds_statement */ {335545202, "Header page overflow - too many clumplets on it"}, /* hdr_overflow */ {335545203, "No matching client/server authentication plugins configured for execute statement in embedded datasource"}, /* vld_plugins */ {335545204, "Missing database encryption key for your attachment"}, /* db_crypt_key */ + {335545205, "Key holder plugin @1 failed to load"}, /* no_keyholder_plugin */ {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 ad9b3d6079..d89d7d6308 100644 --- a/src/include/gen/sql_code.h +++ b/src/include/gen/sql_code.h @@ -905,6 +905,7 @@ static const struct { {335545202, -901}, /* 882 hdr_overflow */ {335545203, -901}, /* 883 vld_plugins */ {335545204, -902}, /* 884 db_crypt_key */ + {335545205, -104}, /* 885 no_keyholder_plugin */ {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 28af9557c8..1cf4a3146e 100644 --- a/src/include/gen/sql_state.h +++ b/src/include/gen/sql_state.h @@ -905,6 +905,7 @@ static const struct { {335545202, "54000"}, // 882 hdr_overflow {335545203, "28000"}, // 883 vld_plugins {335545204, "08004"}, // 884 db_crypt_key + {335545205, "HY024"}, // 885 no_keyholder_plugin {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 061526917a..2add3bc566 100644 --- a/src/jrd/CryptoManager.cpp +++ b/src/jrd/CryptoManager.cpp @@ -265,6 +265,7 @@ namespace Jrd { : PermanentStorage(*tdbb->getDatabase()->dbb_permanent), sync(this), keyName(getPool()), + pluginName(getPool()), keyProviders(getPool()), keyConsumers(getPool()), hash(getPool()), @@ -366,6 +367,7 @@ namespace Jrd { keyName = ""; loadPlugin(tdbb, hdr->hdr_crypt_plugin); + pluginName = hdr->hdr_crypt_plugin; string valid; calcValidation(valid, cryptPlugin); @@ -395,7 +397,7 @@ namespace Jrd { } } - void CryptoManager::loadPlugin(thread_db* tdbb, const char* pluginName) + void CryptoManager::loadPlugin(thread_db* tdbb, const char* plugName) { if (cryptPlugin) { @@ -408,10 +410,10 @@ namespace Jrd { return; } - AutoPtr cryptControl(FB_NEW Factory(IPluginManager::TYPE_DB_CRYPT, dbb.dbb_config, pluginName)); + AutoPtr cryptControl(FB_NEW Factory(IPluginManager::TYPE_DB_CRYPT, dbb.dbb_config, plugName)); if (!cryptControl->hasData()) { - (Arg::Gds(isc_no_crypt_plugin) << pluginName).raise(); + (Arg::Gds(isc_no_crypt_plugin) << plugName).raise(); } // do not assign cryptPlugin directly before key init complete @@ -461,6 +463,7 @@ namespace Jrd { cryptPlugin = p; cryptPlugin->addRef(); + pluginName = plugName; // remove old factory if present delete checkFactory; @@ -1326,6 +1329,11 @@ namespace Jrd { return keyName.c_str(); } + const char* CryptoManager::getPluginName() const + { + return pluginName.c_str(); + } + void CryptoManager::addClumplet(string& signature, ClumpletReader& block, UCHAR tag) { if (block.find(tag)) diff --git a/src/jrd/CryptoManager.h b/src/jrd/CryptoManager.h index 143f2ca98f..883e99db79 100644 --- a/src/jrd/CryptoManager.h +++ b/src/jrd/CryptoManager.h @@ -304,6 +304,7 @@ public: ULONG getCurrentPage() const; UCHAR getCurrentState() const; const char* getKeyName() const; + const char* getPluginName() const; private: enum IoResult {SUCCESS_ALL, FAILED_CRYPT, FAILED_IO}; @@ -383,7 +384,7 @@ private: void checkDigitalSignature(thread_db* tdbb, const class Header& hdr); BarSync sync; - Firebird::MetaName keyName; + Firebird::MetaName keyName, pluginName; ULONG currentPage; Firebird::Mutex pluginLoadMtx, cryptThreadMtx, holdersMutex; AttVector keyProviders, keyConsumers; diff --git a/src/jrd/SystemPrivileges.h b/src/jrd/SystemPrivileges.h index a81ac52653..79c0c21a72 100644 --- a/src/jrd/SystemPrivileges.h +++ b/src/jrd/SystemPrivileges.h @@ -62,7 +62,7 @@ SYSTEM_PRIVILEGE(USE_GRANTED_BY_CLAUSE) SYSTEM_PRIVILEGE(GRANT_REVOKE_ON_ANY_OBJECT) SYSTEM_PRIVILEGE(GRANT_REVOKE_ANY_DDL_RIGHT) SYSTEM_PRIVILEGE(CREATE_PRIVILEGED_ROLES) -SYSTEM_PRIVILEGE(GET_DBCRYPT_KEY_NAME) +SYSTEM_PRIVILEGE(GET_DBCRYPT_INFO) #ifdef FB_JRD_SYSTEM_PRIVILEGES_TMP maxSystemPrivilege diff --git a/src/jrd/inf.cpp b/src/jrd/inf.cpp index 9b32e50917..fa1bbca8d8 100644 --- a/src/jrd/inf.cpp +++ b/src/jrd/inf.cpp @@ -770,7 +770,7 @@ void INF_database_info(thread_db* tdbb, break; case fb_info_crypt_key: - if (tdbb->getAttachment()->locksmith(tdbb, GET_DBCRYPT_KEY_NAME)) + if (tdbb->getAttachment()->locksmith(tdbb, GET_DBCRYPT_INFO)) { const char* key = dbb->dbb_crypto_manager->getKeyName(); if (!(info = INF_put_item(item, static_cast(strlen(key)), key, info, end))) @@ -788,6 +788,25 @@ void INF_database_info(thread_db* tdbb, length = 1 + INF_convert(isc_adm_task_denied, buffer + 1); break; + case fb_info_crypt_plugin: + if (tdbb->getAttachment()->locksmith(tdbb, GET_DBCRYPT_INFO)) + { + const char* key = dbb->dbb_crypto_manager->getPluginName(); + if (!(info = INF_put_item(item, static_cast(strlen(key)), key, info, end))) + { + if (transaction) + TRA_commit(tdbb, transaction, false); + + return; + } + continue; + } + + buffer[0] = item; + item = isc_info_error; + length = 1 + INF_convert(isc_adm_task_denied, buffer + 1); + break; + case fb_info_conn_flags: length = INF_convert(tdbb->getAttachment()->att_remote_flags, buffer); break; diff --git a/src/jrd/inf_pub.h b/src/jrd/inf_pub.h index df61e9c498..432302eac0 100644 --- a/src/jrd/inf_pub.h +++ b/src/jrd/inf_pub.h @@ -148,7 +148,6 @@ enum db_info_types fb_info_ses_idle_timeout_run = 131, fb_info_conn_flags = 132, - fb_info_protocol_version = 133, fb_info_crypt_key = 133, fb_info_crypt_state = 134, @@ -156,6 +155,9 @@ enum db_info_types fb_info_statement_timeout_db = 135, fb_info_statement_timeout_att = 136, + fb_info_protocol_version = 137, + fb_info_crypt_plugin = 138, + isc_info_db_last_value /* Leave this LAST! */ }; diff --git a/src/jrd/svc.cpp b/src/jrd/svc.cpp index e721cf9f19..f05b53096c 100644 --- a/src/jrd/svc.cpp +++ b/src/jrd/svc.cpp @@ -647,6 +647,11 @@ bool Service::utf8FileNames() return svc_utf8; } +Firebird::ICryptKeyCallback* Service::getCryptCallback() +{ + return svc_crypt_callback; +} + void Service::need_admin_privs(Arg::StatusVector& status, const char* message) { status << Arg::Gds(isc_insufficient_svc_privileges) << Arg::Str(message); @@ -2933,6 +2938,9 @@ bool Service::process_switches(ClumpletReader& spb, string& switches) case isc_spb_res_fix_fss_metadata: case isc_spb_bkp_stat: case isc_spb_bkp_skip_data: + case isc_spb_bkp_keyholder: + case isc_spb_bkp_keyname: + case isc_spb_bkp_crypt: if (!get_action_svc_parameter(spb.getClumpTag(), reference_burp_in_sw_table, switches)) { return false; diff --git a/src/jrd/svc.h b/src/jrd/svc.h index b58ad10d5a..3cb12fc705 100644 --- a/src/jrd/svc.h +++ b/src/jrd/svc.h @@ -141,6 +141,8 @@ public: // utilities interface with service virtual void fillDpb(Firebird::ClumpletWriter& dpb); // encoding for string parameters passed to utility virtual bool utf8FileNames(); + // get database encryption key transfer callback routine + virtual Firebird::ICryptKeyCallback* getCryptCallback(); virtual TraceManager* getTraceManager() { @@ -196,7 +198,6 @@ public: // external interface with service const Firebird::string& getRemoteProcess() const { return svc_remote_process; } int getRemotePID() const { return svc_remote_pid; } const Firebird::PathName& getExpectedDb() const { return svc_expected_db; } - Firebird::ICryptKeyCallback* getCryptCallback() { return svc_crypt_callback; } private: // Service must have private destructor, called from finish diff --git a/src/msgs/facilities2.sql b/src/msgs/facilities2.sql index fff966edf2..0823a0eb54 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 (?, ?, ?, ?); -- -('2018-02-01 17:40:00', 'JRD', 0, 885) +('2018-05-05 16:40:00', 'JRD', 0, 886) ('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) @@ -9,7 +9,7 @@ set bulk_insert INSERT INTO FACILITIES (LAST_CHANGE, FACILITY, FAC_CODE, MAX_NUM ('2018-01-15 00:15:00', 'DYN', 8, 299) ('1996-11-07 13:39:40', 'INSTALL', 10, 1) ('1996-11-07 13:38:41', 'TEST', 11, 4) -('2018-02-19 19:40:00', 'GBAK', 12, 372) +('2018-04-26 20:40:00', 'GBAK', 12, 387) ('2015-08-05 12:40:00', 'SQLERR', 13, 1045) ('1996-11-07 13:38:42', 'SQLWARN', 14, 613) ('2018-02-27 14:50:31', 'JRD_BUGCHK', 15, 308) diff --git a/src/msgs/messages2.sql b/src/msgs/messages2.sql index 2e2a9ef282..72f6cef0ef 100644 --- a/src/msgs/messages2.sql +++ b/src/msgs/messages2.sql @@ -992,6 +992,7 @@ Data source : @4', NULL, NULL) ('hdr_overflow', NULL, 'CryptoManager.cpp', NULL, 0, 882, NULL, 'Header page overflow - too many clumplets on it', NULL, NULL); ('vld_plugins', NULL, 'ValidatePassword.cpp', NULL, 0, 883, NULL, 'No matching client/server authentication plugins configured for execute statement in embedded datasource', NULL, NULL); ('db_crypt_key', NULL, 'CryptoManager.cpp', NULL, 0, 884, NULL, 'Missing database encryption key for your attachment', NULL, NULL); +('no_keyholder_plugin', NULL, 'mvol.cpp', NULL, 0, 885, NULL, 'Key holder plugin @1 failed to load', 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); @@ -2464,6 +2465,21 @@ ERROR: Backup incomplete', NULL, NULL); (NULL, 'api_gbak/gbak', 'burp.cpp', NULL, 12, 369, NULL, 'total statistics', NULL, NULL); (NULL, 'get_blob', 'restore.epp', NULL, 12, 370, NULL, 'could not append BLOB data to batch', NULL, NULL); (NULL, 'get_data', 'restore.epp', NULL, 12, 371, NULL, 'could not start batch when restoring table @1, trying old way', NULL, NULL); +(NULL, 'burp_usage', 'burp.cpp', NULL, 12, 372, NULL, ' @1KEYNAME name of a key to be used for encryption', NULL, NULL); +(NULL, 'burp_usage', 'burp.cpp', NULL, 12, 373, NULL, ' @1CRYPT crypt plugin name', NULL, NULL); +(NULL, 'burp_usage', 'burp.cpp', NULL, 12, 374, NULL, ' @1ZIP backup file is in zip compressed format', NULL, NULL); +(NULL, 'gbak', 'burp.cpp', NULL, 12, 375, NULL, 'Keyname parameter missing', NULL, NULL); +(NULL, 'gbak', 'burp.cpp', NULL, 12, 376, NULL, 'Key holder parameter missing but backup file is encrypted', NULL, NULL); +(NULL, 'gbak', 'mvol.cpp', NULL, 12, 377, NULL, 'CryptPlugin parameter missing', NULL, NULL); +(NULL, 'gbak', 'burp.cpp', NULL, 12, 378, NULL, 'Unknown crypt plugin name - use -CRYPT switch', NULL, NULL); +(NULL, NULL, 'mvol.cpp', NULL, 12, 379, NULL, 'Inflate error @1', NULL, NULL); +(NULL, NULL, 'mvol.cpp', NULL, 12, 380, NULL, 'Deflate error @1', NULL, NULL); +(NULL, 'gbak', 'burp.cpp', NULL, 12, 381, NULL, 'Key holder parameter missing', NULL, NULL); +(NULL, 'burp_usage', 'burp.cpp', NULL, 12, 382, NULL, ' @1KEYHOLDER name of a key holder plugin', NULL, NULL); +(NULL, NULL, 'mvol.cpp', NULL, 12, 383, NULL, 'Decompression stream init error @1', NULL, NULL); +(NULL, NULL, 'mvol.cpp', NULL, 12, 384, NULL, 'Compression stream init error @1', NULL, NULL); +(NULL, NULL, 'restore.epp', NULL, 12, 385, NULL, 'Invalid reply from getInfo() when waiting for DB encryption', NULL, NULL); +(NULL, NULL, 'restore.epp', NULL, 12, 386, NULL, 'Problems with just created database encryption', NULL, NULL); -- SQLERR (NULL, NULL, NULL, NULL, 13, 1, NULL, 'Firebird error', NULL, NULL); (NULL, NULL, NULL, NULL, 13, 74, NULL, 'Rollback not performed', NULL, NULL); diff --git a/src/msgs/system_errors2.sql b/src/msgs/system_errors2.sql index a1b031d47a..d1e582768b 100644 --- a/src/msgs/system_errors2.sql +++ b/src/msgs/system_errors2.sql @@ -891,6 +891,7 @@ set bulk_insert INSERT INTO SYSTEM_ERRORS (SQL_CODE, SQL_CLASS, SQL_SUBCLASS, FA (-901, '54', '000', 0, 882, 'hdr_overflow', NULL, NULL) (-901, '28', '000', 0, 883, 'vld_plugins', NULL, NULL) (-902, '08', '004', 0, 884, 'db_crypt_key', NULL, NULL) +(-104, 'HY', '024', 0, 885, 'no_keyholder_plugin', 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/fbsvcmgr/fbsvcmgr.cpp b/src/utilities/fbsvcmgr/fbsvcmgr.cpp index 619d0becfc..e4c4214e98 100644 --- a/src/utilities/fbsvcmgr/fbsvcmgr.cpp +++ b/src/utilities/fbsvcmgr/fbsvcmgr.cpp @@ -41,7 +41,9 @@ #include "../common/classes/timestamp.h" #include "../common/utils_proto.h" #include "../common/classes/MsgPrint.h" +#include "../common/classes/GetPlugins.h" #include "../common/StatusArg.h" +#include "../common/status.h" #include "../common/os/os_utils.h" #include "../jrd/license.h" @@ -108,6 +110,32 @@ bool putStringArgument(char**& av, ClumpletWriter& spb, unsigned int tag) return true; } +// add callback to named KeyHolderPlugin +IKeyHolderPlugin* keyHolder = NULL; + +bool putCallback(char**& av, ClumpletWriter&, unsigned int) +{ + if (! *av) + return false; + + char* x = *av++; + GetPlugins keyControl(IPluginManager::TYPE_KEY_HOLDER, x); + if (!keyControl.hasData()) + (Firebird::Arg::Gds(isc_no_keyholder_plugin) << x).raise(); + keyHolder = keyControl.plugin(); + keyHolder->addRef(); // Leak memory, OK for utility + + FbLocalStatus st; + ICryptKeyCallback* cb = keyHolder->chainHandle(&st); + check(&st); + + ISC_STATUS_ARRAY status; + if (fb_database_crypt_callback(status, cb) != 0) + status_exception::raise(status); + + return true; +} + // add string tag from file (fetch password) bool putFileArgument(char**& av, ClumpletWriter& spb, unsigned int tag) @@ -341,6 +369,7 @@ const SvcSwitches attSwitch[] = {"fetch_password", putFileArgument, 0, isc_spb_password, 0}, {"trusted_auth", putSingleTag, 0, isc_spb_trusted_auth, 0}, {"expected_db", putStringArgument, 0, isc_spb_expected_db, 0}, + {"key_holder", putCallback, 0, 0, 0}, {0, 0, 0, 0, 0} }; @@ -376,6 +405,10 @@ const SvcSwitches backupOptions[] = {"verbint", putIntArgument, 0, isc_spb_verbint, 0}, {"bkp_skip_data", putStringArgument, 0, isc_spb_bkp_skip_data, 0}, {"bkp_stat", putStringArgument, 0, isc_spb_bkp_stat, 0 }, + {"bkp_keyholder", putStringArgument, 0, isc_spb_bkp_keyholder, 0 }, + {"bkp_keyname", putStringArgument, 0, isc_spb_bkp_keyname, 0 }, + {"bkp_crypt", putStringArgument, 0, isc_spb_bkp_crypt, 0 }, + {"bkp_zip", putOption, 0, isc_spb_bkp_zip, 0 }, {0, 0, 0, 0, 0} }; @@ -401,6 +434,9 @@ const SvcSwitches restoreOptions[] = {"verbint", putIntArgument, 0, isc_spb_verbint, 0}, {"res_skip_data", putStringArgument, 0, isc_spb_res_skip_data, 0}, {"res_stat", putStringArgument, 0, isc_spb_res_stat, 0 }, + {"res_keyholder", putStringArgument, 0, isc_spb_res_keyholder, 0 }, + {"res_keyname", putStringArgument, 0, isc_spb_res_keyname, 0 }, + {"res_crypt", putStringArgument, 0, isc_spb_res_crypt, 0 }, {0, 0, 0, 0, 0} }; @@ -961,6 +997,7 @@ struct TypeText { putBigIntArgument, "int64 value", "456" }, { putOption, NULL, "" }, { putSingleTag, NULL, "" }, + { putCallback, "key holder plugin name", NULL }, { NULL, NULL , NULL } }; @@ -1003,6 +1040,9 @@ void testSvc(isc_svc_handle* h, ClumpletWriter& spb, const SvcSwitches* sw) { if (sw->populate == tt->populate) { + if (!tt->testArg) + break; + // some tricks to emulate 'char* argv[]' char x[100]; strcpy(x, tt->testArg);