8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-24 20:43:04 +01:00

Merge pull request #255 from FirebirdSQL/SrpPerf3

Backported CORE-6237: Fixed performance issue with SRP plugin (added connections cache)
This commit is contained in:
Alexander Peshkov 2020-01-31 15:25:25 +03:00 committed by GitHub
commit 9e1109cbd8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 605 additions and 416 deletions

View File

@ -52,9 +52,14 @@ YValve_Objects:= $(call dirObjects,yvalve) $(call dirObjects,yvalve/config)
AllObjects += $(YValve_Objects) AllObjects += $(YValve_Objects)
# Authentication database connections cache
SecDbCache:= $(call makeObjects,auth,SecDbCache.cpp)
# Remote # Remote
Remote_Common:= $(call dirObjects,remote) $(call dirObjects,auth/SecureRemotePassword) Remote_Common:= $(call dirObjects,remote) $(call dirObjects,auth/SecureRemotePassword)
Remote_Server:= $(call dirObjects,remote/server) $(call dirObjects,auth/SecureRemotePassword/server) Remote_Server:= $(call dirObjects,remote/server) $(call dirObjects,auth/SecureRemotePassword/server) \
$(SecDbCache)
Remote_Client:= $(call dirObjects,remote/client) $(call dirObjects,auth/SecureRemotePassword/client) \ Remote_Client:= $(call dirObjects,remote/client) $(call dirObjects,auth/SecureRemotePassword/client) \
$(call makeObjects,auth/SecurityDatabase,LegacyClient.cpp) \ $(call makeObjects,auth/SecurityDatabase,LegacyClient.cpp) \
$(call dirObjects,plugins/crypt/arc4) $(call dirObjects,plugins/crypt/arc4)
@ -171,7 +176,7 @@ AllObjects += $(LEGACY_USERS_MANAGE_Objects)
# Legacy authentication on server # Legacy authentication on server
LEGACY_AUTH_SERVER_Objects:= $(call makeObjects,auth/SecurityDatabase,LegacyServer.cpp) LEGACY_AUTH_SERVER_Objects:= $(call makeObjects,auth/SecurityDatabase,LegacyServer.cpp) $(SecDbCache)
AllObjects += $(LEGACY_AUTH_SERVER_Objects) AllObjects += $(LEGACY_AUTH_SERVER_Objects)

View File

@ -207,6 +207,7 @@
</Link> </Link>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="..\..\..\src\auth\SecDbCache.cpp" />
<ClCompile Include="..\..\..\src\auth\SecureRemotePassword\server\SrpServer.cpp" /> <ClCompile Include="..\..\..\src\auth\SecureRemotePassword\server\SrpServer.cpp" />
<ClCompile Include="..\..\..\src\remote\server\os\win32\chop.cpp" /> <ClCompile Include="..\..\..\src\remote\server\os\win32\chop.cpp" />
<ClCompile Include="..\..\..\src\remote\server\os\win32\cntl.cpp" /> <ClCompile Include="..\..\..\src\remote\server\os\win32\cntl.cpp" />

View File

@ -34,6 +34,9 @@
<ClCompile Include="..\..\..\src\auth\SecureRemotePassword\server\SrpServer.cpp"> <ClCompile Include="..\..\..\src\auth\SecureRemotePassword\server\SrpServer.cpp">
<Filter>AUTH files</Filter> <Filter>AUTH files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\..\src\auth\SecDbCache.cpp">
<Filter>AUTH files</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="..\..\..\src\remote\server\os\win32\server.ico"> <None Include="..\..\..\src\remote\server\os\win32\server.ico">

View File

@ -180,6 +180,7 @@
</ProjectReference> </ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="..\..\..\src\auth\SecDbCache.cpp" />
<ClCompile Include="..\..\..\src\auth\SecurityDatabase\LegacyServer.cpp" /> <ClCompile Include="..\..\..\src\auth\SecurityDatabase\LegacyServer.cpp" />
</ItemGroup> </ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />

View File

@ -20,5 +20,8 @@
<ClCompile Include="..\..\..\src\auth\SecurityDatabase\LegacyServer.cpp"> <ClCompile Include="..\..\..\src\auth\SecurityDatabase\LegacyServer.cpp">
<Filter>AUTH files</Filter> <Filter>AUTH files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\..\src\auth\SecDbCache.cpp">
<Filter>AUTH files</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -211,6 +211,7 @@
</Link> </Link>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="..\..\..\src\auth\SecDbCache.cpp" />
<ClCompile Include="..\..\..\src\auth\SecureRemotePassword\server\SrpServer.cpp" /> <ClCompile Include="..\..\..\src\auth\SecureRemotePassword\server\SrpServer.cpp" />
<ClCompile Include="..\..\..\src\remote\server\os\win32\chop.cpp" /> <ClCompile Include="..\..\..\src\remote\server\os\win32\chop.cpp" />
<ClCompile Include="..\..\..\src\remote\server\os\win32\cntl.cpp" /> <ClCompile Include="..\..\..\src\remote\server\os\win32\cntl.cpp" />

View File

@ -34,6 +34,9 @@
<ClCompile Include="..\..\..\src\auth\SecureRemotePassword\server\SrpServer.cpp"> <ClCompile Include="..\..\..\src\auth\SecureRemotePassword\server\SrpServer.cpp">
<Filter>AUTH files</Filter> <Filter>AUTH files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\..\src\auth\SecDbCache.cpp">
<Filter>AUTH files</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="..\..\..\src\remote\server\os\win32\server.ico"> <None Include="..\..\..\src\remote\server\os\win32\server.ico">

View File

@ -188,6 +188,7 @@
</ProjectReference> </ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="..\..\..\src\auth\SecDbCache.cpp" />
<ClCompile Include="..\..\..\src\auth\SecurityDatabase\LegacyServer.cpp" /> <ClCompile Include="..\..\..\src\auth\SecurityDatabase\LegacyServer.cpp" />
</ItemGroup> </ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />

