8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-23 06:03:02 +01:00

Fixed CORE-5082: Server does not validate correctness of user/password pair provided in EXECUTE STATEMENT operator

This commit is contained in:
alexpeshkoff 2016-01-24 17:58:28 +00:00
parent 315332944b
commit eacbf41269
11 changed files with 354 additions and 85 deletions

View File

@ -67,17 +67,17 @@ public:
getPlugin();
}
bool hasData()
bool hasData() const
{
return currentPlugin;
}
const char* name()
const char* name() const
{
return hasData() ? pluginSet->getName() : NULL;
}
P* plugin()
P* plugin() const
{
return currentPlugin;
}

View File

@ -119,4 +119,61 @@ int setGsecCode(int code, int operation)
return GsecMsg17;
}
void parseList(ParsedList& parsed, PathName list)
{
list.alltrim(" \t");
parsed.clear();
const char* sep = " \t,;";
for(;;)
{
PathName::size_type p = list.find_first_of(sep);
if (p == PathName::npos)
{
if (list.hasData())
{
parsed.push(list);
}
break;
}
parsed.push(list.substr(0, p));
list = list.substr(p + 1);
list.ltrim(" \t,;");
}
}
void makeList(PathName& list, const ParsedList& parsed)
{
fb_assert(parsed.hasData());
list = parsed[0];
for (unsigned i = 1; i < parsed.getCount(); ++i)
{
list += ' ';
list += parsed[i];
}
}
void mergeLists(PathName& list, const PathName& serverList, const PathName& clientList)
{
ParsedList onClient, onServer, merged;
parseList(onClient, clientList);
parseList(onServer, serverList);
// do not expect too long lists, therefore use double loop
for (unsigned c = 0; c < onClient.getCount(); ++c)
{
for (unsigned s = 0; s < onServer.getCount(); ++s)
{
if (onClient[c] == onServer[s])
{
merged.push(onClient[c]);
break;
}
}
}
makeList(list, merged);
}
} // namespace Auth

View File

@ -29,6 +29,7 @@
#include "../common/classes/GetPlugins.h"
#include "../common/classes/array.h"
#include "../common/classes/MetaName.h"
#include "../common/classes/objects_array.h"
namespace Auth {
@ -263,6 +264,13 @@ public:
int setGsecCode(int code, int operation);
// tools to operate lists of security-related plugins
typedef Firebird::ObjectsArray<Firebird::PathName> ParsedList;
void parseList(ParsedList& parsed, Firebird::PathName list);
void makeList(Firebird::PathName& list, const ParsedList& parsed);
void mergeLists(Firebird::PathName& list, const Firebird::PathName& serverList,
const Firebird::PathName& clientList);
} // namespace Auth
#endif // UTILITIES_SECUR_PROTO_H

View File

