2012-05-31 18:53:42 +02:00
|
|
|
/*
|
|
|
|
* PROGRAM: JRD access method
|
|
|
|
* MODULE: CryptoManager.cpp
|
|
|
|
* DESCRIPTION: Database encryption
|
|
|
|
*
|
|
|
|
* The contents of this file are subject to the Initial
|
|
|
|
* Developer's Public License Version 1.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the
|
|
|
|
* License. You may obtain a copy of the License at
|
|
|
|
* http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl.
|
|
|
|
*
|
|
|
|
* Software distributed under the License is distributed AS IS,
|
|
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing rights
|
|
|
|
* and limitations under the License.
|
|
|
|
*
|
|
|
|
* The Original Code was created by Alex Peshkov
|
|
|
|
* for the Firebird Open Source RDBMS project.
|
|
|
|
*
|
2012-06-03 05:00:24 +02:00
|
|
|
* Copyright (c) 2012 Alex Peshkov <peshkoff at mail.ru>
|
2012-05-31 18:53:42 +02:00
|
|
|
* and all contributors signed below.
|
|
|
|
*
|
|
|
|
* All Rights Reserved.
|
|
|
|
* Contributor(s): ______________________________________.
|
|
|
|
*
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "firebird.h"
|
2014-09-29 13:03:47 +02:00
|
|
|
#include "firebird/Interface.h"
|
2012-05-31 18:53:42 +02:00
|
|
|
#include "gen/iberror.h"
|
|
|
|
#include "../jrd/CryptoManager.h"
|
|
|
|
|
|
|
|
#include "../common/classes/alloc.h"
|
|
|
|
#include "../jrd/Database.h"
|
|
|
|
#include "../common/ThreadStart.h"
|
|
|
|
#include "../common/StatusArg.h"
|
|
|
|
#include "../common/StatusHolder.h"
|
|
|
|
#include "../jrd/lck.h"
|
|
|
|
#include "../jrd/jrd.h"
|
|
|
|
#include "../jrd/pag.h"
|
|
|
|
#include "../jrd/nbak.h"
|
|
|
|
#include "../jrd/cch_proto.h"
|
|
|
|
#include "../jrd/lck_proto.h"
|
|
|
|
#include "../jrd/pag_proto.h"
|
|
|
|
#include "../common/isc_proto.h"
|
|
|
|
#include "../common/classes/GetPlugins.h"
|
2013-09-09 12:32:18 +02:00
|
|
|
#include "../common/classes/RefMutex.h"
|
2012-05-31 18:53:42 +02:00
|
|
|
|
|
|
|
using namespace Firebird;
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
THREAD_ENTRY_DECLARE cryptThreadStatic(THREAD_ENTRY_PARAM p)
|
|
|
|
{
|
2012-06-03 05:00:24 +02:00
|
|
|
Jrd::CryptoManager* cryptoManager = (Jrd::CryptoManager*) p;
|
2012-05-31 18:53:42 +02:00
|
|
|
cryptoManager->cryptThread();
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
class Header
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
Header(Jrd::thread_db* p_tdbb, USHORT lockType)
|
|
|
|
: tdbb(p_tdbb),
|
|
|
|
window(Jrd::HEADER_PAGE_NUMBER),
|
|
|
|
header((Ods::header_page*) CCH_FETCH(tdbb, &window, lockType, pag_header))
|
|
|
|
{
|
|
|
|
if (!header)
|
|
|
|
{
|
2012-06-29 14:44:41 +02:00
|
|
|
ERR_punt();
|
2012-05-31 18:53:42 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ods::header_page* write()
|
|
|
|
{
|
|
|
|
CCH_MARK_MUST_WRITE(tdbb, &window);
|
|
|
|
return header;
|
|
|
|
}
|
|
|
|
|
|
|
|
void depends(Stack<ULONG>& pages)
|
|
|
|
{
|
2012-06-03 05:00:24 +02:00
|
|
|
while (pages.hasData())
|
2012-05-31 18:53:42 +02:00
|
|
|
{
|
|
|
|
CCH_precedence(tdbb, &window, pages.pop());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const Ods::header_page* operator->() const
|
|
|
|
{
|
|
|
|
return header;
|
|
|
|
}
|
|
|
|
|
|
|
|
operator const Ods::header_page*() const
|
|
|
|
{
|
|
|
|
return header;
|
|
|
|
}
|
|
|
|
|
|
|
|
~Header()
|
|
|
|
{
|
|
|
|
CCH_RELEASE(tdbb, &window);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
Jrd::thread_db* tdbb;
|
|
|
|
Jrd::WIN window;
|
|
|
|
Ods::header_page* header;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
namespace Jrd {
|
|
|
|
|
|
|
|
CryptoManager::CryptoManager(thread_db* tdbb)
|
|
|
|
: PermanentStorage(*tdbb->getDatabase()->dbb_permanent),
|
|
|
|
keyHolderPlugins(getPool()),
|
|
|
|
cryptThreadId(0),
|
|
|
|
cryptPlugin(NULL),
|
|
|
|
dbb(*tdbb->getDatabase()),
|
|
|
|
needLock(true),
|
|
|
|
crypt(false),
|
|
|
|
process(false),
|
|
|
|
down(false)
|
|
|
|
{
|
|
|
|
stateLock = FB_NEW_RPT(getPool(), 0)
|
2012-06-21 17:37:38 +02:00
|
|
|
Lock(tdbb, 0, LCK_crypt_status, this, blockingAstChangeCryptState);
|
|
|
|
threadLock = FB_NEW_RPT(getPool(), 0) Lock(tdbb, 0, LCK_crypt);
|
2012-05-31 18:53:42 +02:00
|
|
|
|
|
|
|
takeStateLock(tdbb);
|
|
|
|
}
|
|
|
|
|
|
|
|
CryptoManager::~CryptoManager()
|
|
|
|
{
|
2013-11-13 08:44:50 +01:00
|
|
|
delete stateLock;
|
|
|
|
delete threadLock;
|
2012-05-31 18:53:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void CryptoManager::terminateCryptThread(thread_db*)
|
|
|
|
{
|
|
|
|
if (cryptThreadId)
|
|
|
|
{
|
|
|
|
down = true;
|
|
|
|
Thread::waitForCompletion(cryptThreadId);
|
|
|
|
cryptThreadId = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CryptoManager::shutdown(thread_db* tdbb)
|
|
|
|
{
|
|
|
|
terminateCryptThread(tdbb);
|
|
|
|
|
|
|
|
if (cryptPlugin)
|
|
|
|
{
|
|
|
|
PluginManagerInterfacePtr()->releasePlugin(cryptPlugin);
|
|
|
|
cryptPlugin = NULL;
|
|
|
|
}
|
|
|
|
|
2013-11-13 08:44:50 +01:00
|
|
|
LCK_release(tdbb, stateLock);
|
2012-05-31 18:53:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void CryptoManager::takeStateLock(thread_db* tdbb)
|
|
|
|
{
|
|
|
|
fb_assert(stateLock);
|
|
|
|
fb_assert(tdbb->getAttachment());
|
|
|
|
|
|
|
|
if (needLock)
|
|
|
|
{
|
|
|
|
if (!LCK_lock(tdbb, stateLock, LCK_SR, LCK_WAIT))
|
|
|
|
{
|
2015-03-28 01:36:04 +01:00
|
|
|
fb_assert(tdbb->tdbb_status_vector->getState() & IStatus::STATE_ERRORS);
|
2012-05-31 18:53:42 +02:00
|
|
|
ERR_punt();
|
|
|
|
}
|
|
|
|
|
2015-03-20 19:02:30 +01:00
|
|
|
tdbb->tdbb_status_vector->init();
|
2012-05-31 18:53:42 +02:00
|
|
|
needLock = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CryptoManager::loadPlugin(const char* pluginName)
|
|
|
|
{
|
2012-12-14 18:59:02 +01:00
|
|
|
MutexLockGuard guard(pluginLoadMtx, FB_FUNCTION);
|
2012-05-31 18:53:42 +02:00
|
|
|
|
|
|
|
if (cryptPlugin)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-02-18 16:01:17 +01:00
|
|
|
GetPlugins<IDbCryptPlugin> cryptControl(IPluginManager::TYPE_DB_CRYPT, dbb.dbb_config, pluginName);
|
2012-05-31 18:53:42 +02:00
|
|
|
if (!cryptControl.hasData())
|
|
|
|
{
|
2012-06-29 14:44:41 +02:00
|
|
|
(Arg::Gds(isc_no_crypt_plugin) << pluginName).raise();
|
2012-05-31 18:53:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// do not assign cryptPlugin directly before key init complete
|
|
|
|
IDbCryptPlugin* p = cryptControl.plugin();
|
|
|
|
keyHolderPlugins.init(p);
|
|
|
|
cryptPlugin = p;
|
|
|
|
cryptPlugin->addRef();
|
|
|
|
}
|
|
|
|
|
2015-12-09 15:22:13 +01:00
|
|
|
void CryptoManager::prepareChangeCryptState(thread_db* tdbb, const Firebird::MetaName& plugName)
|
2012-05-31 18:53:42 +02:00
|
|
|
{
|
|
|
|
if (plugName.length() > 31)
|
|
|
|
{
|
2012-06-29 14:44:41 +02:00
|
|
|
(Arg::Gds(isc_cp_name_too_long) << Arg::Num(31)).raise();
|
2012-05-31 18:53:42 +02:00
|
|
|
}
|
|
|
|
|
2015-12-09 15:22:13 +01:00
|
|
|
const bool newCryptState = plugName.hasData();
|
|
|
|
{ // window scope
|
|
|
|
Header hdr(tdbb, LCK_read);
|
|
|
|
|
|
|
|
// Check header page for flags
|
|
|
|
if (hdr->hdr_flags & Ods::hdr_crypt_process)
|
|
|
|
{
|
|
|
|
(Arg::Gds(isc_cp_process_active)).raise();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool headerCryptState = hdr->hdr_flags & Ods::hdr_encrypted;
|
|
|
|
if (headerCryptState == newCryptState)
|
|
|
|
{
|
|
|
|
(Arg::Gds(isc_cp_already_crypted)).raise();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Load plugin
|
|
|
|
if (newCryptState)
|
|
|
|
{
|
|
|
|
if (cryptPlugin)
|
|
|
|
{
|
|
|
|
// Unload old plugin
|
|
|
|
PluginManagerInterfacePtr()->releasePlugin(cryptPlugin);
|
|
|
|
cryptPlugin = NULL;
|
|
|
|
}
|
|
|
|
loadPlugin(plugName.c_str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CryptoManager::changeCryptState(thread_db* tdbb, const Firebird::string& plugName)
|
|
|
|
{
|
2014-03-26 20:10:08 +01:00
|
|
|
const bool newCryptState = plugName.hasData();
|
2012-06-03 05:00:24 +02:00
|
|
|
|
2012-05-31 18:53:42 +02:00
|
|
|
{ // window scope
|
|
|
|
Header hdr(tdbb, LCK_write);
|
|
|
|
|
|
|
|
// Check header page for flags
|
|
|
|
if (hdr->hdr_flags & Ods::hdr_crypt_process)
|
|
|
|
{
|
2012-06-29 14:44:41 +02:00
|
|
|
(Arg::Gds(isc_cp_process_active)).raise();
|
2012-05-31 18:53:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool headerCryptState = hdr->hdr_flags & Ods::hdr_encrypted;
|
|
|
|
if (headerCryptState == newCryptState)
|
|
|
|
{
|
2012-06-29 14:44:41 +02:00
|
|
|
(Arg::Gds(isc_cp_already_crypted)).raise();
|
2012-05-31 18:53:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
fb_assert(stateLock);
|
|
|
|
// Take exclusive stateLock
|
|
|
|
bool ret = needLock ? LCK_lock(tdbb, stateLock, LCK_PW, LCK_WAIT) :
|
|
|
|
LCK_convert(tdbb, stateLock, LCK_PW, LCK_WAIT);
|
|
|
|
if (!ret)
|
|
|
|
{
|
2015-03-28 01:36:04 +01:00
|
|
|
fb_assert(tdbb->tdbb_status_vector->getState() & IStatus::STATE_ERRORS);
|
2012-05-31 18:53:42 +02:00
|
|
|
ERR_punt();
|
|
|
|
}
|
|
|
|
fb_utils::init_status(tdbb->tdbb_status_vector);
|
|
|
|
needLock = false;
|
|
|
|
|
|
|
|
crypt = newCryptState;
|
|
|
|
|
|
|
|
// Write modified header page
|
|
|
|
Ods::header_page* header = hdr.write();
|
|
|
|
if (crypt)
|
|
|
|
{
|
|
|
|
header->hdr_flags |= Ods::hdr_encrypted;
|
|
|
|
plugName.copyTo(header->hdr_crypt_plugin, sizeof header->hdr_crypt_plugin);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
header->hdr_flags &= ~Ods::hdr_encrypted;
|
|
|
|
}
|
|
|
|
header->hdr_flags |= Ods::hdr_crypt_process;
|
2015-12-09 15:22:13 +01:00
|
|
|
header->hdr_crypt_page = 1;
|
2012-05-31 18:53:42 +02:00
|
|
|
process = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Trigger lock on ChangeCryptState
|
|
|
|
if (!LCK_convert(tdbb, stateLock, LCK_EX, LCK_WAIT))
|
|
|
|
{
|
|
|
|
ERR_punt();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!LCK_convert(tdbb, stateLock, LCK_SR, LCK_WAIT))
|
|
|
|
{
|
|
|
|
ERR_punt();
|
|
|
|
}
|
|
|
|
fb_utils::init_status(tdbb->tdbb_status_vector);
|
|
|
|
|
|
|
|
startCryptThread(tdbb);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CryptoManager::blockingAstChangeCryptState()
|
|
|
|
{
|
2012-12-14 18:59:02 +01:00
|
|
|
AsyncContextHolder tdbb(&dbb, FB_FUNCTION);
|
2012-05-31 18:53:42 +02:00
|
|
|
|
|
|
|
fb_assert(stateLock);
|
|
|
|
LCK_release(tdbb, stateLock);
|
|
|
|
needLock = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CryptoManager::startCryptThread(thread_db* tdbb)
|
|
|
|
{
|
2013-09-09 12:32:18 +02:00
|
|
|
// Try to take crypt mutex
|
|
|
|
// If can't take that mutex - nothing to do, cryptThread already runs in our process
|
|
|
|
MutexEnsureUnlock guard(cryptThreadMtx, FB_FUNCTION);
|
|
|
|
if (!guard.tryEnter())
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2013-09-06 15:09:07 +02:00
|
|
|
|
2012-05-31 18:53:42 +02:00
|
|
|
// Take exclusive threadLock
|
|
|
|
// If can't take that lock - nothing to do, cryptThread already runs somewhere
|
2013-09-09 12:32:18 +02:00
|
|
|
if (!LCK_lock(tdbb, threadLock, LCK_EX, LCK_NO_WAIT))
|
|
|
|
{
|
|
|
|
// Cleanup lock manager error
|
|
|
|
fb_utils::init_status(tdbb->tdbb_status_vector);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool releasingLock = false;
|
|
|
|
try
|
2012-05-31 18:53:42 +02:00
|
|
|
{
|
|
|
|
// Cleanup resources
|
|
|
|
terminateCryptThread(tdbb);
|
|
|
|
down = false;
|
|
|
|
|
|
|
|
// Determine current page from the header
|
|
|
|
Header hdr(tdbb, LCK_read);
|
|
|
|
process = hdr->hdr_flags & Ods::hdr_crypt_process ? true : false;
|
|
|
|
if (!process)
|
|
|
|
{
|
2013-09-09 12:32:18 +02:00
|
|
|
releasingLock = true;
|
2012-05-31 18:53:42 +02:00
|
|
|
LCK_release(tdbb, threadLock);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
currentPage.setValue(hdr->hdr_crypt_page);
|
|
|
|
|
|
|
|
// Refresh encryption flag
|
|
|
|
crypt = hdr->hdr_flags & Ods::hdr_encrypted ? true : false;
|
|
|
|
|
2013-09-09 12:32:18 +02:00
|
|
|
// If we are going to start crypt thread, we need plugin to be loaded
|
2012-05-31 18:53:42 +02:00
|
|
|
loadPlugin(hdr->hdr_crypt_plugin);
|
|
|
|
|
|
|
|
// ready to go
|
2013-09-09 12:32:18 +02:00
|
|
|
guard.leave(); // release in advance to avoid races with cryptThread()
|
2015-11-03 10:12:12 +01:00
|
|
|
Thread::start(cryptThreadStatic, (THREAD_ENTRY_PARAM) this, THREAD_medium, &cryptThreadId);
|
2012-05-31 18:53:42 +02:00
|
|
|
}
|
2013-09-09 12:32:18 +02:00
|
|
|
catch (const Firebird::Exception&)
|
|
|
|
{
|
|
|
|
if (!releasingLock) // avoid secondary exception in catch
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
LCK_release(tdbb, threadLock);
|
|
|
|
}
|
|
|
|
catch (const Firebird::Exception&)
|
|
|
|
{ }
|
|
|
|
}
|
|
|
|
|
|
|
|
throw;
|
|
|
|
}
|
2012-05-31 18:53:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void CryptoManager::attach(thread_db* tdbb, Attachment* att)
|
|
|
|
{
|
|
|
|
keyHolderPlugins.attach(att, dbb.dbb_config);
|
2015-11-16 10:48:16 +01:00
|
|
|
|
|
|
|
if (!cryptPlugin)
|
|
|
|
{
|
|
|
|
// Lock crypt state
|
|
|
|
takeStateLock(tdbb);
|
|
|
|
|
|
|
|
Header hdr(tdbb, LCK_read);
|
|
|
|
crypt = hdr->hdr_flags & Ods::hdr_encrypted;
|
|
|
|
process = hdr->hdr_flags & Ods::hdr_crypt_process;
|
|
|
|
|
|
|
|
if (crypt || process)
|
|
|
|
{
|
|
|
|
loadPlugin(hdr->hdr_crypt_plugin);
|
|
|
|
|
|
|
|
if (!cryptPlugin)
|
|
|
|
(Arg::Gds(isc_decrypt_error)).raise();
|
|
|
|
}
|
|
|
|
}
|
2012-05-31 18:53:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void CryptoManager::cryptThread()
|
|
|
|
{
|
2015-03-20 19:02:30 +01:00
|
|
|
FbLocalStatus status_vector;
|
2012-05-31 18:53:42 +02:00
|
|
|
|
|
|
|
try
|
|
|
|
{
|
2013-09-09 12:32:18 +02:00
|
|
|
// Try to take crypt mutex
|
|
|
|
// If can't take that mutex - nothing to do, cryptThread already runs in our process
|
|
|
|
MutexEnsureUnlock guard(cryptThreadMtx, FB_FUNCTION);
|
|
|
|
if (!guard.tryEnter())
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2013-09-06 15:09:07 +02:00
|
|
|
|
2012-05-31 18:53:42 +02:00
|
|
|
// establish context
|
|
|
|
UserId user;
|
|
|
|
user.usr_user_name = "(Crypt thread)";
|
|
|
|
|
|
|
|
Jrd::Attachment* const attachment = Jrd::Attachment::create(&dbb);
|
2015-10-12 16:26:00 +02:00
|
|
|
RefPtr<SysStableAttachment> sAtt(FB_NEW SysStableAttachment(attachment));
|
2014-06-10 09:13:34 +02:00
|
|
|
attachment->setStable(sAtt);
|
2012-05-31 18:53:42 +02:00
|
|
|
attachment->att_filename = dbb.dbb_filename;
|
|
|
|
attachment->att_user = &user;
|
|
|
|
|
2015-03-23 12:06:51 +01:00
|
|
|
BackgroundContextHolder tdbb(&dbb, attachment, &status_vector, FB_FUNCTION);
|
2012-05-31 18:53:42 +02:00
|
|
|
tdbb->tdbb_quantum = SWEEP_QUANTUM;
|
|
|
|
|
|
|
|
ULONG lastPage = getLastPage(tdbb);
|
|
|
|
ULONG runpage = 1;
|
|
|
|
Stack<ULONG> pages;
|
2013-09-09 12:32:18 +02:00
|
|
|
|
|
|
|
// Take exclusive threadLock
|
|
|
|
// If can't take that lock - nothing to do, cryptThread already runs somewhere
|
|
|
|
if (!LCK_lock(tdbb, threadLock, LCK_EX, LCK_NO_WAIT))
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-09-06 15:09:07 +02:00
|
|
|
bool lckRelease = false;
|
2012-05-31 18:53:42 +02:00
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
do
|
|
|
|
{
|
|
|
|
// Check is there some job to do
|
|
|
|
while ((runpage = currentPage.exchangeAdd(+1)) < lastPage)
|
|
|
|
{
|
|
|
|
// forced terminate
|
|
|
|
if (down)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// nbackup state check
|
2014-09-30 16:21:44 +02:00
|
|
|
if (dbb.dbb_backup_manager && dbb.dbb_backup_manager->getState() != Ods::hdr_nbak_normal)
|
2012-05-31 18:53:42 +02:00
|
|
|
{
|
2014-07-17 20:48:46 +02:00
|
|
|
if (static_cast<ULONG>(currentPage.exchangeAdd(-1)) >= lastPage)
|
2012-05-31 18:53:42 +02:00
|
|
|
{
|
|
|
|
// currentPage was set to last page by thread, closing database
|
|
|
|
break;
|
|
|
|
}
|
2014-08-15 16:19:02 +02:00
|
|
|
Thread::sleep(100);
|
2012-05-31 18:53:42 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// scheduling
|
|
|
|
if (--tdbb->tdbb_quantum < 0)
|
|
|
|
{
|
|
|
|
JRD_reschedule(tdbb, SWEEP_QUANTUM, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
// writing page to disk will change it's crypt status in usual way
|
|
|
|
WIN window(DB_PAGE_SPACE, runpage);
|
|
|
|
Ods::pag* page = CCH_FETCH(tdbb, &window, LCK_write, pag_undefined);
|
|
|
|
if (page && page->pag_type <= pag_max &&
|
|
|
|
(bool(page->pag_flags & Ods::crypted_page) != crypt) &&
|
|
|
|
Ods::pag_crypt_page[page->pag_type])
|
|
|
|
{
|
|
|
|
CCH_MARK(tdbb, &window);
|
|
|
|
pages.push(runpage);
|
|
|
|
}
|
|
|
|
CCH_RELEASE_TAIL(tdbb, &window);
|
|
|
|
|
|
|
|
// sometimes save currentPage into DB header
|
|
|
|
++runpage;
|
|
|
|
if ((runpage & 0x3FF) == 0)
|
|
|
|
{
|
|
|
|
writeDbHeader(tdbb, runpage, pages);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// At this moment of time all pages with number < lastpage
|
|
|
|
// are guaranteed to change crypt state. Check for added pages.
|
|
|
|
lastPage = getLastPage(tdbb);
|
2013-09-09 12:32:18 +02:00
|
|
|
|
|
|
|
// forced terminate
|
|
|
|
if (down)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
2012-05-31 18:53:42 +02:00
|
|
|
} while (runpage < lastPage);
|
|
|
|
|
|
|
|
// Finalize crypt
|
|
|
|
if (!down)
|
|
|
|
{
|
|
|
|
writeDbHeader(tdbb, 0, pages);
|
2015-12-09 15:22:13 +01:00
|
|
|
if (!crypt)
|
|
|
|
{
|
|
|
|
// Decryption finished, unload plugin as we don't need it anymore
|
|
|
|
PluginManagerInterfacePtr()->releasePlugin(cryptPlugin);
|
|
|
|
cryptPlugin = NULL;
|
|
|
|
}
|
2012-05-31 18:53:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Release exclusive lock on StartCryptThread
|
2013-09-06 15:09:07 +02:00
|
|
|
lckRelease = true;
|
2012-05-31 18:53:42 +02:00
|
|
|
LCK_release(tdbb, threadLock);
|
|
|
|
}
|
2012-06-03 05:00:24 +02:00
|
|
|
catch (const Exception&)
|
2012-05-31 18:53:42 +02:00
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
2013-09-06 15:09:07 +02:00
|
|
|
if (!lckRelease)
|
|
|
|
{
|
|
|
|
// try to save current state of crypt thread
|
2013-09-09 12:32:18 +02:00
|
|
|
if (!down)
|
|
|
|
{
|
|
|
|
writeDbHeader(tdbb, runpage, pages);
|
|
|
|
}
|
2012-05-31 18:53:42 +02:00
|
|
|
|
2013-09-06 15:09:07 +02:00
|
|
|
// Release exclusive lock on StartCryptThread
|
|
|
|
LCK_release(tdbb, threadLock);
|
|
|
|
}
|
2012-05-31 18:53:42 +02:00
|
|
|
}
|
2012-06-03 05:00:24 +02:00
|
|
|
catch (const Exception&)
|
2012-05-31 18:53:42 +02:00
|
|
|
{ }
|
|
|
|
|
|
|
|
throw;
|
|
|
|
}
|
|
|
|
}
|
2012-06-03 05:00:24 +02:00
|
|
|
catch (const Exception& ex)
|
2012-05-31 18:53:42 +02:00
|
|
|
{
|
|
|
|
// Error during context creation - we can't even release lock
|
|
|
|
iscLogException("Crypt thread:", ex);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CryptoManager::writeDbHeader(thread_db* tdbb, ULONG runpage, Stack<ULONG>& pages)
|
|
|
|
{
|
|
|
|
Header hdr(tdbb, LCK_write);
|
|
|
|
hdr.depends(pages);
|
|
|
|
|
|
|
|
Ods::header_page* header = hdr.write();
|
|
|
|
header->hdr_crypt_page = runpage;
|
|
|
|
if (!runpage)
|
|
|
|
{
|
|
|
|
header->hdr_flags &= ~Ods::hdr_crypt_process;
|
|
|
|
process = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-20 19:02:30 +01:00
|
|
|
bool CryptoManager::decrypt(FbStatusVector* sv, Ods::pag* page)
|
2012-05-31 18:53:42 +02:00
|
|
|
{
|
2012-06-05 12:06:31 +02:00
|
|
|
// Code calling us is not ready to process exceptions correctly
|
|
|
|
// Therefore use old (status vector based) method
|
2012-05-31 18:53:42 +02:00
|
|
|
try
|
|
|
|
{
|
2012-06-05 12:06:31 +02:00
|
|
|
if (page->pag_flags & Ods::crypted_page)
|
2012-05-31 18:53:42 +02:00
|
|
|
{
|
2012-06-05 12:06:31 +02:00
|
|
|
if (!cryptPlugin)
|
|
|
|
{
|
2015-11-16 11:25:16 +01:00
|
|
|
// This may happen only in case of classic server when other connection encrypted alive database
|
|
|
|
// We are invoked from shared cache manager, i.e. no valid attachment in tdbb
|
|
|
|
// Therefore create system temporary attachment like in crypt thread to be able to work with locks
|
|
|
|
UserId user;
|
|
|
|
user.usr_user_name = "(Crypt plugin loader)";
|
|
|
|
|
|
|
|
Jrd::Attachment* const attachment = Jrd::Attachment::create(&dbb);
|
|
|
|
RefPtr<SysStableAttachment> sAtt(FB_NEW SysStableAttachment(attachment));
|
|
|
|
attachment->setStable(sAtt);
|
|
|
|
attachment->att_filename = dbb.dbb_filename;
|
|
|
|
attachment->att_user = &user;
|
|
|
|
|
|
|
|
BackgroundContextHolder tdbb(&dbb, attachment, sv, FB_FUNCTION);
|
|
|
|
|
|
|
|
// Lock crypt state
|
|
|
|
takeStateLock(tdbb);
|
|
|
|
|
|
|
|
Header hdr(tdbb, LCK_read);
|
|
|
|
crypt = hdr->hdr_flags & Ods::hdr_encrypted;
|
|
|
|
process = hdr->hdr_flags & Ods::hdr_crypt_process;
|
|
|
|
|
|
|
|
if (crypt || process)
|
|
|
|
{
|
|
|
|
loadPlugin(hdr->hdr_crypt_plugin);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!cryptPlugin)
|
|
|
|
{
|
|
|
|
(Arg::Gds(isc_decrypt_error)).raise();
|
|
|
|
return false;
|
|
|
|
}
|
2012-05-31 18:53:42 +02:00
|
|
|
}
|
|
|
|
|
2015-03-20 19:02:30 +01:00
|
|
|
cryptPlugin->decrypt(sv, dbb.dbb_page_size - sizeof(Ods::pag), &page[1], &page[1]);
|
|
|
|
if (sv->getState() & IStatus::STATE_ERRORS)
|
2012-05-31 18:53:42 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2012-06-05 12:06:31 +02:00
|
|
|
return true;
|
2012-05-31 18:53:42 +02:00
|
|
|
}
|
2012-06-05 12:06:31 +02:00
|
|
|
catch (const Exception& ex)
|
|
|
|
{
|
2015-03-27 18:51:19 +01:00
|
|
|
ex.stuffException(sv);
|
2012-06-05 12:06:31 +02:00
|
|
|
}
|
|
|
|
return false;
|
2012-05-31 18:53:42 +02:00
|
|
|
}
|
|
|
|
|
2015-03-20 19:02:30 +01:00
|
|
|
Ods::pag* CryptoManager::encrypt(FbStatusVector* sv, Ods::pag* from, Ods::pag* to)
|
2012-05-31 18:53:42 +02:00
|
|
|
{
|
2012-06-05 12:06:31 +02:00
|
|
|
// Code calling us is not ready to process exceptions correctly
|
|
|
|
// Therefore use old (status vector based) method
|
|
|
|
try
|
2012-05-31 18:53:42 +02:00
|
|
|
{
|
2012-06-05 12:06:31 +02:00
|
|
|
if (crypt && cryptPlugin && Ods::pag_crypt_page[from->pag_type % (pag_max + 1)])
|
2012-05-31 18:53:42 +02:00
|
|
|
{
|
2012-06-05 12:06:31 +02:00
|
|
|
to[0] = from[0];
|
2012-05-31 18:53:42 +02:00
|
|
|
|
2015-03-20 19:02:30 +01:00
|
|
|
cryptPlugin->encrypt(sv, dbb.dbb_page_size - sizeof(Ods::pag), &from[1], &to[1]);
|
|
|
|
if (sv->getState() & IStatus::STATE_ERRORS)
|
2012-06-05 12:06:31 +02:00
|
|
|
return NULL;
|
2012-05-31 18:53:42 +02:00
|
|
|
|
2015-11-11 17:14:39 +01:00
|
|
|
to->pag_flags |= Ods::crypted_page; // Mark page that is going to be written as encrypted
|
|
|
|
from->pag_flags |= Ods::crypted_page; // Set the mark for page in cache as well
|
|
|
|
// If page write fail, nothing bad can happen
|
|
|
|
// it will be encrypted next time is modified
|
2012-06-05 12:06:31 +02:00
|
|
|
return to;
|
2012-05-31 18:53:42 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2012-06-05 12:06:31 +02:00
|
|
|
from->pag_flags &= ~Ods::crypted_page;
|
|
|
|
return from;
|
2012-05-31 18:53:42 +02:00
|
|
|
}
|
|
|
|
}
|
2012-06-05 12:06:31 +02:00
|
|
|
catch (const Exception& ex)
|
|
|
|
{
|
2015-03-27 18:51:19 +01:00
|
|
|
ex.stuffException(sv);
|
2012-06-05 12:06:31 +02:00
|
|
|
}
|
|
|
|
return NULL;
|
2012-05-31 18:53:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int CryptoManager::blockingAstChangeCryptState(void* object)
|
|
|
|
{
|
2012-06-03 05:00:24 +02:00
|
|
|
((CryptoManager*) object)->blockingAstChangeCryptState();
|
2012-05-31 18:53:42 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
ULONG CryptoManager::getCurrentPage()
|
|
|
|
{
|
|
|
|
return process ? currentPage.value() : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
ULONG CryptoManager::getLastPage(thread_db* tdbb)
|
|
|
|
{
|
|
|
|
return PAG_last_page(tdbb) + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
CryptoManager::HolderAttachments::HolderAttachments(MemoryPool& p)
|
|
|
|
: keyHolder(NULL), attachments(p)
|
|
|
|
{ }
|
|
|
|
|
|
|
|
void CryptoManager::HolderAttachments::setPlugin(IKeyHolderPlugin* kh)
|
|
|
|
{
|
|
|
|
keyHolder = kh;
|
|
|
|
keyHolder->addRef();
|
|
|
|
}
|
|
|
|
|
|
|
|
CryptoManager::HolderAttachments::~HolderAttachments()
|
|
|
|
{
|
|
|
|
if (keyHolder)
|
|
|
|
{
|
|
|
|
PluginManagerInterfacePtr()->releasePlugin(keyHolder);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CryptoManager::HolderAttachments::registerAttachment(Attachment* att)
|
|
|
|
{
|
|
|
|
attachments.add(att);
|
|
|
|
}
|
|
|
|
|
2012-06-25 15:11:11 +02:00
|
|
|
bool CryptoManager::HolderAttachments::unregisterAttachment(Attachment* att)
|
2012-05-31 18:53:42 +02:00
|
|
|
{
|
2012-06-25 15:11:11 +02:00
|
|
|
unsigned i = attachments.getCount();
|
|
|
|
while (i--)
|
2012-05-31 18:53:42 +02:00
|
|
|
{
|
|
|
|
if (attachments[i] == att)
|
|
|
|
{
|
|
|
|
attachments.remove(i);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2012-06-25 15:11:11 +02:00
|
|
|
return attachments.getCount() == 0;
|
2012-05-31 18:53:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool CryptoManager::HolderAttachments::operator==(IKeyHolderPlugin* kh) const
|
|
|
|
{
|
2015-01-12 15:56:55 +01:00
|
|
|
// ASF: I think there should be a better way to do this.
|
|
|
|
return keyHolder->cloopVTable == kh->cloopVTable;
|
2012-05-31 18:53:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void CryptoManager::KeyHolderPlugins::attach(Attachment* att, Config* config)
|
|
|
|
{
|
2012-12-14 18:59:02 +01:00
|
|
|
MutexLockGuard g(holdersMutex, FB_FUNCTION);
|
2012-05-31 18:53:42 +02:00
|
|
|
|
2015-02-18 16:01:17 +01:00
|
|
|
for (GetPlugins<IKeyHolderPlugin> keyControl(IPluginManager::TYPE_KEY_HOLDER, config);
|
2014-09-29 13:03:47 +02:00
|
|
|
keyControl.hasData(); keyControl.next())
|
2012-05-31 18:53:42 +02:00
|
|
|
{
|
|
|
|
IKeyHolderPlugin* keyPlugin = keyControl.plugin();
|
2015-03-20 19:02:30 +01:00
|
|
|
FbLocalStatus st;
|
2014-11-10 15:45:40 +01:00
|
|
|
if (keyPlugin->keyCallback(&st, att->att_crypt_callback) == 1) //// FIXME: 1 ???
|
2012-05-31 18:53:42 +02:00
|
|
|
{
|
|
|
|
// holder accepted attachment's key
|
|
|
|
HolderAttachments* ha = NULL;
|
|
|
|
|
|
|
|
for (unsigned i = 0; i < knownHolders.getCount(); ++i)
|
|
|
|
{
|
|
|
|
if (knownHolders[i] == keyPlugin)
|
|
|
|
{
|
|
|
|
ha = &knownHolders[i];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ha)
|
|
|
|
{
|
|
|
|
ha = &(knownHolders.add());
|
|
|
|
ha->setPlugin(keyPlugin);
|
|
|
|
}
|
|
|
|
|
|
|
|
ha->registerAttachment(att);
|
|
|
|
break; // Do not need >1 key from attachment to single DB
|
|
|
|
}
|
2015-03-20 19:02:30 +01:00
|
|
|
else
|
|
|
|
st.check();
|
2012-05-31 18:53:42 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CryptoManager::KeyHolderPlugins::detach(Attachment* att)
|
|
|
|
{
|
2012-12-14 18:59:02 +01:00
|
|
|
MutexLockGuard g(holdersMutex, FB_FUNCTION);
|
2012-05-31 18:53:42 +02:00
|
|
|
|
2012-06-25 15:11:11 +02:00
|
|
|
unsigned i = knownHolders.getCount();
|
|
|
|
while (i--)
|
2012-05-31 18:53:42 +02:00
|
|
|
{
|
2012-06-25 15:11:11 +02:00
|
|
|
if (knownHolders[i].unregisterAttachment(att))
|
2012-05-31 18:53:42 +02:00
|
|
|
{
|
|
|
|
knownHolders.remove(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CryptoManager::KeyHolderPlugins::init(IDbCryptPlugin* crypt)
|
|
|
|
{
|
2012-12-14 18:59:02 +01:00
|
|
|
MutexLockGuard g(holdersMutex, FB_FUNCTION);
|
2012-05-31 18:53:42 +02:00
|
|
|
|
|
|
|
Firebird::HalfStaticArray<Firebird::IKeyHolderPlugin*, 64> holdersVector;
|
|
|
|
unsigned int length = knownHolders.getCount();
|
|
|
|
IKeyHolderPlugin** vector = holdersVector.getBuffer(length);
|
|
|
|
for (unsigned i = 0; i < length; ++i)
|
|
|
|
{
|
|
|
|
vector[i] = knownHolders[i].getPlugin();
|
|
|
|
}
|
|
|
|
|
2015-03-20 19:02:30 +01:00
|
|
|
FbLocalStatus st;
|
2012-05-31 18:53:42 +02:00
|
|
|
crypt->setKey(&st, length, vector);
|
2015-03-20 19:02:30 +01:00
|
|
|
st.check();
|
2012-05-31 18:53:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace Jrd
|