View File

@ -20,5 +20,8 @@
<ClCompile Include="..\..\..\src\auth\SecurityDatabase\LegacyServer.cpp"> <ClCompile Include="..\..\..\src\auth\SecurityDatabase\LegacyServer.cpp">
<Filter>AUTH files</Filter> <Filter>AUTH files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\..\src\auth\SecDbCache.cpp">
<Filter>AUTH files</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -211,6 +211,7 @@
</Link> </Link>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="..\..\..\src\auth\SecDbCache.cpp" />
<ClCompile Include="..\..\..\src\auth\SecureRemotePassword\server\SrpServer.cpp" /> <ClCompile Include="..\..\..\src\auth\SecureRemotePassword\server\SrpServer.cpp" />
<ClCompile Include="..\..\..\src\remote\server\os\win32\chop.cpp" /> <ClCompile Include="..\..\..\src\remote\server\os\win32\chop.cpp" />
<ClCompile Include="..\..\..\src\remote\server\os\win32\cntl.cpp" /> <ClCompile Include="..\..\..\src\remote\server\os\win32\cntl.cpp" />

View File

@ -34,6 +34,9 @@
<ClCompile Include="..\..\..\src\auth\SecureRemotePassword\server\SrpServer.cpp"> <ClCompile Include="..\..\..\src\auth\SecureRemotePassword\server\SrpServer.cpp">
<Filter>AUTH files</Filter> <Filter>AUTH files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\..\src\auth\SecDbCache.cpp">
<Filter>AUTH files</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="..\..\..\src\remote\server\os\win32\server.ico"> <None Include="..\..\..\src\remote\server\os\win32\server.ico">

View File

@ -188,6 +188,7 @@
</ProjectReference> </ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="..\..\..\src\auth\SecDbCache.cpp" />
<ClCompile Include="..\..\..\src\auth\SecurityDatabase\LegacyServer.cpp" /> <ClCompile Include="..\..\..\src\auth\SecurityDatabase\LegacyServer.cpp" />
</ItemGroup> </ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />

View File

@ -20,5 +20,8 @@
<ClCompile Include="..\..\..\src\auth\SecurityDatabase\LegacyServer.cpp"> <ClCompile Include="..\..\..\src\auth\SecurityDatabase\LegacyServer.cpp">
<Filter>AUTH files</Filter> <Filter>AUTH files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\..\src\auth\SecDbCache.cpp">
<Filter>AUTH files</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
</Project> </Project>

158
src/auth/SecDbCache.cpp Normal file
View File

@ -0,0 +1,158 @@
/*
* PROGRAM: JRD Access Method
* MODULE: SecDbCache.cpp
* DESCRIPTION: Cached security database connection
*
* The contents of this file are subject to the Interbase 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.Inprise.com/IPL.html
*
* Software distributed under the License is distributed on an
* "AS IS" basis, 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 Inprise Corporation
* and its predecessors. Portions created by Inprise Corporation are
* Copyright (C) Inprise Corporation.
*
* All Rights Reserved.
* Contributor(s): ______________________________________.
*
* 2003.02.02 Dmitry Yemanov: Implemented cached security database connection
* 2011 - 2020 Alexander Peshkov
*/
#include "firebird.h"
#include "../auth/SecDbCache.h"
#include "../jrd/status.h"
#include "../common/isc_proto.h"
#include <string.h>
using namespace Firebird;
namespace Auth {
void CachedSecurityDatabase::close()
{
Jrd::FbLocalStatus s;
TimerInterfacePtr()->start(&s, this, 10 * 1000 * 1000);
if (s->getState() & IStatus::STATE_ERRORS)
handler();
}
void CachedSecurityDatabase::handler()
{
list->handler(this);
}
void PluginDatabases::getInstance(IPluginConfig* pluginConfig, RefPtr<CachedSecurityDatabase>& instance)
{
// Determine sec.db name based on existing config
PathName secDbName;
{ // config scope
Jrd::FbLocalStatus s;
RefPtr<IFirebirdConf> config(REF_NO_INCR, pluginConfig->getFirebirdConf(&s));
check(&s);
const unsigned int INIT_KEY = ((~0) - 1);
static unsigned int secDbKey = INIT_KEY;
if (secDbKey == INIT_KEY)
secDbKey = config->getKey("SecurityDatabase");
const char* tmp = config->asString(secDbKey);
if (!tmp)
Arg::Gds(isc_secdb_name).raise();
secDbName = tmp;
}
{ // guard scope
MutexLockGuard g(arrayMutex, FB_FUNCTION);
for (unsigned int i = 0; i < dbArray.getCount(); ++i)
{
if (secDbName == dbArray[i]->secureDbName)
{
instance = dbArray[i];
break;
}
}
if (!instance)
{
instance = FB_NEW CachedSecurityDatabase(this, secDbName);
instance->addRef();
secDbName.copyTo(instance->secureDbName, sizeof(instance->secureDbName));
dbArray.add(instance);
}
}
}
int PluginDatabases::shutdown()
{
try
{
MutexLockGuard g(arrayMutex, FB_FUNCTION);
for (unsigned int i = 0; i < dbArray.getCount(); ++i)
{
if (dbArray[i])
{
Jrd::FbLocalStatus s;
TimerInterfacePtr()->stop(&s, dbArray[i]);
check(&s);
dbArray[i]->release();
dbArray[i] = NULL;
}
}
dbArray.clear();
}
catch (Exception &ex)
{
StaticStatusVector st;
ex.stuffException(st);
const ISC_STATUS* status = st.begin();
if (status[0] == 1 && status[1] != isc_att_shutdown)
{
iscLogStatus("Legacy security database shutdown", status);
}
return FB_FAILURE;
}
return FB_SUCCESS;
}
void PluginDatabases::handler(CachedSecurityDatabase* tgt)
{
try
{
MutexLockGuard g(arrayMutex, FB_FUNCTION);
for (unsigned int i = 0; i < dbArray.getCount(); ++i)
{
if (dbArray[i] == tgt)
{
dbArray.remove(i);
tgt->release();
break;
}
}
}
catch (Exception &ex)
{
StaticStatusVector st;
ex.stuffException(st);
const ISC_STATUS* status = st.begin();
if (status[0] == 1 && status[1] != isc_att_shutdown)
{
iscLogStatus("Legacy security database timer handler", status);
}
}
}
} // namespace Auth