@ -41,7 +41,10 @@
#include "../evl_proto.h"
#include "../intl_proto.h"
#include "../mov_proto.h"
#include "../common/isc_f_proto.h"
#include "../common/db_alias.h"
#include "../common/isc_proto.h"
#include "../common/Auth.h"
using namespace Jrd;
using namespace Firebird;
@ -112,12 +115,13 @@ Connection* Manager::getConnection(thread_db* tdbb, const string& dataSource,
// dataSource : registered data source name
// or connection string : provider::database
string prvName, dbName;
string prvName;
PathName dbName;
if (dataSource.isEmpty())
{
prvName = INTERNAL_PROVIDER_NAME;
dbName = tdbb->getDatabase()->dbb_database_name.c_str();
dbName = tdbb->getDatabase()->dbb_database_name;
}
else
{
@ -125,7 +129,7 @@ Connection* Manager::getConnection(thread_db* tdbb, const string& dataSource,
if (pos != string::npos)
{
prvName = dataSource.substr(0, pos);
dbName = dataSource.substr(pos + 2);
dbName = dataSource.substr(pos + 2).ToPathName();
}
else
{
@ -134,7 +138,7 @@ Connection* Manager::getConnection(thread_db* tdbb, const string& dataSource,
// if not found - treat dataSource as Firebird's connection string
prvName = FIREBIRD_PROVIDER_NAME;
dbName = dataSource;
dbName = dataSource.ToPathName();
}
}
@ -174,7 +178,7 @@ Provider::~Provider()
clearConnections(tdbb);
}
Connection* Provider::getConnection(thread_db* tdbb, const string& dbName,
Connection* Provider::getConnection(thread_db* tdbb, const PathName& dbName,
const string& user, const string& pwd, const string& role, TraScope tra_scope)
{
const Jrd::Attachment* attachment = tdbb->getAttachment();
@ -311,7 +315,7 @@ Connection::~Connection()
}
void Connection::generateDPB(thread_db* tdbb, ClumpletWriter& dpb,
const string& user, const string& pwd, const string& role) const
const string& user, const string& pwd, const string& role, const PathName& file) const
{
dpb.reset(isc_dpb_version1);
@ -334,6 +338,8 @@ void Connection::generateDPB(thread_db* tdbb, ClumpletWriter& dpb,
if (!role.isEmpty()) {
dpb.insertString(isc_dpb_sql_role_name, role);
}
validatePassword(tdbb, file, dpb);
}
CharSet* const cs = INTL_charset_lookup(tdbb, attachment->att_charset);
@ -344,14 +350,14 @@ void Connection::generateDPB(thread_db* tdbb, ClumpletWriter& dpb,
// remote network address???
}
bool Connection::isSameDatabase(thread_db* tdbb, const string& dbName,
bool Connection::isSameDatabase(thread_db* tdbb, const PathName& dbName,
const string& user, const string& pwd, const string& role) const
{
if (m_dbName != dbName)
return false;
ClumpletWriter dpb(ClumpletReader::dpbList, MAX_DPB_SIZE);
generateDPB(tdbb, dpb, user, pwd, role);
generateDPB(tdbb, dpb, user, pwd, role, dbName);
return m_dpb.simpleCompare(dpb);
}
@ -1631,4 +1637,245 @@ EngineCallbackGuard::~EngineCallbackGuard()
}
}
namespace {
class DummyCryptKey FB_FINAL :
public Firebird::AutoIface<Firebird::ICryptKeyImpl<DummyCryptKey, Firebird::CheckStatusWrapper> >
{
public:
// ICryptKey implementation
void setSymmetric(Firebird::CheckStatusWrapper* status, const char* type, unsigned keyLength, const void* key)
{ }
void setAsymmetric(Firebird::CheckStatusWrapper* status, const char* type, unsigned encryptKeyLength,
const void* encryptKey, unsigned decryptKeyLength, const void* decryptKey)
{ }
const void* getEncryptKey(unsigned* length)
{
*length = 0;
return NULL;
}
const void* getDecryptKey(unsigned* length)
{
*length = 0;
return NULL;
}
};
class SBlock;
class CBlock FB_FINAL : public RefCntIface<IClientBlockImpl<CBlock, CheckStatusWrapper> >
{
public:
CBlock(const string& p_login, const string& p_password)
: login(p_login), password(p_password)
{ }
// Firebird::IClientBlock implementation
int release()
{
return 1;
}
const char* getLogin()
{
return login.c_str();
}
const char* getPassword()
{
return password.c_str();
}
const unsigned char* getData(unsigned int* length)
{
*length = data.getCount();
return data.begin();
}
void putData(CheckStatusWrapper* status, unsigned int length, const void* d);
Firebird::ICryptKey* newKey(Firebird::CheckStatusWrapper* status)
{
return &dummyCryptKey;
}
private:
const string& login;
const string& password;
DummyCryptKey dummyCryptKey;
UCharBuffer data;
friend class SBlock;
SBlock* sBlock;
};
class SBlock FB_FINAL : public AutoIface<IServerBlockImpl<SBlock, CheckStatusWrapper> >
{
public:
explicit SBlock(CBlock* par)
: cBlock(par)
{
cBlock->sBlock = this;
}
// Firebird::IServerBlock implementation
const char* getLogin()
{
return cBlock->getLogin();
}
const unsigned char* getData(unsigned int* length)
{
*length = data.getCount();
return data.begin();
}
void putData(CheckStatusWrapper* status, unsigned int length, const void* d);
Firebird::ICryptKey* newKey(Firebird::CheckStatusWrapper* status)
{
return &dummyCryptKey;
}
private:
DummyCryptKey dummyCryptKey;
UCharBuffer data;
friend class CBlock;
CBlock* cBlock;
};
void CBlock::putData(CheckStatusWrapper* status, unsigned int length, const void* d)
{
void* to = sBlock->data.getBuffer(length);
memcpy(to, d, length);
}
void SBlock::putData(CheckStatusWrapper* status, unsigned int length, const void* d)
{
void* to = cBlock->data.getBuffer(length);
memcpy(to, d, length);
}
}
void Connection::validatePassword(thread_db* tdbb, const PathName& file, ClumpletWriter& dpb)
{
// Peliminary checks - should we really validate password ourself
if (!dpb.find(isc_dpb_user_name)) // check for user name presence
return;
if (ISC_check_if_remote(file, false)) // check for remote connection
return;
UserId* usr = tdbb->getAttachment()->att_user;
if (!usr->usr_auth_block.hasData()) // check for embedded attachment
return;
Arg::Gds loginError(isc_login_error);
// Build list of client/server plugins
RefPtr<Config> config;
PathName list;
expandDatabaseName(file, list /* usused value */, &config);
PathName serverList = config->getPlugins(IPluginManager::TYPE_AUTH_SERVER);
PathName clientList = config->getPlugins(IPluginManager::TYPE_AUTH_CLIENT);
Auth::mergeLists(list, serverList, clientList);
if (!list.hasData())
{
Arg::Gds noPlugins(isc_random);
noPlugins << "No matching client/server authentication plugins configured for execute statement in embedded datasource";
iscLogStatus(NULL, noPlugins.value());
#ifdef DEV_BUILD
loginError << noPlugins;
#endif
loginError.raise();
}
// Analyze DPB
string login;
if (dpb.find(isc_dpb_user_name))
{
dpb.getString(login);
fb_utils::dpbItemUpper(login);
}
string password;
if (dpb.find(isc_dpb_password))
dpb.getString(password);
// Try each plugin from the merged list
for (GetPlugins<IClient> client(IPluginManager::TYPE_AUTH_CLIENT, config, list.c_str());
client.hasData(); client.next())
{
GetPlugins<IServer> server(IPluginManager::TYPE_AUTH_SERVER, config, client.name());
if (!server.hasData())
continue;
CBlock cBlock(login, password);
SBlock sBlock(&cBlock);
Auth::WriterImplementation writer;
writer.setPlugin(client.name());
const int MAXLIMIT = 100;
for (int limit = 0; limit < MAXLIMIT; ++limit) // avoid endless AUTH_MORE_DATA loop
{
LocalStatus ls;
CheckStatusWrapper s(&ls);
ISC_STATUS code = isc_login;
switch(client.plugin()->authenticate(&s, &cBlock))
{
case IAuth::AUTH_SUCCESS:
case IAuth::AUTH_MORE_DATA:
break;
case IAuth::AUTH_CONTINUE:
limit = MAXLIMIT;
continue;
case IAuth::AUTH_FAILED:
if (s.getState() & Firebird::IStatus::STATE_ERRORS)
{
iscLogStatus("Authentication failed, client plugin:", &s);
code = isc_login_error;
}
(Arg::Gds(code)
#ifdef DEV_BUILD
<< Arg::StatusVector(&s)
#endif
).raise();
break; // compiler silencer
}
switch(server.plugin()->authenticate(&s, &sBlock, &writer))
{
case IAuth::AUTH_SUCCESS:
dpb.deleteWithTag(isc_dpb_user_name);
dpb.deleteWithTag(isc_dpb_password);
writer.store(&dpb, isc_dpb_auth_block);
return;
case IAuth::AUTH_CONTINUE:
limit = MAXLIMIT;
continue;
case IAuth::AUTH_MORE_DATA:
break;
case IAuth::AUTH_FAILED:
if (s.getState() & Firebird::IStatus::STATE_ERRORS)
{
iscLogStatus("Authentication faled, server plugin:", &s);
code = isc_login_error;
}
(Arg::Gds(code)
#ifdef DEV_BUILD
<< Arg::StatusVector(&s)
#endif
).raise();
break; // compiler silencer
}
}
}
Arg::Gds(isc_login).raise();
}
} // namespace EDS

View File

@ -91,7 +91,7 @@ public:
virtual ~Provider();
// return existing or create new Connection
virtual Connection* getConnection(Jrd::thread_db* tdbb, const Firebird::string& dbName,
virtual Connection* getConnection(Jrd::thread_db* tdbb, const Firebird::PathName& dbName,
const Firebird::string& user, const Firebird::string& pwd, const Firebird::string& role,
TraScope tra_scope);
@ -155,7 +155,7 @@ public:
Provider* getProvider() { return &m_provider; }
virtual void attach(Jrd::thread_db* tdbb, const Firebird::string& dbName,
virtual void attach(Jrd::thread_db* tdbb, const Firebird::PathName& dbName,
const Firebird::string& user, const Firebird::string& pwd,
const Firebird::string& role) = 0;
virtual void detach(Jrd::thread_db* tdbb);
@ -172,7 +172,7 @@ public:
virtual bool isConnected() const = 0;
virtual bool isSameDatabase(Jrd::thread_db* tdbb, const Firebird::string& dbName,
virtual bool isSameDatabase(Jrd::thread_db* tdbb, const Firebird::PathName& dbName,
const Firebird::string& user, const Firebird::string& pwd,
const Firebird::string& role) const;
@ -186,7 +186,7 @@ public:
const Firebird::string getDataSourceName() const
{
return m_provider.getName() + "::" + m_dbName;
return m_provider.getName() + "::" + m_dbName.ToString();
}
// Get error description from provider and put it with additional context
@ -215,7 +215,9 @@ public:
protected:
void generateDPB(Jrd::thread_db* tdbb, Firebird::ClumpletWriter& dpb,
const Firebird::string& user, const Firebird::string& pwd,
const Firebird::string& role) const;
const Firebird::string& role, const Firebird::PathName& file) const;
static void validatePassword(Jrd::thread_db* tdbb, const Firebird::PathName& file,
Firebird::ClumpletWriter& dpb);
virtual Transaction* doCreateTransaction() = 0;
virtual Statement* doCreateStatement() = 0;
@ -229,7 +231,7 @@ protected:
Firebird::Mutex m_mutex;
Provider& m_provider;
Firebird::string m_dbName;
Firebird::PathName m_dbName;
Firebird::ClumpletWriter m_dpb;
Firebird::Array<Transaction*> m_transactions;

View File

@ -134,7 +134,7 @@ private:
FbStatusVector *v;
};
void InternalConnection::attach(thread_db* tdbb, const string& dbName,
void InternalConnection::attach(thread_db* tdbb, const PathName& dbName,
const string& user, const string& pwd,
const string& role)
{
@ -157,7 +157,7 @@ void InternalConnection::attach(thread_db* tdbb, const string& dbName,
{
m_isCurrent = false;
m_dbName = dbb->dbb_database_name.c_str();
generateDPB(tdbb, m_dpb, user, pwd, role);
generateDPB(tdbb, m_dpb, user, pwd, role, dbName);
FbLocalStatus status;
@ -238,7 +238,7 @@ bool InternalConnection::isAvailable(thread_db* tdbb, TraScope /*traScope*/) con
(m_isCurrent && (tdbb->getAttachment() == m_attachment->getHandle()));
}
bool InternalConnection::isSameDatabase(thread_db* tdbb, const string& dbName,
bool InternalConnection::isSameDatabase(thread_db* tdbb, const PathName& dbName,
const string& user, const string& pwd,
const string& role) const
{

View File

@ -63,7 +63,7 @@ protected:
virtual ~InternalConnection();
public:
virtual void attach(Jrd::thread_db* tdbb, const Firebird::string& dbName,
virtual void attach(Jrd::thread_db* tdbb, const Firebird::PathName& dbName,
const Firebird::string& user, const Firebird::string& pwd,
const Firebird::string& role);
@ -73,7 +73,7 @@ public:
virtual bool isConnected() const { return (m_attachment != 0); }
virtual bool isSameDatabase(Jrd::thread_db* tdbb, const Firebird::string& dbName,
virtual bool isSameDatabase(Jrd::thread_db* tdbb, const Firebird::PathName& dbName,
const Firebird::string& user, const Firebird::string& pwd,
const Firebird::string& role) const;

View File

@ -110,11 +110,11 @@ IscConnection::~IscConnection()
{
}
void IscConnection::attach(thread_db* tdbb, const string& dbName, const string& user,
void IscConnection::attach(thread_db* tdbb, const PathName& dbName, const string& user,
const string& pwd, const string& role)
{
m_dbName = dbName;
generateDPB(tdbb, m_dpb, user, pwd, role);
generateDPB(tdbb, m_dpb, user, pwd, role, dbName);
FbLocalStatus status;
{

View File

@ -510,7 +510,7 @@ protected:
public:
FB_API_HANDLE& getAPIHandle() { return m_handle; }
virtual void attach(Jrd::thread_db* tdbb, const Firebird::string& dbName,
virtual void attach(Jrd::thread_db* tdbb, const Firebird::PathName& dbName,
const Firebird::string& user, const Firebird::string& pwd,
const Firebird::string& role);

View File

@ -27,7 +27,7 @@
#include "../common/classes/fb_string.h"
#include "../common/config/config.h"
#include "../common/classes/RefCounted.h"
#include "../common/classes/objects_array.h"
#include "../common/security.h"
#include "../common/xdr.h"
#include "../remote/protocol.h"
@ -38,7 +38,7 @@ namespace Firebird
namespace Remote
{
typedef Firebird::ObjectsArray<Firebird::PathName> ParsedList;
typedef Auth::ParsedList ParsedList;
}
struct rem_port;
@ -63,12 +63,20 @@ bool_t REMOTE_getbytes (XDR*, SCHAR*, u_int);
LegacyPlugin REMOTE_legacy_auth(const char* nm, int protocol);
Firebird::RefPtr<Config> REMOTE_get_config(const Firebird::PathName* dbName,
const Firebird::string* dpb_config = NULL);
void REMOTE_parseList(Remote::ParsedList&, Firebird::PathName);
void REMOTE_makeList(Firebird::PathName& list, const Remote::ParsedList& parsed);
void REMOTE_check_response(Firebird::IStatus* warning, Rdb* rdb, PACKET* packet, bool checkKeys = false);
bool REMOTE_inflate(rem_port*, PacketReceive*, UCHAR*, SSHORT, SSHORT*);
bool REMOTE_deflate(XDR*, ProtoWrite*, PacketSend*, bool flash);
static inline void REMOTE_parseList(Remote::ParsedList& parsed, const Firebird::PathName& list)
{
Auth::parseList(parsed, list);
}
static inline void REMOTE_makeList(Firebird::PathName& list, const Remote::ParsedList& parsed)
{
Auth::makeList(list, parsed);
}
extern signed char wcCompatible[3][3];
#define HANDSHAKE_DEBUG(A)

View File

@ -986,23 +986,8 @@ void ClntAuthBlock::resetClnt(const Firebird::PathName* fileName, const CSTRING*
Firebird::PathName final;
if (serverPluginList.hasData())
{
Remote::ParsedList onClient, fromServer, merged;
REMOTE_parseList(onClient, pluginList);
REMOTE_parseList(fromServer, serverPluginList);
for (unsigned c = 0; c < onClient.getCount(); ++c)
{
// do not expect too long lists, therefore use double loop
for (unsigned s = 0; s < fromServer.getCount(); ++s)
{
if (onClient[c] == fromServer[s])
{
merged.push(onClient[c]);
}
}
}
if (merged.getCount() == 0)
Auth::mergeLists(final, serverPluginList, pluginList);
if (final.length() == 0)
{
HANDSHAKE_DEBUG(fprintf(stderr, "Cli: No matching plugins on client\n"));
(Firebird::Arg::Gds(isc_login)
@ -1011,8 +996,6 @@ void ClntAuthBlock::resetClnt(const Firebird::PathName* fileName, const CSTRING*
#endif
).raise();
}
REMOTE_makeList(final, merged);
}
else
{
@ -1051,42 +1034,6 @@ Firebird::RefPtr<Config> REMOTE_get_config(const Firebird::PathName* dbName,
return config;
}
void REMOTE_parseList(Remote::ParsedList& parsed, Firebird::PathName list)
{
list.alltrim(" \t");
parsed.clear();
const char* sep = " \t,;";
for(;;)
{
Firebird::PathName::size_type p = list.find_first_of(sep);
if (p == Firebird::PathName::npos)
{
if (list.hasData())
{
parsed.push(list);
}
break;
}
parsed.push(list.substr(0, p));
list = list.substr(p + 1);
list.ltrim(" \t,;");
}
}
void REMOTE_makeList(Firebird::PathName& list, const Remote::ParsedList& parsed)
{
fb_assert(parsed.hasData());
//list.erase();
list = parsed[0];
for (unsigned i = 1; i < parsed.getCount(); ++i)
{
list += ' ';
list += parsed[i];
}
}
void REMOTE_check_response(Firebird::IStatus* warning, Rdb* rdb, PACKET* packet, bool checkKeys)
{
/**************************************