109
src/auth/SecDbCache.h Normal file
View File

@ -0,0 +1,109 @@
/*
* PROGRAM: JRD Access Method
* MODULE: SecDbCache.h
* DESCRIPTION: Cached security database connection
*
* The contents of this file are subject to the Interbase 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.Inprise.com/IPL.html
*
* Software distributed under the License is distributed on an
* "AS IS" basis, 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 Inprise Corporation
* and its predecessors. Portions created by Inprise Corporation are
* Copyright (C) Inprise Corporation.
*
* All Rights Reserved.
* Contributor(s): ______________________________________.
*
* 2003.02.02 Dmitry Yemanov: Implemented cached security database connection
* 2011 - 2020 Alexander Peshkov
*/
#ifndef FB_SECDBCACHE_H
#define FB_SECDBCACHE_H
#include "firebird/Interface.h"
#include "../common/classes/ImplementHelper.h"
#include "../common/classes/fb_string.h"
#include "../common/classes/array.h"
#include "../common/classes/alloc.h"
#include "../common/classes/auto.h"
namespace Auth {
class VSecDb
{
public:
VSecDb()
{
}
virtual ~VSecDb()
{
}
virtual bool lookup(void* inMsg, void* outMsg) = 0;
};
class PluginDatabases;
class CachedSecurityDatabase FB_FINAL
: public Firebird::RefCntIface<Firebird::ITimerImpl<CachedSecurityDatabase, Firebird::CheckStatusWrapper> >
{
public:
char secureDbName[MAXPATHLEN + 1];
CachedSecurityDatabase(PluginDatabases* l, const Firebird::PathName& nm)
: secDb(NULL), list(l)
{
nm.copyTo(secureDbName, sizeof secureDbName);
}
// ITimer implementation
void handler();
int release()
{
if (--refCounter == 0)
{
delete this;
return 0;
}
return 1;
}
void close();
Firebird::Mutex mutex;
Firebird::AutoPtr<VSecDb> secDb;
PluginDatabases* list;
};
class PluginDatabases
{
public:
PluginDatabases(MemoryPool& p)
: dbArray(p)
{ }
private:
Firebird::HalfStaticArray<CachedSecurityDatabase*, 4> dbArray;
Firebird::Mutex arrayMutex;
public:
void getInstance(Firebird::IPluginConfig* pluginConfig, Firebird::RefPtr<CachedSecurityDatabase>& instance);
int shutdown();
void handler(CachedSecurityDatabase* tgt);
};
} // namespace Auth
#endif // FB_SECDBCACHE_H

View File

@ -25,28 +25,51 @@
*/ */
#include "firebird.h" #include "firebird.h"
#include "firebird/Message.h"
#include "../auth/SecureRemotePassword/client/SrpClient.h" #include "../auth/SecureRemotePassword/server/SrpServer.h"
#include "../auth/SecureRemotePassword/srp.h" #include "../auth/SecureRemotePassword/srp.h"
#include "../common/classes/ImplementHelper.h" #include "../common/classes/ImplementHelper.h"
#include "../common/classes/ClumpletWriter.h" #include "../common/classes/ClumpletWriter.h"
#include "../auth/SecureRemotePassword/Message.h" #include "../jrd/status.h"
#include "../common/isc_proto.h"
#include "../jrd/constants.h" #include "../jrd/constants.h"
#include "../auth/SecDbCache.h"
using namespace Firebird; using namespace Firebird;
using namespace Auth;
namespace { namespace {
GlobalPtr<PluginDatabases> instances;
const unsigned int INIT_KEY = ((~0) - 1); const unsigned int INIT_KEY = ((~0) - 1);
unsigned int secDbKey = INIT_KEY; unsigned int secDbKey = INIT_KEY;
const unsigned int SZ_LOGIN = 31; const unsigned int SZ_LOGIN = 31;
} struct Metadata
{
Jrd::FbLocalStatus status;
FB_MESSAGE (Param, CheckStatusWrapper,
(FB_VARCHAR(SZ_LOGIN), login)
) param;
FB_MESSAGE (Data, CheckStatusWrapper,
(FB_VARCHAR(128), verifier)
(FB_VARCHAR(32), salt)
) data;
Metadata()
: param(&status, MasterInterfacePtr()), data(&status, MasterInterfacePtr())
{ }
namespace Auth { Metadata(MemoryPool& p)
: status(p), param(&status, MasterInterfacePtr()), data(&status, MasterInterfacePtr())
{ }
};
InitInstance<Metadata> meta;
class SrpServer : public StdPlugin<IServerImpl<SrpServer, CheckStatusWrapper> > class SrpServer : public StdPlugin<IServerImpl<SrpServer, CheckStatusWrapper> >
{ {
@ -55,13 +78,8 @@ public:
: server(NULL), data(getPool()), account(getPool()), : server(NULL), data(getPool()), account(getPool()),
clientPubKey(getPool()), serverPubKey(getPool()), clientPubKey(getPool()), serverPubKey(getPool()),
verifier(getPool()), salt(getPool()), sessionKey(getPool()), verifier(getPool()), salt(getPool()), sessionKey(getPool()),
secDbName(NULL), cryptCallback(NULL) iParameter(par), secDbName(getPool()), cryptCallback(NULL)
{ { }
LocalStatus ls;
CheckStatusWrapper s(&ls);
config.assignRefNoIncr(par->getFirebirdConf(&s));
check(&s);
}
// IServer implementation // IServer implementation
int authenticate(CheckStatusWrapper* status, IServerBlock* sBlock, IWriter* writerInterface); int authenticate(CheckStatusWrapper* status, IServerBlock* sBlock, IWriter* writerInterface);
@ -81,13 +99,105 @@ private:
UCharBuffer verifier; UCharBuffer verifier;
string salt; string salt;
UCharBuffer sessionKey; UCharBuffer sessionKey;
RefPtr<IFirebirdConf> config; RefPtr<IPluginConfig> iParameter;
const char* secDbName; PathName secDbName;
ICryptKeyCallback* cryptCallback; ICryptKeyCallback* cryptCallback;
protected: protected:
virtual RemotePassword* RemotePasswordFactory()=0; virtual RemotePassword* RemotePasswordFactory()=0;
}; };
class SecurityDatabase : public VSecDb
{
public:
bool lookup(void* inMsg, void* outMsg)
{
Jrd::FbLocalStatus status;
stmt->execute(&status, tra, meta().param.getMetadata(), inMsg,
meta().data.getMetadata(), outMsg);
check(&status);
return false; // safe default
}
// This 2 are needed to satisfy temporarily different calling requirements
static int shutdown(const int, const int, void*)
{
return instances->shutdown();
}
static void cleanup()
{
instances->shutdown();
}
SecurityDatabase(const char* secDbName, ICryptKeyCallback* cryptCallback)
: att(NULL), tra(NULL), stmt(NULL)
{
Jrd::FbLocalStatus status;
DispatcherPtr p;
if (cryptCallback)
{
p->setDbCryptCallback(&status, cryptCallback);
status->init(); // ignore possible errors like missing call in provider
}
ClumpletWriter dpb(ClumpletReader::dpbList, MAX_DPB_SIZE);
dpb.insertByte(isc_dpb_sec_attach, TRUE);
dpb.insertString(isc_dpb_user_name, SYSDBA_USER_NAME, fb_strlen(SYSDBA_USER_NAME));
dpb.insertString(isc_dpb_config, ParsedList::getNonLoopbackProviders(secDbName));
att = p->attachDatabase(&status, secDbName, dpb.getBufferLength(), dpb.getBuffer());
check(&status);
HANDSHAKE_DEBUG(fprintf(stderr, "Srv SRP: attached sec db %s\n", secDbName));
const UCHAR tpb[] =
{
isc_tpb_version1,
isc_tpb_read,
isc_tpb_read_committed,
isc_tpb_rec_version,
isc_tpb_wait
};
tra = att->startTransaction(&status, sizeof(tpb), tpb);
check(&status);
HANDSHAKE_DEBUG(fprintf(stderr, "Srv: SRP1: started transaction\n"));
const char* sql =
"SELECT PLG$VERIFIER, PLG$SALT FROM PLG$SRP WHERE PLG$USER_NAME = ? AND PLG$ACTIVE";
stmt = att->prepare(&status, tra, 0, sql, 3, IStatement::PREPARE_PREFETCH_METADATA);
if (status->getState() & IStatus::STATE_ERRORS)
{
checkStatusVectorForMissingTable(status->getErrors());
status_exception::raise(&status);
}
}
private:
IAttachment* att;
ITransaction* tra;
IStatement* stmt;
~SecurityDatabase()
{
Jrd::FbLocalStatus status;
stmt->free(&status);
checkLogStatus(status);
tra->rollback(&status);
checkLogStatus(status);
att->detach(&status);
checkLogStatus(status);
}
void checkLogStatus(Jrd::FbLocalStatus& status)
{
if (!status.isSuccess())
iscLogStatus("Srp Server", &status);
}
};
template <class SHA> class SrpServerImpl FB_FINAL : public SrpServer template <class SHA> class SrpServerImpl FB_FINAL : public SrpServer
{ {
public: public:
@ -100,6 +210,7 @@ protected:
} }
}; };
int SrpServer::authenticate(CheckStatusWrapper* status, IServerBlock* sb, IWriter* writerInterface) int SrpServer::authenticate(CheckStatusWrapper* status, IServerBlock* sb, IWriter* writerInterface)
{ {
try try
@ -126,110 +237,46 @@ int SrpServer::authenticate(CheckStatusWrapper* status, IServerBlock* sb, IWrite
return AUTH_MORE_DATA; return AUTH_MORE_DATA;
} }
// read salt and verifier from database // load verifier and salt from security database
// obviously we need something like attachments cache here Metadata messages;
if (secDbKey == INIT_KEY) messages.param->login.set(account.c_str());
{ messages.param->loginNull = 0;
secDbKey = config->getKey("SecurityDatabase"); messages.data.clear();
}
secDbName = config->asString(secDbKey); { // reference & mutex scope scope
if (!(secDbName && secDbName[0])) // Get database block from cache
{ RefPtr<CachedSecurityDatabase> instance;
Arg::Gds(isc_secdb_name).raise(); instances->getInstance(iParameter, instance);
}
DispatcherPtr p;
IAttachment* att = NULL;
ITransaction* tra = NULL;
IStatement* stmt = NULL;
try try
{ {
if (cryptCallback) MutexLockGuard g(instance->mutex, FB_FUNCTION);
{
p->setDbCryptCallback(status, cryptCallback);
status->init(); // ignore possible errors like missing call in provider
}
ClumpletWriter dpb(ClumpletReader::dpbList, MAX_DPB_SIZE); secDbName = instance->secureDbName;
dpb.insertByte(isc_dpb_sec_attach, TRUE); if (!instance->secDb)
dpb.insertString(isc_dpb_user_name, SYSDBA_USER_NAME, fb_strlen(SYSDBA_USER_NAME)); instance->secDb = FB_NEW SecurityDatabase(instance->secureDbName, cryptCallback);
dpb.insertString(isc_dpb_config, Auth::ParsedList::getNonLoopbackProviders(secDbName));
att = p->attachDatabase(status, secDbName, dpb.getBufferLength(), dpb.getBuffer()); instance->secDb->lookup(messages.param.getData(), messages.data.getData());
check(status);
HANDSHAKE_DEBUG(fprintf(stderr, "Srv SRP: attached sec db %s\n", secDbName));
const UCHAR tpb[] =
{
isc_tpb_version1,
isc_tpb_read,
isc_tpb_read_committed,
isc_tpb_rec_version,
isc_tpb_wait
};
tra = att->startTransaction(status, sizeof(tpb), tpb);
check(status);
HANDSHAKE_DEBUG(fprintf(stderr, "Srv: SRP1: started transaction\n"));
const char* sql =
"SELECT PLG$VERIFIER, PLG$SALT FROM PLG$SRP WHERE PLG$USER_NAME = ? AND PLG$ACTIVE";
stmt = att->prepare(status, tra, 0, sql, 3, IStatement::PREPARE_PREFETCH_METADATA);
if (status->getState() & IStatus::STATE_ERRORS)
{
checkStatusVectorForMissingTable(status->getErrors());
status_exception::raise(status);
}
Meta im(stmt, false);
Message par(im);
Field<Varying> login(par);
login = account.c_str();
Meta om(stmt, true);
Message dat(om);
check(status);
Field<Varying> verify(dat);
Field<Varying> slt(dat);
HANDSHAKE_DEBUG(fprintf(stderr, "Srv: SRP1: Ready to run statement with login '%s'\n", account.c_str()));
stmt->execute(status, tra, par.getMetadata(), par.getBuffer(),
dat.getMetadata(), dat.getBuffer());
check(status);
HANDSHAKE_DEBUG(fprintf(stderr, "Srv: SRP1: Executed statement\n"));
verifier.assign(reinterpret_cast<const UCHAR*>(verify->data), verify->len);
dumpIt("Srv: verifier", verifier);
UCharBuffer s;
s.assign(reinterpret_cast<const UCHAR*>(slt->data), slt->len);
BigInteger(s).getText(salt);
dumpIt("Srv: salt", salt);
stmt->free(status);
check(status);
stmt = NULL;
tra->rollback(status);
check(status);
tra = NULL;
att->detach(status);
check(status);
att = NULL;
} }
catch(const Exception&) catch(const Exception&)
{ {
LocalStatus ls; instance->close();
CheckStatusWrapper s(&ls);
if (stmt) stmt->free(&s);
if (tra) tra->rollback(&s);
if (att) att->detach(&s);
throw; throw;
} }
instance->close();
}
HANDSHAKE_DEBUG(fprintf(stderr, "Srv: SRP1: Executed statement\n"));
verifier.assign(reinterpret_cast<const UCHAR*>(messages.data->verifier.str), messages.data->verifier.length);
dumpIt("Srv: verifier", verifier);
UCharBuffer s;
s.assign(reinterpret_cast<const UCHAR*>(messages.data->salt.str), messages.data->salt.length);
BigInteger(s).getText(salt);
dumpIt("Srv: salt", salt);
// create SRP-calculating server
server = RemotePasswordFactory(); server = RemotePasswordFactory();
server->genServerKey(serverPubKey, verifier); server->genServerKey(serverPubKey, verifier);
@ -274,7 +321,7 @@ int SrpServer::authenticate(CheckStatusWrapper* status, IServerBlock* sb, IWrite
{ {
return AUTH_FAILED; return AUTH_FAILED;
} }
writerInterface->setDb(status, secDbName); writerInterface->setDb(status, secDbName.c_str());
if (status->getState() & IStatus::STATE_ERRORS) if (status->getState() & IStatus::STATE_ERRORS)
{ {
return AUTH_FAILED; return AUTH_FAILED;
@ -327,14 +374,16 @@ int SrpServer::release()
return 1; return 1;
} }
namespace
{
SimpleFactory<SrpServerImpl<Sha1> > factory_sha1; SimpleFactory<SrpServerImpl<Sha1> > factory_sha1;
SimpleFactory<SrpServerImpl<sha224> > factory_sha224; SimpleFactory<SrpServerImpl<sha224> > factory_sha224;
SimpleFactory<SrpServerImpl<sha256> > factory_sha256; SimpleFactory<SrpServerImpl<sha256> > factory_sha256;
SimpleFactory<SrpServerImpl<sha384> > factory_sha384; SimpleFactory<SrpServerImpl<sha384> > factory_sha384;
SimpleFactory<SrpServerImpl<sha512> > factory_sha512; SimpleFactory<SrpServerImpl<sha512> > factory_sha512;
}
} // anonymous namespace
namespace Auth {
void registerSrpServer(IPluginManager* iPlugin) void registerSrpServer(IPluginManager* iPlugin)
{ {

View File

@ -24,25 +24,22 @@
*/ */
#include "firebird.h" #include "firebird.h"
#include <string.h>
#include <stdlib.h> #include "ibase.h"
#include <time.h> #include "gen/iberror.h"
#include "../jrd/ibase.h"
#include "../auth/SecurityDatabase/LegacyServer.h"
#include "../auth/SecurityDatabase/LegacyHash.h"
#include "../common/enc_proto.h"
#include "../jrd/err_proto.h"
#include "../yvalve/gds_proto.h"
#include "../common/isc_proto.h"
#include "../jrd/jrd_proto.h"
#include "../jrd/scl.h"
#include "../common/config/config.h"
#include "../common/classes/objects_array.h"
#include "../common/classes/init.h"
#include "../common/classes/ImplementHelper.h"
#include "firebird/Interface.h" #include "firebird/Interface.h"
#include "../auth/SecurityDatabase/LegacyServer.h"
#include "../auth/SecurityDatabase/LegacyHash.h"
#include "../auth/SecDbCache.h"
#include "../remote/remot_proto.h" #include "../remote/remot_proto.h"
#include "../jrd/constants.h"
#include "../common/enc_proto.h"
#include "../jrd/status.h"
#include "../common/classes/init.h"
#include "../common/classes/ClumpletWriter.h"
#include <string.h>
#define PLUG_MODULE 1 #define PLUG_MODULE 1
@ -109,6 +106,8 @@ struct user_record
SCHAR password[Auth::MAX_LEGACY_PASSWORD_LENGTH + 2]; SCHAR password[Auth::MAX_LEGACY_PASSWORD_LENGTH + 2];
}; };
typedef char user_name[129];
// Transaction parameter buffer // Transaction parameter buffer
const UCHAR TPB[4] = const UCHAR TPB[4] =
@ -123,65 +122,50 @@ const UCHAR TPB[4] =
namespace Auth { namespace Auth {
GlobalPtr<PluginDatabases> instances;
class SecurityDatabaseServer FB_FINAL : class SecurityDatabaseServer FB_FINAL :
public StdPlugin<IServerImpl<SecurityDatabaseServer, CheckStatusWrapper> > public StdPlugin<IServerImpl<SecurityDatabaseServer, CheckStatusWrapper> >
{ {
public: public:
explicit SecurityDatabaseServer(Firebird::IPluginConfig* p) explicit SecurityDatabaseServer(IPluginConfig* p)
: iParameter(p) : iParameter(p)
{ } { }
// IServer implementation // IServer implementation
int authenticate(Firebird::CheckStatusWrapper* status, Firebird::IServerBlock* sBlock, int authenticate(CheckStatusWrapper* status, IServerBlock* sBlock,
Firebird::IWriter* writerInterface); IWriter* writerInterface);
void setDbCryptCallback(Firebird::CheckStatusWrapper*, Firebird::ICryptKeyCallback*) { } // ignore void setDbCryptCallback(CheckStatusWrapper*, ICryptKeyCallback*) { } // ignore
int release(); int release();
private: private:
Firebird::RefPtr<Firebird::IPluginConfig> iParameter; RefPtr<IPluginConfig> iParameter;
}; };
class SecurityDatabase FB_FINAL : public RefCntIface<ITimerImpl<SecurityDatabase, CheckStatusWrapper> >
class SecurityDatabase : public VSecDb
{ {
public: public:
int verify(IWriter* authBlock, IServerBlock* sBlock); bool lookup(void* inMsg, void* outMsg);
// This 2 are needed to satisfy temporarily different calling requirements // This 2 are needed to satisfy temporarily different calling requirements
static int shutdown(const int, const int, void*) static int shutdown(const int, const int, void*)
{ {
return shutdown(); return instances->shutdown();
} }
static void cleanup() static void cleanup()
{ {
shutdown(); instances->shutdown();
} }
static int shutdown(); SecurityDatabase(const char* secDbName)
char secureDbName[MAXPATHLEN];
SecurityDatabase()
: lookup_db(0), lookup_req(0) : lookup_db(0), lookup_req(0)
{ {
} prepare(secDbName);
// ITimer implementation
void handler();
int release()
{
if (--refCounter == 0)
{
delete this;
return 0;
}
return 1;
} }
private: private:
Firebird::Mutex mutex;
ISC_STATUS_ARRAY status; ISC_STATUS_ARRAY status;
isc_db_handle lookup_db; isc_db_handle lookup_db;
@ -189,8 +173,7 @@ private:
~SecurityDatabase(); ~SecurityDatabase();
bool lookup_user(const char*, char*); void prepare(const char* secDbName);
void prepare();
void checkStatus(const char* callName, ISC_STATUS userError = isc_psw_db_error); void checkStatus(const char* callName, ISC_STATUS userError = isc_psw_db_error);
}; };
@ -219,60 +202,8 @@ SecurityDatabase::~SecurityDatabase()
} }
} }
bool SecurityDatabase::lookup_user(const char* user_name, char* pwd)
{
bool found = false; // user found flag
char uname[129]; // user name buffer
user_record user; // user record
// Start by clearing the output data void SecurityDatabase::prepare(const char* secureDbName)
if (pwd)
*pwd = '\0';
strncpy(uname, user_name, sizeof uname);
uname[sizeof uname - 1] = 0;
MutexLockGuard guard(mutex, FB_FUNCTION);
// Attach database and compile request
prepare();
// Lookup
isc_tr_handle lookup_trans = 0;
isc_start_transaction(status, &lookup_trans, 1, &lookup_db, sizeof(TPB), TPB);
checkStatus("isc_start_transaction", isc_psw_start_trans);
isc_start_and_send(status, &lookup_req, &lookup_trans, 0, sizeof(uname), uname, 0);
checkStatus("isc_start_and_send");
while (true)
{
isc_receive(status, &lookup_req, 1, sizeof(user), &user, 0);
checkStatus("isc_receive");
if (!user.flag || status[1])
break;
found = true;
if (pwd)
{
strncpy(pwd, user.password, MAX_LEGACY_PASSWORD_LENGTH);
pwd[MAX_LEGACY_PASSWORD_LENGTH] = 0;
}
}
isc_rollback_transaction(status, &lookup_trans);
checkStatus("isc_rollback_transaction");
return found;
}
void SecurityDatabase::prepare()
{ {
if (lookup_db) if (lookup_db)
{ {
@ -295,7 +226,7 @@ void SecurityDatabase::prepare()
dpb.insertString(isc_dpb_trusted_auth, SYSDBA_USER_NAME, fb_strlen(SYSDBA_USER_NAME)); dpb.insertString(isc_dpb_trusted_auth, SYSDBA_USER_NAME, fb_strlen(SYSDBA_USER_NAME));
// Do not use loopback provider // Do not use loopback provider
dpb.insertString(isc_dpb_config, Auth::ParsedList::getNonLoopbackProviders(secureDbName)); dpb.insertString(isc_dpb_config, ParsedList::getNonLoopbackProviders(secureDbName));
isc_db_handle tempHandle = 0; isc_db_handle tempHandle = 0;
isc_attach_database(status, 0, secureDbName, &tempHandle, isc_attach_database(status, 0, secureDbName, &tempHandle,
@ -315,74 +246,6 @@ void SecurityDatabase::prepare()
checkStatus("isc_compile_request", isc_psw_attach); checkStatus("isc_compile_request", isc_psw_attach);
} }
/******************************************************************************
*
* Public interface
*/
int SecurityDatabase::verify(IWriter* authBlock, IServerBlock* sBlock)
{
const char* user = sBlock->getLogin();
string login(user ? user : "");
unsigned length;
const unsigned char* data = sBlock->getData(&length);
string passwordEnc;
if (data)
{
passwordEnc.assign(data, length);
}
if (!login.hasData())
{
return IAuth::AUTH_CONTINUE;
}
if (!passwordEnc.hasData())
{
return IAuth::AUTH_MORE_DATA;
}
// Look up the user name in the userinfo database and use the parameters
// found there. This means that another database must be accessed, and
// that means the current context must be saved and restored.
char pw1[MAX_LEGACY_PASSWORD_LENGTH + 1];
if (!lookup_user(login.c_str(), pw1))
{
return IAuth::AUTH_CONTINUE;
}
pw1[MAX_LEGACY_PASSWORD_LENGTH] = 0;
string storedHash(pw1, MAX_LEGACY_PASSWORD_LENGTH);
storedHash.rtrim();
string newHash;
LegacyHash::hash(newHash, login, passwordEnc, storedHash);
if (newHash != storedHash)
{
bool legacyHash = Config::getLegacyHash();
if (legacyHash)
{
newHash.resize(MAX_LEGACY_PASSWORD_LENGTH + 2);
ENC_crypt(newHash.begin(), newHash.length(), passwordEnc.c_str(), LEGACY_PASSWORD_SALT);
newHash.recalculate_length();
newHash.erase(0, 2);
legacyHash = newHash == storedHash;
}
if (!legacyHash)
{
return IAuth::AUTH_CONTINUE;
}
}
LocalStatus ls;
CheckStatusWrapper s(&ls);
authBlock->add(&s, login.c_str());
check(&s);
authBlock->setDb(&s, secureDbName);
check(&s);
return IAuth::AUTH_SUCCESS;
}
void SecurityDatabase::checkStatus(const char* callName, ISC_STATUS userError) void SecurityDatabase::checkStatus(const char* callName, ISC_STATUS userError)
{ {
if (status[1] == 0) if (status[1] == 0)
@ -402,143 +265,136 @@ void SecurityDatabase::checkStatus(const char* callName, ISC_STATUS userError)
secDbError.raise(); secDbError.raise();
} }
typedef HalfStaticArray<SecurityDatabase*, 4> InstancesArray; bool SecurityDatabase::lookup(void* inMsg, void* outMsg)
GlobalPtr<InstancesArray> instances; {
GlobalPtr<Mutex> instancesMutex; isc_tr_handle lookup_trans = 0;
void SecurityDatabase::handler() isc_start_transaction(status, &lookup_trans, 1, &lookup_db, sizeof(TPB), TPB);
{ checkStatus("isc_start_transaction", isc_psw_start_trans);
try
{
MutexLockGuard g(instancesMutex, FB_FUNCTION);
InstancesArray& curInstances(instances); isc_start_and_send(status, &lookup_req, &lookup_trans, 0, sizeof(user_name), inMsg, 0);
for (unsigned int i = 0; i < curInstances.getCount(); ++i) checkStatus("isc_start_and_send");
bool found = false;
while (true)
{ {
if (curInstances[i] == this) user_record* user = static_cast<user_record*>(outMsg);
{ isc_receive(status, &lookup_req, 1, sizeof(user_record), user, 0);
curInstances.remove(i); checkStatus("isc_receive");
release();
if (!user->flag || status[1])
break; break;
}
} found = true;
}
catch (Exception &ex)
{
StaticStatusVector st;
ex.stuffException(st);
const ISC_STATUS* status = st.begin();
if (status[0] == 1 && status[1] != isc_att_shutdown)
{
iscLogStatus("Legacy security database timer handler", status);
}
}
} }
int SecurityDatabase::shutdown() isc_rollback_transaction(status, &lookup_trans);
{ checkStatus("isc_rollback_transaction");
try
{ return found;
MutexLockGuard g(instancesMutex, FB_FUNCTION);
InstancesArray& curInstances(instances);
for (unsigned int i = 0; i < curInstances.getCount(); ++i)
{
if (curInstances[i])
{
LocalStatus ls;
CheckStatusWrapper s(&ls);
TimerInterfacePtr()->stop(&s, curInstances[i]);
check(&s);
curInstances[i]->release();
curInstances[i] = NULL;
}
}
curInstances.clear();
}
catch (Exception &ex)
{
StaticStatusVector st;
ex.stuffException(st);
const ISC_STATUS* status = st.begin();
if (status[0] == 1 && status[1] != isc_att_shutdown)
{
iscLogStatus("Legacy security database shutdown", status);
} }
return FB_FAILURE;
}
return FB_SUCCESS; /******************************************************************************
} *
* Public interface
*/
const static unsigned int INIT_KEY = ((~0) - 1); int SecurityDatabaseServer::authenticate(CheckStatusWrapper* status, IServerBlock* sBlock,
static unsigned int secDbKey = INIT_KEY; IWriter* authBlock)
int SecurityDatabaseServer::authenticate(Firebird::CheckStatusWrapper* status, IServerBlock* sBlock,
IWriter* writerInterface)
{ {
status->init(); status->init();
try try
{ {
PathName secDbName; const char* user = sBlock->getLogin();
{ // config scope if (!user)
LocalStatus ls; {
CheckStatusWrapper s(&ls); HANDSHAKE_DEBUG(fprintf(stderr, "LegacyServer (nologin) %d\n", IAuth::AUTH_CONTINUE));
RefPtr<IFirebirdConf> config(REF_NO_INCR, iParameter->getFirebirdConf(&s)); return IAuth::AUTH_CONTINUE;
}
string login(user);
unsigned length;
const unsigned char* data = sBlock->getData(&length);
if (!(data && length))
{
HANDSHAKE_DEBUG(fprintf(stderr, "LegacyServer (nopw) %d\n", IAuth::AUTH_MORE_DATA));
return IAuth::AUTH_MORE_DATA;
}
bool found = false;
char pw1[MAX_LEGACY_PASSWORD_LENGTH + 1];
PathName secureDbName;
{ // reference & mutex scope scope
// Get database block from cache
RefPtr<CachedSecurityDatabase> instance;
instances->getInstance(iParameter, instance);
try
{
MutexLockGuard g(instance->mutex, FB_FUNCTION);
secureDbName = instance->secureDbName;
if (!instance->secDb)
instance->secDb = FB_NEW SecurityDatabase(instance->secureDbName);
user_name uname; // user name buffer
login.copyTo(uname, sizeof uname);
user_record user_block; // user record
found = instance->secDb->lookup(uname, &user_block);
fb_utils::copy_terminate(pw1, user_block.password, MAX_LEGACY_PASSWORD_LENGTH + 1);
}
catch(const Exception&)
{
instance->close();
throw;
}
instance->close();
}
if (!found)
{
HANDSHAKE_DEBUG(fprintf(stderr, "LegacyServer (badlogin) %d\n", IAuth::AUTH_CONTINUE));
return IAuth::AUTH_CONTINUE;
}
string storedHash(pw1, MAX_LEGACY_PASSWORD_LENGTH);
storedHash.rtrim();
storedHash.recalculate_length();
string passwordEnc;
passwordEnc.assign(data, length);
string newHash;
LegacyHash::hash(newHash, login, passwordEnc, storedHash);
if (newHash != storedHash)
{
bool legacyHash = Config::getLegacyHash();
if (legacyHash)
{
newHash.resize(MAX_LEGACY_PASSWORD_LENGTH + 2);
ENC_crypt(newHash.begin(), newHash.length(), passwordEnc.c_str(), LEGACY_PASSWORD_SALT);
newHash.recalculate_length();
newHash.erase(0, 2);
legacyHash = newHash == storedHash;
}
if (!legacyHash)
{
HANDSHAKE_DEBUG(fprintf(stderr, "LegacyServer (badpw) %d\n", IAuth::AUTH_CONTINUE));
return IAuth::AUTH_CONTINUE;
}
}
Jrd::FbLocalStatus s;
authBlock->add(&s, login.c_str());
check(&s); check(&s);
authBlock->setDb(&s, secureDbName.c_str());
if (secDbKey == INIT_KEY) check(&s);
{ HANDSHAKE_DEBUG(fprintf(stderr, "LegacyServer (OK) %d\n", IAuth::AUTH_SUCCESS));
secDbKey = config->getKey("SecurityDatabase"); return IAuth::AUTH_SUCCESS;
} }
const char* tmp = config->asString(secDbKey); catch (const Exception& ex)
if (!tmp)
{
Arg::Gds(isc_secdb_name).raise();
}
secDbName = tmp;
}
RefPtr<SecurityDatabase> instance;
{ // guard scope
MutexLockGuard g(instancesMutex, FB_FUNCTION);
InstancesArray& curInstances(instances);
for (unsigned int i = 0; i < curInstances.getCount(); ++i)
{
if (secDbName == curInstances[i]->secureDbName)
{
instance = curInstances[i];
break;
}
}
if (!instance)
{
instance = FB_NEW SecurityDatabase;
instance->addRef();
secDbName.copyTo(instance->secureDbName, sizeof(instance->secureDbName));
curInstances.add(instance);
}
}
int rc = instance->verify(writerInterface, sBlock);
#define USE_ATT_RQ_CACHE
#ifdef USE_ATT_RQ_CACHE
LocalStatus ls;
CheckStatusWrapper s(&ls);
TimerInterfacePtr()->start(&s, instance, 10 * 1000 * 1000);
if (s.getState() & IStatus::STATE_ERRORS)
instance->handler();
#else
instance->handler();
#endif
HANDSHAKE_DEBUG(fprintf(stderr, "LegacyServer: verify=%d\n", rc));
return rc;
}
catch (const Firebird::Exception& ex)
{ {
ex.stuffException(status); ex.stuffException(status);
HANDSHAKE_DEBUG(fprintf(stderr, "LegacyServer: exception status:\n")); HANDSHAKE_DEBUG(fprintf(stderr, "LegacyServer: exception status:\n"));
@ -560,12 +416,12 @@ int SecurityDatabaseServer::release()
} }
namespace { namespace {
Firebird::SimpleFactory<SecurityDatabaseServer> factory; SimpleFactory<SecurityDatabaseServer> factory;
} }
void registerLegacyServer(Firebird::IPluginManager* iPlugin) void registerLegacyServer(IPluginManager* iPlugin)
{ {
iPlugin->registerPluginFactory(Firebird::IPluginManager::TYPE_AUTH_SERVER, iPlugin->registerPluginFactory(IPluginManager::TYPE_AUTH_SERVER,
"Legacy_Auth", &factory); "Legacy_Auth", &factory);
} }

View File

@ -27,20 +27,8 @@
#ifndef AUTH_LEGACY_SERVER_H #ifndef AUTH_LEGACY_SERVER_H
#define AUTH_LEGACY_SERVER_H #define AUTH_LEGACY_SERVER_H
#include "../jrd/ibase.h"
#include "../common/utils_proto.h"
#include "../common/sha.h"
#include "gen/iberror.h"
#include "../common/classes/ClumpletWriter.h"
#include "../common/classes/ImplementHelper.h"
#include "firebird/Interface.h" #include "firebird/Interface.h"
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#include <time.h>
namespace Auth { namespace Auth {
void registerLegacyServer(Firebird::IPluginManager* iPlugin); void registerLegacyServer(Firebird::IPluginManager* iPlugin);