From 7fb8ebd3a93a70b4d22304a8f96179d72aeb52f3 Mon Sep 17 00:00:00 2001 From: alexpeshkoff Date: Fri, 23 Dec 2011 12:43:58 +0000 Subject: [PATCH] Fixed CORE-1898: Increase the password length from 8 characters, CORE-3372: Simplify process of non-default security database creation --- .../linux/misc/linuxLibrary.sh.in | 35 +- .../linux/misc/postinstall.sh.in | 18 +- builds/install/misc/posixLibrary.sh.in | 34 +- builds/posix/Makefile.in | 9 +- builds/posix/make.defaults | 5 +- builds/posix/make.shared.variables | 17 +- configure.in | 35 +- lang_helpers/gds_codes.ftn | 2 + lang_helpers/gds_codes.pas | 1 + src/auth/AuthDbg.cpp | 56 +- src/auth/AuthDbg.h | 15 +- src/auth/SecureRemotePassword/BigInteger.cpp | 212 +++++ src/auth/SecureRemotePassword/BigInteger.h | 80 ++ src/auth/SecureRemotePassword/Message.h | 157 ++++ .../SecureRemotePassword/client/SrpClient.cpp | 173 +++++ .../SecureRemotePassword/client/SrpClient.h | 38 + .../manage/SrpManagement.cpp | 633 +++++++++++++++ src/auth/SecureRemotePassword/misc/prime.cpp | 48 ++ src/auth/SecureRemotePassword/misc/test.sh | 13 + .../SecureRemotePassword/misc/test_srp.cpp | 44 ++ .../SecureRemotePassword/server/SrpServer.cpp | 321 ++++++++ .../SecureRemotePassword/server/SrpServer.h | 38 + src/auth/SecureRemotePassword/srp.cpp | 221 ++++++ src/auth/SecureRemotePassword/srp.h | 126 +++ src/auth/SecurityDatabase/LegacyClient.cpp | 31 +- src/auth/SecurityDatabase/LegacyClient.h | 8 +- src/auth/SecurityDatabase/LegacyHash.h | 11 +- .../SecurityDatabase/LegacyManagement.epp | 6 +- src/auth/SecurityDatabase/LegacyManagement.h | 2 +- src/auth/SecurityDatabase/LegacyServer.cpp | 74 +- src/auth/SecurityDatabase/LegacyServer.h | 10 +- src/auth/trusted/AuthSspi.cpp | 4 +- src/auth/trusted/AuthSspi.h | 4 +- src/common/Auth.cpp | 46 -- src/common/Auth.h | 24 +- src/common/classes/ClumpletReader.cpp | 42 +- src/common/classes/ClumpletReader.h | 11 +- src/common/classes/ClumpletWriter.cpp | 6 + src/common/classes/ClumpletWriter.h | 4 +- src/common/classes/GetPlugins.h | 31 +- src/common/classes/array.h | 19 +- src/common/common.h | 4 +- src/common/config/config.cpp | 6 +- src/common/security.h | 2 +- src/common/sha.cpp | 153 ++-- src/common/sha.h | 72 +- src/common/thd.cpp | 4 +- src/common/utils.cpp | 43 ++ src/common/utils_proto.h | 8 + src/include/consts_pub.h | 10 +- .../firebird/Auth.h} | 46 +- src/include/firebird/Plugin.h | 15 +- src/include/firebird/Provider.h | 1 + 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/SysFunction.cpp | 4 +- src/jrd/UserManagement.h | 2 +- src/jrd/jrd.cpp | 5 +- src/jrd/svc.cpp | 4 +- src/jrd/tra.cpp | 1 - src/jrd/trace/TraceCmdLine.cpp | 5 +- src/jrd/trace/TraceManager.h | 2 +- src/msgs/facilities2.sql | 2 +- src/msgs/messages2.sql | 1 + src/msgs/system_errors2.sql | 1 + src/remote/client/interface.cpp | 571 ++++++++------ src/remote/inet.cpp | 74 +- src/remote/inet_proto.h | 3 +- src/remote/os/win32/wnet.cpp | 2 +- src/remote/protocol.cpp | 14 + src/remote/protocol.h | 24 +- src/remote/remot_proto.h | 24 +- src/remote/remote.cpp | 272 ++++++- src/remote/remote.h | 186 ++++- src/remote/server/os/posix/inet_server.cpp | 2 + src/remote/server/server.cpp | 728 ++++++++++++++---- src/utilities/gsec/gsec.cpp | 50 +- src/utilities/gsec/gsec_proto.h | 2 +- src/utilities/gstat/dba.epp | 1 - src/yvalve/DistributedTransaction.cpp | 1 - src/yvalve/PluginManager.cpp | 18 +- src/yvalve/PluginManager.h | 8 +- src/yvalve/why.cpp | 2 +- 86 files changed, 4210 insertions(+), 837 deletions(-) create mode 100644 src/auth/SecureRemotePassword/BigInteger.cpp create mode 100644 src/auth/SecureRemotePassword/BigInteger.h create mode 100644 src/auth/SecureRemotePassword/Message.h create mode 100644 src/auth/SecureRemotePassword/client/SrpClient.cpp create mode 100644 src/auth/SecureRemotePassword/client/SrpClient.h create mode 100644 src/auth/SecureRemotePassword/manage/SrpManagement.cpp create mode 100644 src/auth/SecureRemotePassword/misc/prime.cpp create mode 100755 src/auth/SecureRemotePassword/misc/test.sh create mode 100644 src/auth/SecureRemotePassword/misc/test_srp.cpp create mode 100644 src/auth/SecureRemotePassword/server/SrpServer.cpp create mode 100644 src/auth/SecureRemotePassword/server/SrpServer.h create mode 100644 src/auth/SecureRemotePassword/srp.cpp create mode 100644 src/auth/SecureRemotePassword/srp.h rename src/{auth/AuthInterface.h => include/firebird/Auth.h} (72%) diff --git a/builds/install/arch-specific/linux/misc/linuxLibrary.sh.in b/builds/install/arch-specific/linux/misc/linuxLibrary.sh.in index 471fccf05a..0d51249f10 100644 --- a/builds/install/arch-specific/linux/misc/linuxLibrary.sh.in +++ b/builds/install/arch-specific/linux/misc/linuxLibrary.sh.in @@ -178,35 +178,40 @@ stopSuperServerIfRunning() { } #------------------------------------------------------------------------ -# Generate new sysdba password - this routine is used only in the -# rpm file not in the install script. +# Create new password string - this routine is used only in +# silent mode of the install script. -generateNewDBAPassword() { +createNewPassword() { # openssl generates random data. openssl /dev/null 2&>/dev/null if [ $? -eq 0 ] then - # We generate 20 random chars, strip any '/''s and get the first 8 - NewPasswd=`openssl rand -base64 20 | tr -d '/' | cut -c1-8` + # We generate 40 random chars, strip any '/''s and get the first 20 + NewPasswd=`openssl rand -base64 40 | tr -d '/' | cut -c1-20` fi - # mkpasswd is a bit of a hassle, but check to see if it's there - if [ -z "$NewPasswd" ] - then - if [ -f /usr/bin/mkpasswd ] - then - NewPasswd=`/usr/bin/mkpasswd -l 8` - fi - fi + # If openssl is missing... + if [ -z "$NewPasswd" ] + then + NewPasswd=`dd if=/dev/urandom bs=10 count=1 2>/dev/null | od -x | head -n 1 | tr -d ' ' | cut -c8-27` + fi - # On some systems the mkpasswd program doesn't appear and on others - # there is another mkpasswd which does a different operation. So if + # On some systems even this routines may be missing. So if # the specific one isn't available then keep the original password. if [ -z "$NewPasswd" ] then NewPasswd="masterkey" fi + echo "$NewPasswd" +} + +#------------------------------------------------------------------------ +# Generate new sysdba password - this routine is used only in +# silent mode of the install script. + +generateNewDBAPassword() { + NewPasswd=`createNewPassword` writeNewPassword $NewPasswd } diff --git a/builds/install/arch-specific/linux/misc/postinstall.sh.in b/builds/install/arch-specific/linux/misc/postinstall.sh.in index 2c9855ae7c..fdd7470860 100644 --- a/builds/install/arch-specific/linux/misc/postinstall.sh.in +++ b/builds/install/arch-specific/linux/misc/postinstall.sh.in @@ -40,16 +40,6 @@ if [ $RunUser = firebird ]; then addFirebirdUser fi -# Create the fbmgr shell script. -if [ -x @FB_SBINDIR@/fbmgr.bin ]; then - cat > @FB_SBINDIR@/fbmgr <upgradeInterface(dpb, FB_AUTH_CLUMPLETS_VERSION, upInfo); str.erase(); @@ -86,10 +86,10 @@ Result FB_CARG DebugServer::startAuthentication(Firebird::IStatus* status, const catch (const Firebird::Exception& ex) { ex.stuffException(status); - return AUTH_FAILED; - } + } */ + return AUTH_FAILED; } - +/* Result FB_CARG DebugServer::contAuthentication(Firebird::IStatus* status, const unsigned char* data, unsigned int size, IWriter* writerInterface) { @@ -108,15 +108,7 @@ Result FB_CARG DebugServer::contAuthentication(Firebird::IStatus* status, const return AUTH_FAILED; } } - -void FB_CARG DebugServer::getData(const unsigned char** data, unsigned short* dataSize) -{ - *data = reinterpret_cast(str.c_str()); - *dataSize = str.length(); -#ifdef AUTH_VERBOSE - fprintf(stderr, "DebugServer::getData: %.*s\n", *dataSize, *data); -#endif -} + */ int FB_CARG DebugServer::release() { @@ -133,8 +125,9 @@ DebugClient::DebugClient(Firebird::IPluginConfig*) : str(getPool()) { } -Result FB_CARG DebugClient::startAuthentication(Firebird::IStatus* status, const AuthTags* tags, IClumplets* dpb) +Result FB_CARG DebugClient::authenticate(Firebird::IStatus* status, IClientBlock* cBlock) { +/* try { str = "HAND"; @@ -155,10 +148,11 @@ Result FB_CARG DebugClient::startAuthentication(Firebird::IStatus* status, const catch (const Firebird::Exception& ex) { ex.stuffException(status); - return AUTH_FAILED; } + */ + return AUTH_FAILED; } - +/* Result FB_CARG DebugClient::contAuthentication(Firebird::IStatus* status, const unsigned char* data, unsigned int size) { try @@ -180,15 +174,7 @@ Result FB_CARG DebugClient::contAuthentication(Firebird::IStatus* status, const return AUTH_FAILED; } } - -void FB_CARG DebugClient::getData(const unsigned char** data, unsigned short* dataSize) -{ - *data = reinterpret_cast(str.c_str()); - *dataSize = str.length(); -#ifdef AUTH_VERBOSE - fprintf(stderr, "DebugClient::getData: %.*s\n", *dataSize, *data); -#endif -} +*/ int FB_CARG DebugClient::release() { @@ -201,6 +187,22 @@ int FB_CARG DebugClient::release() return 1; } +Result DebugServer::getSessionKey(Firebird::IStatus*, + const unsigned char** key, unsigned int* keyLen) +{ + *key = NULL; + *keyLen = 0; + return AUTH_CONTINUE; +} + +Result DebugClient::getSessionKey(Firebird::IStatus*, + const unsigned char** key, unsigned int* keyLen) +{ + *key = NULL; + *keyLen = 0; + return AUTH_CONTINUE; +} + } // namespace Auth #endif // AUTH_DEBUG diff --git a/src/auth/AuthDbg.h b/src/auth/AuthDbg.h index 02b35f3499..ac1c62ea57 100644 --- a/src/auth/AuthDbg.h +++ b/src/auth/AuthDbg.h @@ -34,7 +34,7 @@ #ifdef AUTH_DEBUG -#include "../auth/AuthInterface.h" +#include "firebird/Auth.h" #include "../common/classes/ImplementHelper.h" #include "../common/classes/ClumpletWriter.h" #include "../common/classes/init.h" @@ -51,11 +51,10 @@ class DebugServer : public Firebird::StdPlugin public: DebugServer(Firebird::IPluginConfig*); - Result startAuthentication(Firebird::IStatus* status, const AuthTags* tags, IClumplets* dpb, + Result authenticate(Firebird::IStatus* status, IServerBlock* sBlock, IWriter* writerInterface); - Result contAuthentication(Firebird::IStatus* status, const unsigned char* data, - unsigned int size, IWriter* writerInterface); - void getData(const unsigned char** data, unsigned short* dataSize); + Result getSessionKey(Firebird::IStatus* status, + const unsigned char** key, unsigned int* keyLen); int release(); private: @@ -67,9 +66,9 @@ class DebugClient : public Firebird::StdPlugin public: DebugClient(Firebird::IPluginConfig*); - Result startAuthentication(Firebird::IStatus* status, const AuthTags* tags, IClumplets* dpb); - Result contAuthentication(Firebird::IStatus* status, const unsigned char* data, unsigned int size); - void getData(const unsigned char** data, unsigned short* dataSize); + Result authenticate(Firebird::IStatus* status, IClientBlock* sBlock); + Result getSessionKey(Firebird::IStatus* status, + const unsigned char** key, unsigned int* keyLen); int release(); private: diff --git a/src/auth/SecureRemotePassword/BigInteger.cpp b/src/auth/SecureRemotePassword/BigInteger.cpp new file mode 100644 index 0000000000..e97f37ba47 --- /dev/null +++ b/src/auth/SecureRemotePassword/BigInteger.cpp @@ -0,0 +1,212 @@ +/* + * PROGRAM: Firebird interface. + * MODULE: BigInteger.cpp + * DESCRIPTION: Integer of unlimited precision. Uses libtommath. + * + * 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. + * + * Copyright (c) 2011 Alex Peshkov + * and all contributors signed below. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + * + * + */ + +#include "firebird.h" +#include "gen/iberror.h" + +#include + +#include "../auth/SecureRemotePassword/BigInteger.h" +#include "../common/os/guid.h" + +using namespace Firebird; + +#define CHECK_MP(a) check(a, #a) + +namespace Auth +{ + + static inline void check(int rc, const char* function) + { + switch (rc) + { + case MP_OKAY: + return; + case MP_MEM: + BadAlloc::raise(); + default: + // Want fancy "Libtommath error in @1 code @2" + (Arg::Gds(isc_random) << "Libtommath error").raise(); + } + } + + BigInteger::BigInteger() + { + CHECK_MP(mp_init(&t)); + } + + BigInteger::BigInteger(const char* text, unsigned int radix) + { + CHECK_MP(mp_init(&t)); + CHECK_MP(mp_read_radix(&t, text, radix)); + } + + BigInteger::BigInteger(unsigned int count, const unsigned char* bytes) + { + CHECK_MP(mp_init(&t)); + assign(count, bytes); + } + + BigInteger::BigInteger(const Firebird::UCharBuffer& val) + { + CHECK_MP(mp_init(&t)); + assign(val.getCount(), val.begin()); + } + + BigInteger::BigInteger(const BigInteger& val) + { + CHECK_MP(mp_init_copy(const_cast(&val.t), &t)); + } + + BigInteger::~BigInteger() + { + mp_clear(&t); + } + + BigInteger& BigInteger::operator= (const BigInteger& val) + { + CHECK_MP(mp_copy(const_cast(&val.t), &t)); + return *this; + } + + void BigInteger::random(int numBytes) + { + Firebird::UCharBuffer b; + Firebird::GenerateRandomBytes(b.getBuffer(numBytes), numBytes); + assign(numBytes, b.begin()); + } + + void BigInteger::assign(unsigned int count, const unsigned char* bytes) + { + CHECK_MP(mp_read_unsigned_bin(&t, bytes, count)); + } + + BigInteger BigInteger::operator+ (const BigInteger& val) const + { + BigInteger rc; + CHECK_MP(mp_add(const_cast(&t), const_cast(&val.t), &rc.t)); + return rc; + } + + BigInteger BigInteger::operator- (const BigInteger& val) const + { + BigInteger rc; + CHECK_MP(mp_sub(const_cast(&t), const_cast(&val.t), &rc.t)); + return rc; + } + + BigInteger BigInteger::operator* (const BigInteger& val) const + { + BigInteger rc; + CHECK_MP(mp_mul(const_cast(&t), const_cast(&val.t), &rc.t)); + return rc; + } + + BigInteger BigInteger::operator/ (const BigInteger& val) const + { + BigInteger rc; + CHECK_MP(mp_div(const_cast(&t), const_cast(&val.t), &rc.t, NULL)); + return rc; + } + + BigInteger BigInteger::operator% (const BigInteger& val) const + { + BigInteger rc; + CHECK_MP(mp_mod(const_cast(&t), const_cast(&val.t), &rc.t)); + return rc; + } + + BigInteger& BigInteger::operator+= (const BigInteger& val) + { + CHECK_MP(mp_add(&t, const_cast(&val.t), &t)); + return *this; + } + + BigInteger& BigInteger::operator-= (const BigInteger& val) + { + CHECK_MP(mp_sub(&t, const_cast(&val.t), &t)); + return *this; + } + + BigInteger& BigInteger::operator*= (const BigInteger& val) + { + CHECK_MP(mp_mul(&t, const_cast(&val.t), &t)); + return *this; + } + + BigInteger& BigInteger::operator/= (const BigInteger& val) + { + CHECK_MP(mp_div(&t, const_cast(&val.t), &t, NULL)); + return *this; + } + + BigInteger& BigInteger::operator%= (const BigInteger& val) + { + CHECK_MP(mp_mod(&t, const_cast(&val.t), &t)); + return *this; + } + + bool BigInteger::operator== (const BigInteger& val) const + { + return mp_cmp(const_cast(&t), const_cast(&val.t)) == 0; + } + + void BigInteger::getBytes(Firebird::UCharBuffer& bytes) const + { + CHECK_MP(mp_to_unsigned_bin(const_cast(&t), bytes.getBuffer(length()))); + } + + unsigned int BigInteger::length() const + { + int rc = mp_unsigned_bin_size(const_cast(&t)); + if (rc < 0) + { + check(rc, "mp_unsigned_bin_size(&t)"); + } + return static_cast(rc); + } + + void BigInteger::getText(string& str, unsigned int radix) const + { + int size; + CHECK_MP(mp_radix_size(const_cast(&t), radix, &size)); + str.resize(size - 1, ' '); + CHECK_MP(mp_toradix(const_cast(&t), const_cast(str.c_str()), radix)); + } + + BigInteger BigInteger::modPow(const BigInteger& pow, const BigInteger& mod) const + { + BigInteger rc; + CHECK_MP(mp_exptmod(const_cast(&t), const_cast(&pow.t), + const_cast(&mod.t), &rc.t)); + return rc; + } + +} + +#undef CHECK_MP diff --git a/src/auth/SecureRemotePassword/BigInteger.h b/src/auth/SecureRemotePassword/BigInteger.h new file mode 100644 index 0000000000..62c74f5795 --- /dev/null +++ b/src/auth/SecureRemotePassword/BigInteger.h @@ -0,0 +1,80 @@ +/* + * PROGRAM: Firebird interface. + * MODULE: BigInteger.h + * DESCRIPTION: Integer of unlimited precision. Uses libtommath. + * + * 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. + * + * Copyright (c) 2010 Alex Peshkov + * and all contributors signed below. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + * + * + */ + +#ifndef FB_COMMON_CLASSES_BIG_INTEGER +#define FB_COMMON_CLASSES_BIG_INTEGER + +#include + +#include "../common/classes/fb_string.h" +#include "../common/classes/array.h" + +namespace Auth { + +class BigInteger +{ +public: + BigInteger(); + BigInteger(const char* text, unsigned int radix = 16u); + BigInteger(unsigned int count, const unsigned char* bytes); + BigInteger(const Firebird::UCharBuffer& val); + BigInteger(const BigInteger& val); +// BigInteger(int numBits, Random& r); + ~BigInteger(); + + BigInteger& operator= (const BigInteger& val); + void random(int numBytes); + void assign(unsigned int count, const unsigned char* bytes); + + BigInteger operator+ (const BigInteger& val) const; + BigInteger operator- (const BigInteger& val) const; + BigInteger operator* (const BigInteger& val) const; + BigInteger operator/ (const BigInteger& val) const; + BigInteger operator% (const BigInteger& val) const; + + BigInteger& operator+= (const BigInteger& val); + BigInteger& operator-= (const BigInteger& val); + BigInteger& operator*= (const BigInteger& val); + BigInteger& operator/= (const BigInteger& val); + BigInteger& operator%= (const BigInteger& val); + + bool operator== (const BigInteger& val) const; + + void getBytes(Firebird::UCharBuffer& bytes) const; + unsigned int length() const; + void getText(Firebird::string& str, unsigned int radix = 16u) const; + + BigInteger modPow(const BigInteger& pow, const BigInteger& mod) const; + +private: + mp_int t; +}; + +} // namespace Firebird + +#endif // FB_COMMON_CLASSES_BIG_INTEGER diff --git a/src/auth/SecureRemotePassword/Message.h b/src/auth/SecureRemotePassword/Message.h new file mode 100644 index 0000000000..408b909dba --- /dev/null +++ b/src/auth/SecureRemotePassword/Message.h @@ -0,0 +1,157 @@ +#include "../jrd/align.h" +#include "../common/classes/alloc.h" + +// This class helps to fill FbMessage with correct values +class Message : public Firebird::FbMessage, public Firebird::GlobalStorage +{ +public: + Message() + : blrBuf(getPool()), dataBuf(getPool()) + { + blrLength = 0; + blr = NULL; + bufferLength = 0; + buffer = NULL; + + // start message BLR + blrBuf.add(blr_version5); + blrBuf.add(blr_begin); + blrBuf.add(blr_message); + blrBuf.add(0); + countOffset = blrBuf.getCount(); + blrBuf.add(0); + blrBuf.add(0); + + varCount = 0; + } + + template + unsigned genBlr() + { + // for special types call type-specific BLR generator + // for generic types use specialization of whole call + return T::genBlr(blrBuf); + } + + template + void add(unsigned& pos, unsigned& null) + { + if (blr) + { + (Firebird::Arg::Gds(isc_random) << "This is already constructed message").raise(); + } + + // generate code for variable + unsigned align = genBlr(); + if (align) + { + bufferLength = FB_ALIGN(bufferLength, align); + } + pos = bufferLength; + bufferLength += sizeof(T); + + // generate code for null flag + blrBuf.add(blr_short); + blrBuf.add(0); + align = type_alignments[dtype_short]; + if (align) + { + bufferLength = FB_ALIGN(bufferLength, align); + } + null = bufferLength; + bufferLength += sizeof(short); + + ++varCount; + } + + void ready() + { + if (blr) + return; + + // Adjust number of variables + blrBuf[countOffset] = (varCount * 2) & 0xFF; + blrBuf[countOffset + 1] = ((varCount * 2) >> 8) & 0xFF; + + // Complete blr + blrBuf.add(blr_end); + blrBuf.add(blr_eoc); + blrLength = blrBuf.getCount(); + blr = blrBuf.begin(); + + // Allocate buffer + buffer = dataBuf.getBuffer(bufferLength); + } + + Firebird::UCharBuffer blrBuf, dataBuf; + unsigned countOffset, varCount; +}; + +template <> +unsigned Message::genBlr() +{ + blrBuf.add(blr_long); + blrBuf.add(0); // scale + return type_alignments[dtype_long]; +} + +// With template magic, we make the fields strongly-typed. +template +class Field +{ +public: + Field(Message& m) + : msg(m), pos(~0), nullPos(~0) + { + msg.add(pos, nullPos); + } + + T& operator()() + { + msg.ready(); + return *(T*) (msg.buffer + pos); + } + + short& null() + { + msg.ready(); + return *(short*) (msg.buffer + nullPos); + } + +private: + Message& msg; + unsigned pos, nullPos; +}; + +template +class VarChar +{ +public: + short len; + char data[N]; // This guarantees N > 0 + + static unsigned genBlr(Firebird::UCharBuffer& blr) + { + blr.add(blr_varying); + blr.add(N & 0xFF); + blr.add((N >> 8) & 0xFF); + return type_alignments[dtype_varying]; + } + + const VarChar& operator=(const char* str) + { + strncpy(data, str, N); + len = strlen(str); + if (len > N) + len = N; + return *this; + } + + void set(unsigned short l, void* bytes) + { + if (l > (unsigned short) N) + l = N; + memcpy(data, bytes, l); + len = l; + } +}; diff --git a/src/auth/SecureRemotePassword/client/SrpClient.cpp b/src/auth/SecureRemotePassword/client/SrpClient.cpp new file mode 100644 index 0000000000..4d99536f8c --- /dev/null +++ b/src/auth/SecureRemotePassword/client/SrpClient.cpp @@ -0,0 +1,173 @@ +/* + * PROGRAM: Firebird authentication. + * MODULE: SrpClient.cpp + * DESCRIPTION: SPR authentication plugin. + * + * 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. + * + * Copyright (c) 2011 Alex Peshkov + * and all contributors signed below. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + */ + +#include "firebird.h" + +#include "../auth/SecureRemotePassword/client/SrpClient.h" +#include "../auth/SecureRemotePassword/srp.h" +#include "../common/classes/ImplementHelper.h" + +using namespace Firebird; + +namespace Auth { + +class SrpClient : public Firebird::StdPlugin +{ +public: + explicit SrpClient(Firebird::IPluginConfig*) + : client(NULL), data(getPool()), + sessionKey(getPool()) + { } + + // IClient implementation + Result FB_CARG authenticate(Firebird::IStatus*, IClientBlock* cb); + Result FB_CARG getSessionKey(Firebird::IStatus* status, + const unsigned char** key, unsigned int* keyLen); + int FB_CARG release(); + +private: + RemotePassword* client; + string data; + UCharBuffer sessionKey; +}; + +Result SrpClient::authenticate(Firebird::IStatus* status, IClientBlock* cb) +{ + try + { + if (sessionKey.hasData()) + { + // Why are we called when auth is completed? + (Firebird::Arg::Gds(isc_random) << "Auth sync failure - SRP's authenticate called more times than supported").raise(); + } + + if (!client) + { + HANDSHAKE_DEBUG(fprintf(stderr, "Cln SRP1: %s %s\n", cb->getLogin(), cb->getPassword())); + if (!(cb->getLogin() && cb->getPassword())) + { + return AUTH_CONTINUE; + } + + client = new RemotePassword; + client->genClientKey(data); + dumpIt("Clnt: clientPubKey", data); + cb->putData(data.length(), data.begin()); + return AUTH_MORE_DATA; + } + + HANDSHAKE_DEBUG(fprintf(stderr, "Cln SRP2\n")); + unsigned int length; + const unsigned char* saltAndKey = cb->getData(&length); + if (length > (RemotePassword::SRP_SALT_SIZE + RemotePassword::SRP_KEY_SIZE + 2) * 2) + { + string msg; + msg.printf("Wrong length (%d) of data from server", length); + (Arg::Gds(isc_random) << msg).raise(); + } + + string salt, key; + unsigned charSize = *saltAndKey++; + charSize += ((unsigned)*saltAndKey++) << 8; + if (charSize > RemotePassword::SRP_SALT_SIZE * 2) + { + string msg; + msg.printf("Wrong length (%d) of salt from server", charSize); + (Arg::Gds(isc_random) << msg).raise(); + } + salt.assign(saltAndKey, charSize); + dumpIt("Clnt: salt", salt); + saltAndKey += charSize; + length -= (charSize + 2); + + charSize = *saltAndKey++; + charSize += ((unsigned)*saltAndKey++) << 8; + if (charSize + 2 != length) + { + string msg; + msg.printf("Wrong length (%d) of key from server", charSize); + (Arg::Gds(isc_random) << msg).raise(); + } + + key.assign(saltAndKey, charSize); + dumpIt("Clnt: key(srvPub)", key); + + dumpIt("Clnt: login", string(cb->getLogin())); + dumpIt("Clnt: pass", string(cb->getPassword())); + client->clientSessionKey(sessionKey, cb->getLogin(), salt.c_str(), cb->getPassword(), key.c_str()); + dumpIt("Clnt: sessionKey", sessionKey); + + BigInteger cProof = client->clientProof(cb->getLogin(), salt.c_str(), sessionKey); + cProof.getText(data); + + cb->putData(data.length(), data.c_str()); + } + catch(const Exception& ex) + { + ex.stuffException(status); + return AUTH_FAILED; + } + + return AUTH_SUCCESS; +} + + +Result SrpClient::getSessionKey(Firebird::IStatus*, + const unsigned char** key, unsigned int* keyLen) +{ + if (!sessionKey.hasData()) + { + return AUTH_MORE_DATA; + } + + *key = sessionKey.begin(); + *keyLen = sessionKey.getCount(); + + return AUTH_SUCCESS; +} + +int SrpClient::release() +{ + if (--refCounter == 0) + { + delete this; + return 0; + } + return 1; +} + +namespace +{ + Firebird::SimpleFactory factory; +} + +void registerSrpClient(Firebird::IPluginManager* iPlugin) +{ + iPlugin->registerPluginFactory(PluginType::AuthClient, RemotePassword::plugName, &factory); +} + +} // namespace Auth + diff --git a/src/auth/SecureRemotePassword/client/SrpClient.h b/src/auth/SecureRemotePassword/client/SrpClient.h new file mode 100644 index 0000000000..c217402727 --- /dev/null +++ b/src/auth/SecureRemotePassword/client/SrpClient.h @@ -0,0 +1,38 @@ +/* + * PROGRAM: Firebird authentication. + * MODULE: SrpClient.h + * DESCRIPTION: SPR authentication plugin. + * + * 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. + * + * Copyright (c) 2011 Alex Peshkov + * and all contributors signed below. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + */ + +#ifndef AUTH_SRP_CLIENT_H +#define AUTH_SRP_CLIENT_H + +#include "firebird/Auth.h" + +namespace Auth { + +void registerSrpClient(Firebird::IPluginManager* iPlugin); + +} // namespace Auth + +#endif // AUTH_SRP_CLIENT_H diff --git a/src/auth/SecureRemotePassword/manage/SrpManagement.cpp b/src/auth/SecureRemotePassword/manage/SrpManagement.cpp new file mode 100644 index 0000000000..81366dcbf8 --- /dev/null +++ b/src/auth/SecureRemotePassword/manage/SrpManagement.cpp @@ -0,0 +1,633 @@ +/* + * PROGRAM: Firebird authentication + * MODULE: SrpManagement.cpp + * DESCRIPTION: Manages security database with SRP + * + * 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. + * + * Copyright (c) 2011 Alex Peshkov + * and all contributors signed below. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + */ + +#include "firebird.h" + +#include "../common/classes/ImplementHelper.h" +#include "../common/classes/ClumpletWriter.h" +#include "../common/StatusHolder.h" +#include "firebird/Auth.h" +#include "../auth/SecureRemotePassword/srp.h" +#include "../jrd/constants.h" +#include "../utilities/gsec/gsec.h" +#include "../auth/SecureRemotePassword/Message.h" +#include "../common/classes/auto.h" + +namespace { + +Firebird::MakeUpgradeInfo<> upInfo; +const unsigned int INIT_KEY = ((~0) - 1); +unsigned int secDbKey = INIT_KEY; + +const unsigned int SZ_LOGIN = 31; +const unsigned int SZ_NAME = 31; + +template +void setField(Field >& to, Auth::ICharUserField* from) +{ + if (from->entered()) + { + to() = from->get(); + to.null() = 0; + } + else + { + to.null() = -1; + } +} + +// Domains +typedef Field > Login; +typedef Field > Verifier; +typedef Field > Salt; +typedef Field > Name; + +void allocField(Firebird::AutoPtr& field, Message& up, Auth::ICharUserField* value, Firebird::string& update, const char* name) +{ + if (value->entered() || value->specified()) + { + field = new Name(up); + update += ' '; + update += name; + update += "=?,"; + } +} + +void assignField(Firebird::AutoPtr& field, Auth::ICharUserField* name) +{ + if (field.hasData()) + { + if (name->entered()) + { + (*field)() = name->get(); + field->null() = 0; + } + else + { + fb_assert(name->specified()); + field->null() = -1; + } + } +} + +template +void listField(Auth::ICharUserField* to, Field >& from) +{ + to->set(from().data); + to->setEntered(from.null() == 0 ? 1 : 0); +} + +} + +namespace Auth { + +class SrpManagement : public Firebird::StdPlugin +{ +public: + explicit SrpManagement(Firebird::IPluginConfig* par) + : config(par->getFirebirdConf()) + { + config->release(); + } + + void prepareDataStructures() + { + const char* script[] = { +"CREATE TABLE PLG$SRP (PLG$USER_NAME SEC$USER_NAME NOT NULL PRIMARY KEY, " +"PLG$VERIFIER VARCHAR(128) CHARACTER SET OCTETS NOT NULL, " +"PLG$SALT VARCHAR(32) CHARACTER SET OCTETS NOT NULL, " +"PLG$COMMENT RDB$DESCRIPTION, PLG$FIRST SEC$NAME_PART, " +"PLG$MIDDLE SEC$NAME_PART, PLG$LAST SEC$NAME_PART)" , + +"CREATE VIEW PLG$SRP_VIEW AS " +"SELECT PLG$USER_NAME, PLG$VERIFIER, PLG$SALT, PLG$COMMENT, PLG$FIRST, PLG$MIDDLE, PLG$LAST " +"FROM PLG$SRP WHERE CURRENT_USER = 'SYSDBA' OR CURRENT_ROLE = 'RDB$ADMIN' OR CURRENT_USER = PLG$SRP.PLG$USER_NAME", + +"GRANT ALL ON PLG$SRP to VIEW PLG$SRP_VIEW", + +"GRANT SELECT ON PLG$SRP_VIEW to PUBLIC", + +"GRANT UPDATE(PLG$VERIFIER, PLG$SALT, PLG$FIRST, PLG$MIDDLE, PLG$LAST) ON PLG$SRP_VIEW TO PUBLIC", + NULL }; + + Firebird::LocalStatus s; + Firebird::RefPtr ddlTran(att->startTransaction(&s, 0, NULL)); + if (!s.isSuccess()) + { + Firebird::status_exception::raise(s.get()); + } + + try + { + for (const char** sql = script; *sql; ++sql) + { + att->execute(&s, ddlTran, 0, *sql, 3, 0, NULL, NULL); + if (!s.isSuccess()) + { + Firebird::status_exception::raise(s.get()); + } + } + + ddlTran->commit(&s); + if (!s.isSuccess()) + { + Firebird::status_exception::raise(s.get()); + } + } + catch (const Firebird::Exception&) + { + if (ddlTran) + { + ddlTran->rollback(&s); + } + throw; + } + } + + // IManagement implementation + void FB_CARG start(Firebird::IStatus* status, ILogonInfo* logonInfo) + { + try + { + Firebird::MasterInterfacePtr()->upgradeInterface(logonInfo, FB_AUTH_LOGON_INFO_VERSION, upInfo); + + status->init(); + + if (att) + { + (Firebird::Arg::Gds(isc_random) << "Database is already attached in SRP").raise(); + } + + if (secDbKey == INIT_KEY) + { + secDbKey = config->getKey("SecurityDatabase"); + } + const char* secDbName = config->asString(secDbKey); + + if (!(secDbName && secDbName[0])) + { + (Firebird::Arg::Gds(isc_random) << "Error getting security database name").raise(); + } + + Firebird::ClumpletWriter dpb(Firebird::ClumpletReader::dpbList, MAX_DPB_SIZE); + dpb.insertByte(isc_dpb_gsec_attach, TRUE); + + const unsigned char* authBlock; + unsigned int authBlockSize = logonInfo->authBlock(&authBlock); + + if (authBlockSize) + dpb.insertBytes(isc_dpb_auth_block, authBlock, authBlockSize); + else + { + const char* str = logonInfo->name(); + if (str && str[0]) + dpb.insertString(isc_dpb_trusted_auth, str, strlen(str)); + + str = logonInfo->role(); + + if (str && str[0]) + dpb.insertString(isc_dpb_sql_role_name, str, strlen(str)); + else if (logonInfo->forceAdmin()) + dpb.insertString(isc_dpb_trusted_role, ADMIN_ROLE, strlen(ADMIN_ROLE)); + } + + Firebird::RefPtr p(Firebird::MasterInterfacePtr()->getDispatcher()); + p->release(); + att = p->attachDatabase(status, secDbName, dpb.getBufferLength(), dpb.getBuffer()); + if (!status->isSuccess()) + { + Firebird::status_exception::raise(status->get()); + } + + tra = att->startTransaction(status, 0, NULL); + if (!status->isSuccess()) + { + Firebird::status_exception::raise(status->get()); + } + } + catch (const Firebird::Exception& ex) + { + ex.stuffException(status); + + if (att) + { + // detach from database + Firebird::LocalStatus lStatus; + att->detach(&lStatus); + att = NULL; + } + } + } + + int FB_CARG execute(Firebird::IStatus* status, IUser* user, IListUsers* callback) + { + try + { + Firebird::MasterInterfacePtr()->upgradeInterface(callback, FB_AUTH_LIST_USERS_VERSION, upInfo); + Firebird::MasterInterfacePtr()->upgradeInterface(user, FB_AUTH_USER_VERSION, upInfo); + + status->init(); + + fb_assert(att); + fb_assert(tra); + + switch(user->operation()) + { + case MAP_DROP_OPER: + case MAP_SET_OPER: + { + Firebird::string sql; + sql.printf("ALTER ROLE RDB$ADMIN %s AUTO ADMIN MAPPING", + user->operation() == MAP_SET_OPER ? "SET" : "DROP"); + att->execute(status, tra, sql.length(), sql.c_str(), 3, 0, NULL, NULL); + if (!status->isSuccess()) + { + Firebird::status_exception::raise(status->get()); + } + } + break; + + case ADD_OPER: + { + const char* insert = + "INSERT INTO plg$srp_view(PLG$USER_NAME, PLG$VERIFIER, PLG$SALT, PLG$FIRST, PLG$MIDDLE, PLG$LAST) " + "VALUES(?, ?, ?, ?, ?, ?)"; + + Message add; + Login login(add); + Verifier verifier(add); + Salt slt(add); + Name first(add), middle(add), last(add); + + setField(login, user->userName()); + setField(first, user->firstName()); + setField(middle, user->middleName()); + setField(last, user->lastName()); + +#if SRP_DEBUG > 1 + BigInteger salt("02E268803000000079A478A700000002D1A6979000000026E1601C000000054F"); +#else + BigInteger salt; + salt.random(RemotePassword::SRP_SALT_SIZE); +#endif + Firebird::UCharBuffer s; + salt.getBytes(s); + slt().set(s.getCount(), s.begin()); + slt.null() = 0; + + dumpIt("salt", s); +#if SRP_DEBUG > 0 + fprintf(stderr, ">%s< >%s<\n", user->userName()->get(), user->password()->get()); +#endif + Firebird::string s1; + salt.getText(s1); + server.computeVerifier(user->userName()->get(), s1, user->password()->get()).getBytes(s); + dumpIt("verifier", s); + verifier().set(s.getCount(), s.begin()); + verifier.null() = 0; + + for (unsigned repeat = 0; ; ++repeat) + { + att->execute(status, tra, 0, insert, 3, 0, &add, NULL); + if (status->isSuccess() || repeat > 0) + { + break; + } + + const ISC_STATUS* v = status->get(); + while (v[0] == isc_arg_gds) + { + if (v[1] == isc_dsql_relation_err) + { + prepareDataStructures(); + tra->commit(status); + if (!status->isSuccess()) + { + Firebird::status_exception::raise(status->get()); + } + tra = att->startTransaction(status, 0, NULL); + if (!status->isSuccess()) + { + Firebird::status_exception::raise(status->get()); + } + break; + } + do + { + v += 2; + } while (v[0] != isc_arg_warning && v[0] != isc_arg_gds && v[0] != isc_arg_end); + } + } + if (!status->isSuccess()) + { + Firebird::status_exception::raise(status->get()); + } + } + break; + + case MOD_OPER: + { + Message up; + + Firebird::string update = "UPDATE plg$srp_view SET "; + + Firebird::AutoPtr verifier; + Firebird::AutoPtr slt; + if (user->password()->entered()) + { + verifier = new Verifier(up); + slt = new Salt(up); + update += "PLG$VERIFIER=?,PLG$SALT=?,"; + } + + Firebird::AutoPtr first, middle, last; + allocField(first, up, user->firstName(), update, "PLG$FIRST"); + allocField(middle, up, user->middleName(), update, "PLG$MIDDLE"); + allocField(last, up, user->lastName(), update, "PLG$LAST"); + + if (update[update.length() - 1] != ',') + { + return 0; + } + update.rtrim(","); + + update += " WHERE PLG$USER_NAME=?"; + Login login(up); + + if (verifier.hasData()) + { +#if SRP_DEBUG > 1 + BigInteger salt("02E268803000000079A478A700000002D1A6979000000026E1601C000000054F"); +#else + BigInteger salt; + salt.random(RemotePassword::SRP_SALT_SIZE); +#endif + Firebird::UCharBuffer s; + salt.getBytes(s); + (*slt)().set(s.getCount(), s.begin()); + slt->null() = 0; + + dumpIt("salt", s); +#if SRP_DEBUG > 0 + fprintf(stderr, ">%s< >%s<\n", user->userName()->get(), user->password()->get()); +#endif + Firebird::string s1; + salt.getText(s1); + server.computeVerifier(user->userName()->get(), s1, user->password()->get()).getBytes(s); + dumpIt("verifier", s); + (*verifier)().set(s.getCount(), s.begin()); + verifier->null() = 0; + } + + assignField(first, user->firstName()); + assignField(middle, user->middleName()); + assignField(last, user->lastName()); + setField(login, user->userName()); + + att->execute(status, tra, 0, update.c_str(), 3, 0, &up, NULL); + if (!status->isSuccess()) + { + Firebird::status_exception::raise(status->get()); + } + } + break; + + case DEL_OPER: + { + Message dl; + const char* del = "DELETE FROM plg$srp_view WHERE PLG$USER_NAME=?"; + Login login(dl); + setField(login, user->userName()); + + att->execute(status, tra, 0, del, 3, 0, &dl, NULL); + if (!status->isSuccess()) + { + Firebird::status_exception::raise(status->get()); + } + } + break; + + case OLD_DIS_OPER: + case DIS_OPER: + { + user->uid()->setEntered(0); + user->gid()->setEntered(0); + user->groupName()->setEntered(0); + user->password()->setEntered(0); + + Message di; + Firebird::string disp = "SELECT PLG$USER_NAME, PLG$FIRST, PLG$MIDDLE, PLG$LAST, " + " CASE WHEN RDB$RELATION_NAME IS NULL THEN 0 ELSE 1 END " + "FROM PLG$SRP_VIEW LEFT JOIN RDB$USER_PRIVILEGES " + " ON PLG$SRP_VIEW.PLG$USER_NAME = RDB$USER_PRIVILEGES.RDB$USER " + " AND RDB$RELATION_NAME = 'RDB$ADMIN' " + " AND RDB$PRIVILEGE = 'M' "; + Firebird::AutoPtr par; + if (user->userName()->entered()) + { + par = new Message; + Login login(*par); + setField(login, user->userName()); + disp += " WHERE PLG$USER_NAME = ?"; + } + + printf("disp=%s\n", disp.c_str()); + + Login login(di); + Name first(di), middle(di), last(di); + Field admin(di); + di.ready(); + + Firebird::RefPtr stmt; + try + { + stmt = att->allocateStatement(status); + if (!status->isSuccess()) + { + Firebird::status_exception::raise(status->get()); + } + stmt->prepare(status, tra, disp.length(), disp.c_str(), 3, + Firebird::IStatement::PREPARE_PREFETCH_NONE); + if (!status->isSuccess()) + { + Firebird::status_exception::raise(status->get()); + } + stmt->execute(status, tra, 0, par, NULL); + if (!status->isSuccess()) + { + Firebird::status_exception::raise(status->get()); + } + while (stmt->fetch(status, &di) == 0) + { + if (!status->isSuccess()) + { + Firebird::status_exception::raise(status->get()); + } + + listField(user->userName(), login); + listField(user->firstName(), first); + listField(user->middleName(), middle); + listField(user->lastName(), last); + user->admin()->set(admin()); + + callback->list(user); + } + if (!status->isSuccess()) + { + Firebird::status_exception::raise(status->get()); + } + + stmt->free(status, DSQL_drop); + if (!status->isSuccess()) + { + Firebird::status_exception::raise(status->get()); + } + } + catch(const Firebird::Exception&) + { + printf("Exception\n"); + if (stmt.hasData()) + { + stmt->release(); + } + throw; + } + } + break; + + default: + return -1; + } + } + catch (const Firebird::Exception& ex) + { + ex.stuffException(status); + return -1; + +/* + switch(user->operation()) + { + case ADD_OPER: + return GsecMsg19; + + case MOD_OPER: + return GsecMsg20; + + case DEL_OPER: + return GsecMsg23; + + case OLD_DIS_OPER: + case DIS_OPER: + return GsecMsg28; + + case MAP_DROP_OPER: + case MAP_SET_OPER: + return GsecMsg97; + + default: + return GsecMsg17; + } + */ + } + + return 0; + } + + void FB_CARG commit(Firebird::IStatus* status) + { + if (tra) + { + tra->commit(status); + if (status->isSuccess()) + { + tra = NULL; + } + } + } + + void FB_CARG rollback(Firebird::IStatus* status) + { + if (tra) + { + tra->rollback(status); + if (status->isSuccess()) + { + tra = NULL; + } + } + } + + int FB_CARG release() + { + if (--refCounter == 0) + { + Firebird::LocalStatus status; + rollback(&status); + if (att) + { + att->detach(&status); + if (status.isSuccess()) + { + att = NULL; + } + } + + if (tra) + { + tra->release(); + } + + if (att) + { + att->release(); + } + + delete this; + return 0; + } + + return 1; + } + +private: + Firebird::RefPtr config; + Firebird::RefPtr att; + Firebird::RefPtr tra; + RemotePassword server; +}; + +// register plugin +static Firebird::SimpleFactory factory; + +} // namespace Auth + +extern "C" void FB_PLUGIN_ENTRY_POINT(Firebird::IMaster* master) +{ + Firebird::PluginManagerInterfacePtr pi(master); + + pi->registerPluginFactory(Firebird::PluginType::AuthUserManagement, Auth::RemotePassword::plugName, &Auth::factory); + Firebird::myModule->registerMe(); +} diff --git a/src/auth/SecureRemotePassword/misc/prime.cpp b/src/auth/SecureRemotePassword/misc/prime.cpp new file mode 100644 index 0000000000..0d62842ae1 --- /dev/null +++ b/src/auth/SecureRemotePassword/misc/prime.cpp @@ -0,0 +1,48 @@ +#include +#include +#include +#include +#include + +int cb(unsigned char* dst, int len, void*) +{ + static int f = -1; + if (f < 0) + { + f = open("/dev/urandom", 0); + if (f < 0) + { + perror("open /dev/urandom"); + exit(1); + } + } + int rc = read(f, dst, len); + if (rc < 0) + { + perror("read /dev/urandom"); + exit(1); + } + if (rc != len) + { + printf("Read %d from /dev/urandom instead %d\n", rc, len); + exit(1); + } + return rc; +} + +main() +{ + mp_int prime; + assert(mp_init(&prime) == 0); + int rc = mp_prime_random_ex(&prime, 256, 1024, LTM_PRIME_SAFE, cb, NULL); + assert(rc == 0); + + int size; + assert(mp_radix_size(&prime, 16, &size) == 0); + + char* rad = new char[size + 1]; + assert(mp_toradix(&prime, rad, 16) == 0); + rad[size] = 0; + + printf("%s\n", rad); +} diff --git a/src/auth/SecureRemotePassword/misc/test.sh b/src/auth/SecureRemotePassword/misc/test.sh new file mode 100755 index 0000000000..40fb2f7a67 --- /dev/null +++ b/src/auth/SecureRemotePassword/misc/test.sh @@ -0,0 +1,13 @@ +#!/bin/sh +compile() +{ + file=test_${1} + g++ -I../../../include -DLINUX -DAMD64 -DDEV_BUILD ${file}.cpp \ + ../../../../temp/Debug/auth/SecureRemotePassword/srp.o \ + ../../../../temp/Debug/auth/SecureRemotePassword/BigInteger.o \ + ../../../../temp/Debug/common.a -ltommath ../../../../gen/Debug/firebird/lib/libfbclient.so \ + -Wl,-rpath,../../../../gen/Debug/firebird/lib -lrt -o ${file} && ./${file} +} + +compile srp +read x diff --git a/src/auth/SecureRemotePassword/misc/test_srp.cpp b/src/auth/SecureRemotePassword/misc/test_srp.cpp new file mode 100644 index 0000000000..2996c6686c --- /dev/null +++ b/src/auth/SecureRemotePassword/misc/test_srp.cpp @@ -0,0 +1,44 @@ +#include "../auth/SecureRemotePassword/srp.h" + +using namespace Auth; + +int main(int argc, char** argv) +{ + Firebird::string salt; +#if SRP_DEBUG > 1 + BigInteger s("02E268803000000079A478A700000002D1A6979000000026E1601C000000054F"); +#else + BigInteger s; + s.random(128); +#endif + s.getText(salt); + + RemotePassword* server = new RemotePassword(); + RemotePassword* client = new RemotePassword(); + + const char* account = "SYSDBA"; + const char* password = "masterkey"; + + Firebird::UCharBuffer verifier; + dumpIt("salt", salt); +#if SRP_DEBUG > 0 + fprintf(stderr, "%s %s\n", account, password); +#endif + server->computeVerifier(account, salt, password).getBytes(verifier); + dumpIt("verifier", verifier); + + Firebird::string clientPubKey, serverPubKey; + client->genClientKey(clientPubKey); + fprintf(stderr, "C Pub %d\n", clientPubKey.length()); + server->genServerKey(serverPubKey, verifier); + fprintf(stderr, "S Pub %d\n", serverPubKey.length()); + + Firebird::UCharBuffer key1, key2; + client->clientSessionKey(key1, account, salt.c_str(), argc > 1 ? argv[1] : password, serverPubKey.c_str()); + server->serverSessionKey(key2, clientPubKey.c_str(), verifier); + + BigInteger cProof = client->clientProof(account, salt.c_str(), key1); + BigInteger sProof = server->clientProof(account, salt.c_str(), key2); + + printf("%s\n", cProof == sProof ? "OK" : "differ"); +} diff --git a/src/auth/SecureRemotePassword/server/SrpServer.cpp b/src/auth/SecureRemotePassword/server/SrpServer.cpp new file mode 100644 index 0000000000..43a14017a5 --- /dev/null +++ b/src/auth/SecureRemotePassword/server/SrpServer.cpp @@ -0,0 +1,321 @@ +/* + * PROGRAM: Firebird authentication. + * MODULE: SrpServer.cpp + * DESCRIPTION: SPR authentication plugin. + * + * 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. + * + * Copyright (c) 2011 Alex Peshkov + * and all contributors signed below. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + */ + +#include "firebird.h" + +#include "../auth/SecureRemotePassword/client/SrpClient.h" +#include "../auth/SecureRemotePassword/srp.h" +#include "../common/classes/ImplementHelper.h" +#include "../common/classes/ClumpletWriter.h" +#include "../auth/SecureRemotePassword/Message.h" + +using namespace Firebird; + +namespace { + +const unsigned int INIT_KEY = ((~0) - 1); +unsigned int secDbKey = INIT_KEY; + +const unsigned int SZ_LOGIN = 31; + +MakeUpgradeInfo<> upInfo; + +} + + +namespace Auth { + +class SrpServer : public StdPlugin +{ +public: + explicit SrpServer(IPluginConfig* par) + : server(NULL), data(getPool()), account(getPool()), + clientPubKey(getPool()), serverPubKey(getPool()), + verifier(getPool()), salt(getPool()), sessionKey(getPool()), + config(par->getFirebirdConf()), secDbName(NULL) + { + config->release(); + } + + // IServer implementation + Result FB_CARG authenticate(IStatus* status, IServerBlock* sBlock, IWriter* writerInterface); + Result FB_CARG getSessionKey(Firebird::IStatus* status, + const unsigned char** key, unsigned int* keyLen); + int FB_CARG release(); + +private: + RemotePassword* server; + string data; + string account; + string clientPubKey, serverPubKey; + UCharBuffer verifier; + string salt; + UCharBuffer sessionKey; + RefPtr config; + const char *secDbName; +}; + +Result SrpServer::authenticate(IStatus* status, IServerBlock* sb, IWriter* writerInterface) +{ + RefPtr att; + RefPtr tra; + RefPtr stmt; + + try + { + if (!server) + { + HANDSHAKE_DEBUG(fprintf(stderr, "Srv SRP1\n")); + + if (!sb->getLogin()) + { + return AUTH_CONTINUE; + } + + account = sb->getLogin(); + account.upper(); + + unsigned int length; + const unsigned char* val = sb->getData(&length); + clientPubKey.assign(val, length); + + if (!clientPubKey.hasData()) + { + return AUTH_CONTINUE; + } + + // read salt and verifier from database + // obviously we need something like attachments cache here + if (secDbKey == INIT_KEY) + { + secDbKey = config->getKey("SecurityDatabase"); + } + + secDbName = config->asString(secDbKey); + if (!(secDbName && secDbName[0])) + { + (Arg::Gds(isc_random) << "Error getting security database name").raise(); + } + + ClumpletWriter dpb(ClumpletReader::dpbList, MAX_DPB_SIZE); + dpb.insertByte(isc_dpb_sec_attach, TRUE); + const char* str = "SYSDBA"; + dpb.insertString(isc_dpb_user_name, str, strlen(str)); + + RefPtr p(MasterInterfacePtr()->getDispatcher()); + p->release(); + + att = p->attachDatabase(status, secDbName, dpb.getBufferLength(), dpb.getBuffer()); + if (!status->isSuccess()) + { + status_exception::raise(status->get()); + } + HANDSHAKE_DEBUG(fprintf(stderr, "Srv SRP1: 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); + if (!status->isSuccess()) + { + status_exception::raise(status->get()); + } + HANDSHAKE_DEBUG(fprintf(stderr, "Srv SRP1: started transaction\n")); + + stmt = att->allocateStatement(status); + if (!status->isSuccess()) + { + status_exception::raise(status->get()); + } + + const char* sql = "SELECT PLG$VERIFIER, PLG$SALT FROM PLG$SRP WHERE PLG$USER_NAME = ?"; + stmt->prepare(status, tra, 0, sql, 3, 0); + if (!status->isSuccess()) + { + const ISC_STATUS* v = status->get(); + while (v[0] == isc_arg_gds) + { + if (v[1] == isc_dsql_relation_err) + { + Arg::Gds(isc_missing_data_structures).raise(); + } + do + { + v += 2; + } while (v[0] != isc_arg_warning && v[0] != isc_arg_gds && v[0] != isc_arg_end); + } + status_exception::raise(status->get()); + } + + Message par; + Field > login(par); + login() = account.c_str(); + login.null() = 0; + HANDSHAKE_DEBUG(fprintf(stderr, "Srv SRP1: Ready to run statement with login '%s'\n", account.c_str())); + + Message dat; + Field > verify(dat); + Field > slt(dat); + dat.ready(); + + stmt->execute(status, tra, 0, &par, &dat); + if (!status->isSuccess()) + { + status_exception::raise(status->get()); + } + HANDSHAKE_DEBUG(fprintf(stderr, "Srv SRP1: Executed statement\n")); + + stmt->free(status, DSQL_drop); + if (!status->isSuccess()) + { + status_exception::raise(status->get()); + } + stmt = NULL; + + tra->rollback(status); + if (!status->isSuccess()) + { + status_exception::raise(status->get()); + } + tra = NULL; + + att->detach(status); // It will close statement + if (!status->isSuccess()) + { + status_exception::raise(status->get()); + } + att = NULL; + + server = new RemotePassword; + + verifier.assign(reinterpret_cast(verify().data), RemotePassword::SRP_VERIFIER_SIZE); + dumpIt("Srv: verifier", verifier); + UCharBuffer s; + s.assign(reinterpret_cast(slt().data), RemotePassword::SRP_SALT_SIZE); + BigInteger(s).getText(salt); + dumpIt("Srv: salt", salt); + server->genServerKey(serverPubKey, verifier); + + // Ready to prepare data for client and calculate session key + data = ""; + fb_assert(salt.length() <= RemotePassword::SRP_SALT_SIZE * 2); + data += char(salt.length()); + data += char(salt.length() >> 8); + data.append(salt); + fb_assert(serverPubKey.length() <= RemotePassword::SRP_KEY_SIZE * 2); + data += char(serverPubKey.length()); + data += char(serverPubKey.length() >> 8); + data.append(serverPubKey); + dumpIt("Srv: serverPubKey", serverPubKey); + dumpIt("Srv: data", data); + sb->putData(data.length(), data.c_str()); + + // Will be used to produce ISessionKey + dumpIt("Srv: clientPubKey", clientPubKey); + server->serverSessionKey(sessionKey, clientPubKey.c_str(), verifier); + dumpIt("Srv: sessionKey", sessionKey); + + return AUTH_MORE_DATA; + } + + unsigned int length; + const unsigned char* val = sb->getData(&length); + HANDSHAKE_DEBUG(fprintf(stderr, "Srv SRP2, data length is %d\n", length)); + string proof; + proof.assign(val, length); + BigInteger clientProof(proof.c_str()); + BigInteger serverProof = server->clientProof(account.c_str(), salt.c_str(), sessionKey); + if (clientProof == serverProof) + { + MasterInterfacePtr()->upgradeInterface(writerInterface, FB_AUTH_WRITER_VERSION, upInfo); + writerInterface->add(account.c_str()); + writerInterface->setAttribute(AuthReader::AUTH_SECURE_DB, secDbName); + return AUTH_SUCCESS; + } + } + catch (const Exception& ex) + { + if (stmt) + { + stmt->free(status, DSQL_drop); + } + if (tra) + { + tra->rollback(status); + } + if (att) + { + att->detach(status); + } + + status->init(); + ex.stuffException(status); + } + + return AUTH_FAILED; +} + +Result SrpServer::getSessionKey(Firebird::IStatus*, + const unsigned char** key, unsigned int* keyLen) +{ + if (!sessionKey.hasData()) + { + return AUTH_MORE_DATA; + } + + *key = sessionKey.begin(); + *keyLen = sessionKey.getCount(); + + return AUTH_SUCCESS; +} + +int SrpServer::release() +{ + if (--refCounter == 0) + { + delete this; + return 0; + } + return 1; +} + +namespace +{ + SimpleFactory factory; +} + +void registerSrpServer(IPluginManager* iPlugin) +{ + iPlugin->registerPluginFactory(PluginType::AuthServer, RemotePassword::plugName, &factory); +} + +} // namespace Auth + diff --git a/src/auth/SecureRemotePassword/server/SrpServer.h b/src/auth/SecureRemotePassword/server/SrpServer.h new file mode 100644 index 0000000000..43df678c9a --- /dev/null +++ b/src/auth/SecureRemotePassword/server/SrpServer.h @@ -0,0 +1,38 @@ +/* + * PROGRAM: Firebird authentication. + * MODULE: SrpServer.h + * DESCRIPTION: SPR authentication plugin. + * + * 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. + * + * Copyright (c) 2011 Alex Peshkov + * and all contributors signed below. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + */ + +#ifndef AUTH_SRP_SERVER_H +#define AUTH_SRP_SERVER_H + +#include "firebird/Auth.h" + +namespace Auth { + +void registerSrpServer(Firebird::IPluginManager* iPlugin); + +} // namespace Auth + +#endif // AUTH_SRP_SERVER_H diff --git a/src/auth/SecureRemotePassword/srp.cpp b/src/auth/SecureRemotePassword/srp.cpp new file mode 100644 index 0000000000..9fef93e723 --- /dev/null +++ b/src/auth/SecureRemotePassword/srp.cpp @@ -0,0 +1,221 @@ +#include "firebird.h" +#include "gen/iberror.h" + +#include "../auth/SecureRemotePassword/srp.h" +#include "../common/classes/alloc.h" +#include "../common/classes/ImplementHelper.h" +#include "../common/classes/init.h" +#include "../common/classes/fb_string.h" +#include "../common/sha.h" + +using namespace Firebird; + +namespace Auth { + +class RemoteGroup +{ +public: + Auth::BigInteger prime, generator, k; + +private: + RemoteGroup(const char* primeString, const char* generatorString) + : prime(primeString), generator(generatorString), k() + { + Auth::Sha1 hash; + + hash.processInt(prime); + if (prime.length() > generator.length()) + { + unsigned int pad = prime.length() - generator.length(); + char pb[1024]; + memset(pb, 0, pad); + + hash.process(pad, pb); + } + hash.processInt(generator); + + hash.getInt(k); + } + + static RemoteGroup group; + +public: + static RemoteGroup* getGroup() + { + return &group; + } +}; + +const char* primeStr = "E67D2E994B2F900C3F41F08F5BB2627ED0D49EE1FE767A52EFCD565C" + "D6E768812C3E1E9CE8F0A8BEA6CB13CD29DDEBF7A96D4A93B55D488D" + "F099A15C89DCB0640738EB2CBDD9A8F7BAB561AB1B0DC1C6CDABF303" + "264A08D1BCA932D1F1EE428B619D970F342ABA9A65793B8B2F041AE5" + "364350C16F735F56ECBCA87BD57B29E7"; +RemoteGroup RemoteGroup::group(primeStr, "02"); + +const char* RemotePassword::plugName = "Srp"; + +RemotePassword::RemotePassword() + : group(RemoteGroup::getGroup()) +{ +#if SRP_DEBUG > 1 + privateKey = BigInteger("60975527035CF2AD1989806F0407210BC81EDC04E2762A56AFD529DDDA2D4393"); +#else + privateKey.random(RemotePassword::SRP_KEY_SIZE); +#endif + privateKey %= group->prime; +} + +BigInteger RemotePassword::getUserHash(const char* account, const char* salt, const char* password) +{ + hash.reset(); + hash.process(account); + hash.process(":"); + hash.process(password); + UCharBuffer hash1; + hash.getHash(hash1); + + hash.reset(); + hash.process(salt); + hash.process(hash1); + BigInteger rc; + hash.getInt(rc); + + return rc; +} + +BigInteger RemotePassword::computeVerifier(const string& account, const string& salt, const string& password) +{ + BigInteger x(getUserHash(account.c_str(), salt.c_str(), password.c_str())); + return group->generator.modPow(x, group->prime); +} + +void RemotePassword::genClientKey(string& pubkey) +{ + dumpIt("privateKey(C)", privateKey); + clientPublicKey = group->generator.modPow(privateKey, group->prime); + clientPublicKey.getText(pubkey); + dumpIt("clientPublicKey", clientPublicKey); +} + +void RemotePassword::genServerKey(string& pubkey, const Firebird::UCharBuffer& verifier) +{ + dumpIt("privateKey(S)", privateKey); + BigInteger gb(group->generator.modPow(privateKey, group->prime)); // g^b + dumpIt("gb", gb); + BigInteger v(verifier); // v + BigInteger kv = (group->k * v) % group->prime; + dumpIt("kv", kv); + serverPublicKey = (kv + gb) % group->prime; + serverPublicKey.getText(pubkey); + dumpIt("serverPublicKey", serverPublicKey); +} + +void RemotePassword::computeScramble() +{ + hash.reset(); + dumpIt("cS: clientPublicKey", clientPublicKey); + hash.processStrippedInt(clientPublicKey); + dumpIt("cS: serverPublicKey", serverPublicKey); + hash.processStrippedInt(serverPublicKey); + hash.getInt(scramble); +} + +void RemotePassword::clientSessionKey(UCharBuffer& sessionKey, const char* account, + const char* salt, const char* password, + const char* serverPubKey) +{ + serverPublicKey = BigInteger(serverPubKey); + computeScramble(); + dumpIt("scramble", scramble); + BigInteger x = getUserHash(account, salt, password); // x + BigInteger gx = group->generator.modPow(x, group->prime); // g^x + BigInteger kgx = (group->k * gx) % group->prime; // kg^x + dumpIt("kgx", kgx); + BigInteger diff = (serverPublicKey - kgx) % group->prime; // B - kg^x + BigInteger ux = (scramble * x) % group->prime; // ux + BigInteger aux = (privateKey + ux) % group->prime; // A + ux + dumpIt("clientPrivateKey", privateKey); + dumpIt("aux", aux); + BigInteger sessionSecret = diff.modPow(aux, group->prime); // (B - kg^x) ^ (a + ux) + dumpIt("sessionSecret", sessionSecret); + + hash.reset(); + hash.processStrippedInt(sessionSecret); + hash.getHash(sessionKey); +} + +void RemotePassword::serverSessionKey(UCharBuffer& sessionKey, const char* clientPubKey, + const UCharBuffer& verifier) +{ + clientPublicKey = BigInteger(clientPubKey); + computeScramble(); + dumpIt("scramble", scramble); + BigInteger v = BigInteger(verifier); + BigInteger vu = v.modPow(scramble, group->prime); // v^u + BigInteger Avu = (clientPublicKey * vu) % group->prime; // Av^u + dumpIt("Avu", Avu); + BigInteger sessionSecret = Avu.modPow(privateKey, group->prime); // (Av^u) ^ b + dumpIt("serverPrivateKey", privateKey); + dumpIt("sessionSecret", sessionSecret); + + hash.reset(); + hash.processStrippedInt(sessionSecret); + hash.getHash(sessionKey); +} + +// H(H(prime) ^ H(g), H(I), s, A, B, K) +BigInteger RemotePassword::clientProof(const char* account, const char* salt, const UCharBuffer& sessionKey) +{ + hash.reset(); + hash.processInt(group->prime); + BigInteger n1; + hash.getInt(n1); + + hash.reset(); + hash.processInt(group->generator); + BigInteger n2; + hash.getInt(n2); + n1 = n1.modPow(n2, group->prime); + + hash.reset(); + hash.process(account); + hash.getInt(n2); + + hash.reset(); + hash.processInt(n1); // H(prime) ^ H(g) + hash.processInt(n2); // H(I) + hash.process(salt); // s + hash.processInt(clientPublicKey); // A + hash.processInt(serverPublicKey); // B + hash.process(sessionKey); // K + + BigInteger rc; + hash.getInt(rc); + return rc; +} + +#if SRP_DEBUG > 0 +void dumpIt(const char* name, const Firebird::UCharBuffer& data) +{ + fprintf(stderr, "%s\n", name); + for (size_t x=0; x0 - prints some debug info + // >1 - uses consts instead randoms, NEVER use in PRODUCTION! + +// for HANDSHAKE_DEBUG +#include "../remote/remot_proto.h" + +namespace Auth { + +/* + * Order of battle for SRP handshake: + * + * 0. At account creation, the server generates + * a random salt and computes a password + * verifier from the account name, password, + * and salt. +* + * 1. Client generates random number + * as private key, computes public + * key. + * + * 2. Client sends server the account + * name and its public key. + * 3. Server receives account name, looks up + * salt and password verifier. Server + * generates random number as private key. + * Server computes public key from private + * key, account name, verifier, and salt. + * + * 4. Server sends client public key and salt + * + * 3. Client receives server public + * key and computes session key + * from server key, salt, account + * name, and password. + * 5. Server computes session key from client + * public key, client name, and verifier + * + * For full details, see http://www.ietf.org/rfc/rfc5054.txt + * + */ + +class RemoteGroup; + +class Sha1 : public Firebird::Sha1 +{ +public: + void getInt(BigInteger& hash) + { + Firebird::UCharBuffer tmp; + getHash(tmp); + hash.assign(tmp.getCount(), tmp.begin()); + } + + void processInt(const BigInteger& data) + { + Firebird::UCharBuffer bytes; + data.getBytes(bytes); + process(bytes); + } + + void processStrippedInt(const BigInteger& data) + { + Firebird::UCharBuffer bytes; + data.getBytes(bytes); + unsigned int n = (bytes[0] == 0) ? 1u : 0; + process(bytes.getCount() - n, bytes.begin() + n); + } +}; + +class RemotePassword : public Firebird::GlobalStorage +{ +private: + const RemoteGroup* group; + Auth::Sha1 hash; + BigInteger privateKey; + BigInteger scramble; + +public: + BigInteger clientPublicKey; + BigInteger serverPublicKey; + +public: + RemotePassword(); + + static const char* plugName; + static const unsigned SRP_KEY_SIZE = 128; + static const unsigned SRP_VERIFIER_SIZE = SRP_KEY_SIZE; + static const unsigned SRP_SALT_SIZE = 32; + + BigInteger getUserHash(const char* account, + const char* salt, + const char* password); + BigInteger computeVerifier(const Firebird::string& account, + const Firebird::string& salt, + const Firebird::string& password); + void genClientKey(Firebird::string& clientPubKey); + void genServerKey(Firebird::string& serverPubKey, const Firebird::UCharBuffer& verifier); + void computeScramble(); + void clientSessionKey(Firebird::UCharBuffer& sessionKey, const char* account, + const char* salt, const char* password, + const char* serverPubKey); + void serverSessionKey(Firebird::UCharBuffer& sessionKey, + const char* clientPubKey, + const Firebird::UCharBuffer& verifier); + BigInteger clientProof(const char* account, + const char* salt, + const Firebird::UCharBuffer& sessionKey); +}; + + +#if SRP_DEBUG > 0 +void dumpIt(const char* name, const BigInteger& bi); +void dumpIt(const char* name, const Firebird::UCharBuffer& data); +void dumpIt(const char* name, const Firebird::string& str); +#else +void static inline dumpIt(const char* name, const BigInteger& bi) { } +void static inline dumpIt(const char* name, const Firebird::UCharBuffer& data) { } +void static inline dumpIt(const char* name, const Firebird::string& str) { } +#endif + +} // namespace Auth diff --git a/src/auth/SecurityDatabase/LegacyClient.cpp b/src/auth/SecurityDatabase/LegacyClient.cpp index e2c8656b51..6d62846f27 100644 --- a/src/auth/SecurityDatabase/LegacyClient.cpp +++ b/src/auth/SecurityDatabase/LegacyClient.cpp @@ -1,7 +1,7 @@ /* * PROGRAM: Firebird authentication * MODULE: LegacyClient.cpp - * DESCRIPTION: Performs legacy actions on DPB at client side. + * DESCRIPTION: Performs legacy actions on password at client side. * * The contents of this file are subject to the Initial * Developer's Public License Version 1.0 (the "License"); @@ -28,26 +28,33 @@ #include "firebird.h" #include "../jrd/ibase.h" #include "../auth/SecurityDatabase/LegacyClient.h" +#include "../auth/SecurityDatabase/LegacyHash.h" +#include "../common/enc_proto.h" #include "../common/classes/ImplementHelper.h" #include "../common/classes/init.h" namespace Auth { -Result SecurityDatabaseClient::startAuthentication(Firebird::IStatus*, const AuthTags* tags, IClumplets* dpb) +Result SecurityDatabaseClient::authenticate(Firebird::IStatus*, IClientBlock* cb) { - return dpb->find(isc_dpb_user_name) && - (dpb->find(isc_dpb_password) || dpb->find(isc_dpb_password_enc)) ? - AUTH_SUCCESS : AUTH_CONTINUE; + if (!(cb->getLogin() && cb->getPassword())) + { + return AUTH_CONTINUE; + } + + TEXT pwt[Auth::MAX_LEGACY_PASSWORD_LENGTH + 2]; + ENC_crypt(pwt, sizeof pwt, cb->getPassword(), Auth::LEGACY_PASSWORD_SALT); + cb->putData(strlen(&pwt[2]), &pwt[2]); + + return AUTH_SUCCESS; } -Result SecurityDatabaseClient::contAuthentication(Firebird::IStatus*, const unsigned char*, unsigned int) +Result SecurityDatabaseClient::getSessionKey(Firebird::IStatus*, + const unsigned char** key, unsigned int* keyLen) { - return AUTH_FAILED; -} - -void SecurityDatabaseClient::getData(const unsigned char**, unsigned short* dataSize) -{ - *dataSize = 0; + *key = NULL; + *keyLen = 0; + return AUTH_CONTINUE; } int SecurityDatabaseClient::release() diff --git a/src/auth/SecurityDatabase/LegacyClient.h b/src/auth/SecurityDatabase/LegacyClient.h index f6c419ec0a..4c811a37a1 100644 --- a/src/auth/SecurityDatabase/LegacyClient.h +++ b/src/auth/SecurityDatabase/LegacyClient.h @@ -27,7 +27,7 @@ #ifndef AUTH_LEGACY_CLIENT_H #define AUTH_LEGACY_CLIENT_H -#include "../auth/AuthInterface.h" +#include "firebird/Auth.h" #include "../common/classes/ImplementHelper.h" namespace Auth { @@ -43,9 +43,9 @@ public: } // IClient implementation - Result FB_CARG startAuthentication(Firebird::IStatus* status, const AuthTags* tags, IClumplets* dpb); - Result FB_CARG contAuthentication(Firebird::IStatus* status, const unsigned char* data, unsigned int size); - void FB_CARG getData(const unsigned char** data, unsigned short* dataSize); + Result FB_CARG authenticate(Firebird::IStatus*, IClientBlock* data); + Result FB_CARG getSessionKey(Firebird::IStatus* status, + const unsigned char** key, unsigned int* keyLen); int FB_CARG release(); }; diff --git a/src/auth/SecurityDatabase/LegacyHash.h b/src/auth/SecurityDatabase/LegacyHash.h index dbdf2d3926..128236d316 100644 --- a/src/auth/SecurityDatabase/LegacyHash.h +++ b/src/auth/SecurityDatabase/LegacyHash.h @@ -29,20 +29,23 @@ #include "../common/utils_proto.h" #include "../common/sha.h" +#include "../common/os/guid.h" +#include "../common/utils_proto.h" namespace Auth { -const size_t MAX_PASSWORD_LENGTH = 64; // used to store passwords internally -static const char* const PASSWORD_SALT = "9z"; // for old ENC_crypt() +const size_t MAX_LEGACY_PASSWORD_LENGTH = 64; // used to store passwords internally +static const char* const LEGACY_PASSWORD_SALT = "9z"; // for old ENC_crypt() const size_t SALT_LENGTH = 12; // measured after base64 coding class LegacyHash { public: + static void hash(Firebird::string& h, const Firebird::string& userName, const TEXT* passwd) { Firebird::string salt; - Jrd::CryptSupport::random(salt, SALT_LENGTH); + fb_utils::random64(salt, SALT_LENGTH); hash(h, userName, passwd, salt); } @@ -56,7 +59,7 @@ public: Firebird::string allData(salt); allData += userName; allData += passwd; - Jrd::CryptSupport::hash(h, allData); + Firebird::Sha1::hashBased64(h, allData); h = salt + h; } }; diff --git a/src/auth/SecurityDatabase/LegacyManagement.epp b/src/auth/SecurityDatabase/LegacyManagement.epp index be7251e000..726d2a3cf1 100644 --- a/src/auth/SecurityDatabase/LegacyManagement.epp +++ b/src/auth/SecurityDatabase/LegacyManagement.epp @@ -322,7 +322,7 @@ int FB_CARG SecurityDatabaseManagement::execute(Firebird::IStatus* st, IUser* us Firebird::MutexLockGuard guard(execLineMutex); - SCHAR encrypted1[MAX_PASSWORD_LENGTH + 2]; + SCHAR encrypted1[MAX_LEGACY_PASSWORD_LENGTH + 2]; Firebird::string encrypted2; bool found; @@ -383,7 +383,7 @@ int FB_CARG SecurityDatabaseManagement::execute(Firebird::IStatus* st, IUser* us U.PLG$GROUP_NAME.NULL = ISC_TRUE; if (user->password()->entered()) { - ENC_crypt(encrypted1, sizeof encrypted1, user->password()->get(), PASSWORD_SALT); + ENC_crypt(encrypted1, sizeof encrypted1, user->password()->get(), LEGACY_PASSWORD_SALT); LegacyHash::hash(encrypted2, user->userName()->get(), &encrypted1[2]); STR_STORE(U.PLG$PASSWD, encrypted2.c_str()); U.PLG$PASSWD.NULL = ISC_FALSE; @@ -458,7 +458,7 @@ int FB_CARG SecurityDatabaseManagement::execute(Firebird::IStatus* st, IUser* us if (user->password()->entered()) { - ENC_crypt(encrypted1, sizeof encrypted1, user->password()->get(), PASSWORD_SALT); + ENC_crypt(encrypted1, sizeof encrypted1, user->password()->get(), LEGACY_PASSWORD_SALT); LegacyHash::hash(encrypted2, user->userName()->get(), &encrypted1[2]); STR_STORE(U.PLG$PASSWD, encrypted2.c_str()); U.PLG$PASSWD.NULL = ISC_FALSE; diff --git a/src/auth/SecurityDatabase/LegacyManagement.h b/src/auth/SecurityDatabase/LegacyManagement.h index 2a44b3fd6d..7c8fd63370 100644 --- a/src/auth/SecurityDatabase/LegacyManagement.h +++ b/src/auth/SecurityDatabase/LegacyManagement.h @@ -28,7 +28,7 @@ #define AUTH_LEGACY_MANAGEMENT_H #include "../common/classes/ImplementHelper.h" -#include "../auth/AuthInterface.h" +#include "firebird/Auth.h" namespace Auth { diff --git a/src/auth/SecurityDatabase/LegacyServer.cpp b/src/auth/SecurityDatabase/LegacyServer.cpp index 133d189189..20bfff6fea 100644 --- a/src/auth/SecurityDatabase/LegacyServer.cpp +++ b/src/auth/SecurityDatabase/LegacyServer.cpp @@ -61,7 +61,7 @@ const UCHAR PWD_REQUEST[] = blr_long, 0, blr_long, 0, blr_short, 0, - blr_text, BLR_WORD(Auth::MAX_PASSWORD_LENGTH + 2), + blr_text, BLR_WORD(Auth::MAX_LEGACY_PASSWORD_LENGTH + 2), blr_message, 0, 1, 0, blr_cstring, 129, 0, blr_receive, 0, @@ -107,7 +107,7 @@ struct user_record SLONG gid; SLONG uid; SSHORT flag; - SCHAR password[Auth::MAX_PASSWORD_LENGTH + 2]; + SCHAR password[Auth::MAX_LEGACY_PASSWORD_LENGTH + 2]; }; // Transaction parameter buffer @@ -120,16 +120,6 @@ const UCHAR TPB[4] = isc_tpb_wait }; -void getString(Auth::IClumplets* dpb, UCHAR tag, string& str) -{ - if (dpb->find(tag)) - { - unsigned int len; - const UCHAR* s = dpb->get(&len); - str.assign(s, len); - } -} - } // anonymous namespace namespace Auth { @@ -137,7 +127,7 @@ namespace Auth { class SecurityDatabase : public Firebird::RefCntIface { public: - Result verify(IWriter* authBlock, IClumplets* originalDpb); + Result verify(IWriter* authBlock, IServerBlock* sBlock); static int shutdown(const int, const int, void*); @@ -247,8 +237,8 @@ bool SecurityDatabase::lookup_user(const char* user_name, char* pwd) if (pwd) { - strncpy(pwd, user.password, MAX_PASSWORD_LENGTH); - pwd[MAX_PASSWORD_LENGTH] = 0; + strncpy(pwd, user.password, MAX_LEGACY_PASSWORD_LENGTH); + pwd[MAX_LEGACY_PASSWORD_LENGTH] = 0; } } @@ -301,7 +291,7 @@ void SecurityDatabase::prepare() * Public interface */ -Result SecurityDatabase::verify(IWriter* authBlock, IClumplets* originalDpb) +Result SecurityDatabase::verify(IWriter* authBlock, IServerBlock* sBlock) { static AmCache useNative = AM_UNKNOWN; @@ -318,12 +308,16 @@ Result SecurityDatabase::verify(IWriter* authBlock, IClumplets* originalDpb) return AUTH_CONTINUE; } - string login, password, passwordEnc; - getString(originalDpb, isc_dpb_user_name, login); - getString(originalDpb, isc_dpb_password, password); - getString(originalDpb, isc_dpb_password_enc, passwordEnc); + string login(sBlock->getLogin()); + unsigned length; + const unsigned char* data = sBlock->getData(&length); + string passwordEnc; + if (data) + { + passwordEnc.assign(data, length); + } - if (login.hasData() && (password.hasData() || passwordEnc.hasData())) + if (login.hasData() && passwordEnc.hasData()) { login.upper(); @@ -331,22 +325,15 @@ Result SecurityDatabase::verify(IWriter* authBlock, IClumplets* originalDpb) // found there. This means that another database must be accessed, and // that means the current context must be saved and restored. - char pw1[MAX_PASSWORD_LENGTH + 1]; + char pw1[MAX_LEGACY_PASSWORD_LENGTH + 1]; if (!lookup_user(login.c_str(), pw1)) { return AUTH_FAILED; } - pw1[MAX_PASSWORD_LENGTH] = 0; - string storedHash(pw1, MAX_PASSWORD_LENGTH); + pw1[MAX_LEGACY_PASSWORD_LENGTH] = 0; + string storedHash(pw1, MAX_LEGACY_PASSWORD_LENGTH); storedHash.rtrim(); - if (!passwordEnc.hasData()) - { - char pwt[MAX_PASSWORD_LENGTH + 2]; - ENC_crypt(pwt, sizeof pwt, password.c_str(), PASSWORD_SALT); - passwordEnc.assign(&pwt[2]); - } - string newHash; LegacyHash::hash(newHash, login, passwordEnc, storedHash); if (newHash != storedHash) @@ -354,8 +341,8 @@ Result SecurityDatabase::verify(IWriter* authBlock, IClumplets* originalDpb) bool legacyHash = Config::getLegacyHash(); if (legacyHash) { - newHash.resize(MAX_PASSWORD_LENGTH + 2); - ENC_crypt(newHash.begin(), newHash.length(), passwordEnc.c_str(), PASSWORD_SALT); + 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; @@ -464,9 +451,7 @@ int SecurityDatabase::shutdown(const int, const int, void*) const static unsigned int INIT_KEY = ((~0) - 1); static unsigned int secDbKey = INIT_KEY; -Result SecurityDatabaseServer::startAuthentication(Firebird::IStatus* status, - const AuthTags* tags, IClumplets* dpb, - IWriter* writerInterface) +Result SecurityDatabaseServer::authenticate(Firebird::IStatus* status, IServerBlock* sBlock, IWriter* writerInterface) { status->init(); @@ -515,7 +500,7 @@ Result SecurityDatabaseServer::startAuthentication(Firebird::IStatus* status, fb_assert(instance); - Result rc = instance->verify(writerInterface, dpb); + Result rc = instance->verify(writerInterface, sBlock); TimerInterfacePtr()->start(instance, 10 * 1000 * 1000); return rc; } @@ -526,17 +511,12 @@ Result SecurityDatabaseServer::startAuthentication(Firebird::IStatus* status, } } -Result SecurityDatabaseServer::contAuthentication(Firebird::IStatus* status, - const unsigned char* /*data*/, unsigned int /*size*/, - IWriter* /*writerInterface*/) +Result SecurityDatabaseServer::getSessionKey(Firebird::IStatus*, + const unsigned char** key, unsigned int* keyLen) { - return AUTH_FAILED; -} - -void SecurityDatabaseServer::getData(const unsigned char** data, unsigned short* dataSize) -{ - *data = NULL; - *dataSize = 0; + *key = NULL; + *keyLen = 0; + return AUTH_CONTINUE; } int SecurityDatabaseServer::release() diff --git a/src/auth/SecurityDatabase/LegacyServer.h b/src/auth/SecurityDatabase/LegacyServer.h index ee972163ac..a4c88e0fcf 100644 --- a/src/auth/SecurityDatabase/LegacyServer.h +++ b/src/auth/SecurityDatabase/LegacyServer.h @@ -34,7 +34,7 @@ #include "../common/classes/ClumpletWriter.h" #include "../common/classes/ImplementHelper.h" -#include "../auth/AuthInterface.h" +#include "firebird/Auth.h" #ifdef HAVE_STDLIB_H #include @@ -51,11 +51,9 @@ public: { } // IServer implementation - Result FB_CARG startAuthentication(Firebird::IStatus* status, const AuthTags* tags, IClumplets* dpb, - IWriter* writerInterface); - Result FB_CARG contAuthentication(Firebird::IStatus* status, const unsigned char* data, - unsigned int size, IWriter* writerInterface); - void FB_CARG getData(const unsigned char** data, unsigned short* dataSize); + Result FB_CARG authenticate(Firebird::IStatus* status, IServerBlock* sBlock, IWriter* writerInterface); + Result FB_CARG getSessionKey(Firebird::IStatus* status, + const unsigned char** key, unsigned int* keyLen); int FB_CARG release(); private: diff --git a/src/auth/trusted/AuthSspi.cpp b/src/auth/trusted/AuthSspi.cpp index b78e56a125..cabd52c387 100644 --- a/src/auth/trusted/AuthSspi.cpp +++ b/src/auth/trusted/AuthSspi.cpp @@ -437,7 +437,9 @@ int WinSspiServer::release() Result WinSspiClient::startAuthentication(Firebird::IStatus* status, const AuthTags* tags, - IClumplets* dpb) + IClumplets* dpb, + const char* user, + const char* pass) { sspi.request(sspiData); diff --git a/src/auth/trusted/AuthSspi.h b/src/auth/trusted/AuthSspi.h index abce1989ca..0837315f92 100644 --- a/src/auth/trusted/AuthSspi.h +++ b/src/auth/trusted/AuthSspi.h @@ -36,7 +36,7 @@ #include <../common/classes/array.h> #include "../common/classes/ImplementHelper.h" #include <../jrd/ibase.h> -#include "../auth/AuthInterface.h" +#include "firebird/Auth.h" #define SECURITY_WIN32 @@ -119,7 +119,7 @@ class WinSspiClient : public Firebird::StdPluginfind(tag) ? 1 : 0; -} - -void DpbImplementation::add(UCHAR tag, const void* bytes, unsigned int count) -{ - body->insertBytes(tag, bytes, count); -} - -void DpbImplementation::drop() -{ - if (!body->isEof()) - { - body->deleteClumplet(); - } -} - -const unsigned char* DpbImplementation::get(unsigned int* cntPtr) -{ - if (body->isEof()) - { - return NULL; - } - - if (cntPtr) - { - *cntPtr = body->getClumpLength(); - } - - return body->getBytes(); -} - - -bool legacy(const char* nm) -{ - const char* legacyTrusted = "WIN_SSPI"; - - return fb_utils::stricmp(legacyTrusted, nm) == 0; -} - } // namespace Auth diff --git a/src/common/Auth.h b/src/common/Auth.h index 081f1807a2..1ee5c09636 100644 --- a/src/common/Auth.h +++ b/src/common/Auth.h @@ -30,7 +30,7 @@ #ifndef FB_AUTH_H #define FB_AUTH_H -#include "../auth/AuthInterface.h" +#include "firebird/Auth.h" #include "../common/classes/ClumpletWriter.h" #include "../common/classes/init.h" #include "../common/classes/array.h" @@ -39,8 +39,6 @@ namespace Auth { -bool legacy(const char* nm); - class WriterImplementation : public Firebird::AutoIface { public: @@ -62,26 +60,6 @@ private: void putLevel(); }; -class DpbImplementation : public Firebird::AutoIface -{ -public: - DpbImplementation(Firebird::ClumpletWriter& base); - - // DpbImplementation implementation - int FB_CARG find(UCHAR tag); - void FB_CARG add(UCHAR tag, const void* bytes, unsigned int count); - void FB_CARG drop(); - const unsigned char* FB_CARG get(unsigned int* cntPtr); - -private: - Firebird::ClumpletWriter* body; -}; - -static const AuthTags DB_ATTACH_LIST = {isc_dpb_auth_block, isc_dpb_trusted_auth, isc_dpb_trusted_role, 0}; -static const AuthTags SVC_ATTACH_LIST = {isc_spb_auth_block, isc_spb_trusted_auth, isc_spb_trusted_role, 1}; -static const AuthTags SVC_START_LIST = {isc_spb_auth_block, isc_spb_trusted_auth, 0, 1}; -static const AuthTags SVC_QUERY_LIST = {isc_info_svc_auth_block, 0, 0, 1}; - } // namespace Auth diff --git a/src/common/classes/ClumpletReader.cpp b/src/common/classes/ClumpletReader.cpp index 3e8e4d8636..e85f44049c 100644 --- a/src/common/classes/ClumpletReader.cpp +++ b/src/common/classes/ClumpletReader.cpp @@ -128,6 +128,13 @@ ClumpletReader::ClumpletReader(MemoryPool& pool, const ClumpletReader& from) : rewind(); // this will set cur_offset and spbState } +ClumpletReader::ClumpletReader(const ClumpletReader& from) : + AutoStorage(), kind(from.kind), + static_buffer(from.getBuffer()), static_buffer_end(from.getBufferEnd()) +{ + rewind(); // this will set cur_offset and spbState +} + ClumpletReader::ClumpletReader(MemoryPool& pool, const KindList* kl, const UCHAR* buffer, size_t buffLen, FPTR_VOID raise) : AutoStorage(pool), kind(kl->kind), static_buffer(buffer), static_buffer_end(buffer + buffLen) @@ -253,6 +260,9 @@ UCHAR ClumpletReader::getBufferTag() const return 0; } return buffer_start[1]; + case isc_spb_version3: + // This is wide SPB attach format - buffer's tag is the first byte. + return buffer_start[0]; default: invalid_structure("spb in service attach should begin with isc_spb_version1 or isc_spb_version"); return 0; @@ -300,8 +310,12 @@ ClumpletReader::ClumpletType ClumpletReader::getClumpletType(UCHAR tag) const case SpbReceiveItems: return SingleTpb; case SpbStart: - if (tag == isc_spb_auth_block || tag == isc_spb_trusted_auth) + switch(tag) { + case isc_spb_auth_block: + case isc_spb_trusted_auth: + case isc_spb_auth_plugin_name: + case isc_spb_auth_plugin_list: return Wide; } switch (spbState) @@ -609,6 +623,27 @@ bool ClumpletReader::find(UCHAR tag) return false; } +bool ClumpletReader::next(UCHAR tag) +{ + if (!isEof()) + { + const size_t co = getCurOffset(); + if (tag == getClumpTag()) + { + moveNext(); + } + for (; !isEof(); moveNext()) + { + if (tag == getClumpTag()) + { + return true; + } + } + setCurOffset(co); + } + return false; +} + // Methods which work with currently selected clumplet UCHAR ClumpletReader::getClumpTag() const { @@ -746,6 +781,11 @@ PathName& ClumpletReader::getPath(PathName& str) const return str; } +void ClumpletReader::getData(UCharBuffer& data) const +{ + data.assign(getBytes(), getClumpLength()); +} + bool ClumpletReader::getBoolean() const { const UCHAR* ptr = getBytes(); diff --git a/src/common/classes/ClumpletReader.h b/src/common/classes/ClumpletReader.h index 4d844dd5b5..f6546eb54e 100644 --- a/src/common/classes/ClumpletReader.h +++ b/src/common/classes/ClumpletReader.h @@ -83,12 +83,14 @@ public: // Create a copy of reader ClumpletReader(MemoryPool& pool, const ClumpletReader& from); + ClumpletReader(const ClumpletReader& from); // Navigation in clumplet buffer bool isEof() const { return cur_offset >= getBufferLength(); } void moveNext(); void rewind(); bool find(UCHAR tag); + bool next(UCHAR tag); // Methods which work with currently selected clumplet UCHAR getClumpTag() const; @@ -99,6 +101,7 @@ public: SINT64 getBigInt() const; string& getString(string& str) const; PathName& getPath(PathName& str) const; + void getData(UCharBuffer& data) const; const UCHAR* getBytes() const; double getDouble() const; ISC_TIMESTAMP getTimeStamp() const; @@ -164,8 +167,7 @@ protected: virtual void invalid_structure(const char* what) const; private: - // Assignment and copy constructor not implemented. - ClumpletReader(const ClumpletReader& from); + // Assignment not implemented. ClumpletReader& operator=(const ClumpletReader& from); const UCHAR* static_buffer; @@ -175,8 +177,9 @@ private: void create(const KindList* kl, size_t buffLen, FPTR_VOID raise); public: - static const KindList dpbList[]; // Some frequently used kind lists - static const KindList spbList[]; // Some frequently used kind lists + // Some frequently used kind lists + static const KindList dpbList[]; + static const KindList spbList[]; }; class AuthReader : public ClumpletReader diff --git a/src/common/classes/ClumpletWriter.cpp b/src/common/classes/ClumpletWriter.cpp index 942a68ea4c..f340113e44 100644 --- a/src/common/classes/ClumpletWriter.cpp +++ b/src/common/classes/ClumpletWriter.cpp @@ -120,6 +120,12 @@ ClumpletWriter::ClumpletWriter(MemoryPool& pool, const ClumpletWriter& from) create(from.getBuffer(), from.getBufferEnd() - from.getBuffer(), from.isTagged() ? from.getBufferTag() : 0); } +ClumpletWriter::ClumpletWriter(const ClumpletWriter& from) + : ClumpletReader(from), sizeLimit(from.sizeLimit), kindList(NULL), dynamic_buffer(getPool()) +{ + create(from.getBuffer(), from.getBufferEnd() - from.getBuffer(), from.isTagged() ? from.getBufferTag() : 0); +} + void ClumpletWriter::create(const UCHAR* buffer, size_t buffLen, UCHAR tag) { if (buffer && buffLen) { diff --git a/src/common/classes/ClumpletWriter.h b/src/common/classes/ClumpletWriter.h index 5b9b8adbb8..e853454b69 100644 --- a/src/common/classes/ClumpletWriter.h +++ b/src/common/classes/ClumpletWriter.h @@ -62,6 +62,7 @@ public: // Create a copy of writer ClumpletWriter(MemoryPool& pool, const ClumpletWriter& from); + ClumpletWriter(const ClumpletWriter& from); void reset(UCHAR tag); void reset(const UCHAR* buffer, const size_t buffLen); @@ -99,8 +100,7 @@ protected: bool upgradeVersion(); // upgrade clumplet version - obtain newest from kindList private: - // Assignment and copy constructor not implemented. - ClumpletWriter(const ClumpletWriter& from); + // Assignment not implemented. ClumpletWriter& operator=(const ClumpletWriter& from); size_t sizeLimit; diff --git a/src/common/classes/GetPlugins.h b/src/common/classes/GetPlugins.h index 68560c93be..bfba01c348 100644 --- a/src/common/classes/GetPlugins.h +++ b/src/common/classes/GetPlugins.h @@ -31,6 +31,7 @@ #include "../common/classes/ImplementHelper.h" #include "../common/config/config.h" +#include "../common/StatusHolder.h" namespace Firebird { @@ -42,11 +43,18 @@ public: GetPlugins(unsigned int interfaceType, unsigned int desiredVersion, UpgradeInfo* ui, const char* namesList = NULL) : masterInterface(), pluginInterface(masterInterface), - pluginSet(pluginInterface->getPlugins(interfaceType, - namesList ? namesList : Config::getPlugins(interfaceType), - desiredVersion, ui, NULL)), - currentPlugin(NULL) + pluginSet(NULL), currentPlugin(NULL) { + LocalStatus status; + pluginSet = pluginInterface->getPlugins(&status, interfaceType, + namesList ? namesList : Config::getPlugins(interfaceType), + desiredVersion, ui, NULL); + if (!pluginSet) + { + fb_assert(!status.isSuccess()); + status_exception::raise(status.get()); + } + pluginSet->release(); getPlugin(); } @@ -54,11 +62,18 @@ public: GetPlugins(unsigned int interfaceType, unsigned int desiredVersion, UpgradeInfo* ui, Config* knownConfig, const char* namesList = NULL) : masterInterface(), pluginInterface(masterInterface), - pluginSet(pluginInterface->getPlugins(interfaceType, - namesList ? namesList : Config::getPlugins(interfaceType), - desiredVersion, ui, new FirebirdConf(knownConfig))), - currentPlugin(NULL) + pluginSet(NULL), currentPlugin(NULL) { + LocalStatus status; + pluginSet = pluginInterface->getPlugins(&status, interfaceType, + namesList ? namesList : Config::getPlugins(interfaceType), + desiredVersion, ui, new FirebirdConf(knownConfig)); + if (!pluginSet) + { + fb_assert(!status.isSuccess()); + status_exception::raise(status.get()); + } + pluginSet->release(); getPlugin(); } diff --git a/src/common/classes/array.h b/src/common/classes/array.h index f121f1b38f..02af94f2f4 100644 --- a/src/common/classes/array.h +++ b/src/common/classes/array.h @@ -31,6 +31,7 @@ #include #include "../common/classes/vector.h" #include "../common/classes/alloc.h" +#include "../common/common.h" namespace Firebird { @@ -312,6 +313,12 @@ public: copyFrom(source); } + void assign(const T* items, const size_t itemsCount) + { + resize(itemsCount); + memcpy(data, items, sizeof(T) * count); + } + // NOTE: getCount method must be signal safe // Used as such in GlobalRWLock::blockingAstHandler size_t getCount() const { return count; } @@ -334,6 +341,16 @@ public: count += itemsSize; } + void append(const T* items, const size_t itemsSize) + { + push(items, itemsSize); + } + + void append(const Array& source) + { + push(source.begin(), source.getCount()); + } + T pop() { fb_assert(count > 0); @@ -477,7 +494,7 @@ public: Array > (InitialCapacity) {} }; -typedef HalfStaticArray UCharBuffer; +typedef HalfStaticArray UCharBuffer; } // namespace Firebird diff --git a/src/common/common.h b/src/common/common.h index 521f86f016..d02476ad84 100644 --- a/src/common/common.h +++ b/src/common/common.h @@ -867,9 +867,9 @@ void GDS_breakpoint(int); #define STRINGIZE(x) STRINGIZE_AUX(x) #ifdef _MSC_VER -#define CONST64(a) (a##i64) +#define FB_CONST64(a) (a##i64) #else -#define CONST64(a) (a##LL) +#define FB_CONST64(a) (a##LL) #endif // 30 Dec 2002. Nickolay Samofatov diff --git a/src/common/config/config.cpp b/src/common/config/config.cpp index a805af2f92..1d8969f932 100644 --- a/src/common/config/config.cpp +++ b/src/common/config/config.cpp @@ -164,9 +164,9 @@ const Config::ConfigEntry Config::entries[MAX_CONFIG_KEY] = {TYPE_INTEGER, "MaxUserTraceLogSize", (ConfigValue) 10}, // maximum size of user session trace log {TYPE_INTEGER, "FileSystemCacheSize", (ConfigValue) 30}, // percent {TYPE_STRING, "Providers", (ConfigValue) "Remote, Engine12, Loopback"}, - {TYPE_STRING, "AuthServer", (ConfigValue) "Legacy_Auth, Win_Sspi"}, - {TYPE_STRING, "AuthClient", (ConfigValue) "Legacy_Auth, Win_Sspi"}, - {TYPE_STRING, "UserManager", (ConfigValue) "Legacy_UserManager"}, + {TYPE_STRING, "AuthServer", (ConfigValue) "Srp, Win_Sspi"}, + {TYPE_STRING, "AuthClient", (ConfigValue) "Srp, Win_Sspi, Legacy_Auth"}, + {TYPE_STRING, "UserManager", (ConfigValue) "Srp"}, {TYPE_STRING, "TracePlugin", (ConfigValue) "fbtrace"}, {TYPE_STRING, "SecurityDatabase", (ConfigValue) "$(root)/security3.fdb"}, // security database name {TYPE_BOOLEAN, "SharedCache", (ConfigValue) true}, diff --git a/src/common/security.h b/src/common/security.h index 0dae8bab76..01746473e7 100644 --- a/src/common/security.h +++ b/src/common/security.h @@ -24,7 +24,7 @@ #ifndef UTILITIES_SECUR_PROTO_H #define UTILITIES_SECUR_PROTO_H -#include "../auth/AuthInterface.h" +#include "firebird/Auth.h" #include "../common/classes/ImplementHelper.h" #include "../common/classes/GetPlugins.h" #include "../common/classes/array.h" diff --git a/src/common/sha.cpp b/src/common/sha.cpp index 2b9285c4c4..2a4be92511 100644 --- a/src/common/sha.cpp +++ b/src/common/sha.cpp @@ -14,27 +14,19 @@ #include "../common/sha.h" #include "../common/classes/array.h" #include "../common/os/guid.h" +#include "../common/utils_proto.h" + +using namespace Firebird; namespace { -// Useful defines & typedefs -typedef unsigned char BYTE; // 8-bit quantity -typedef unsigned long LONG; // 32-or-more-bit quantity - -#define SHA_BLOCKSIZE 64 -#define SHA_DIGESTSIZE 20 - -struct SHA_INFO -{ - LONG digest[5]; // message digest - LONG count_lo, count_hi; // 64-bit bit count - BYTE data[SHA_BLOCKSIZE]; // SHA data buffer - int local; // unprocessed amount in data -}; +#define SHA_BLOCKSIZE Sha1::BLOCK_SIZE +#define SHA_DIGESTSIZE Sha1::HASH_SIZE +typedef Sha1::ShaInfo SHA_INFO; void sha_init(SHA_INFO *); -void sha_update(SHA_INFO *, const BYTE *, int); +void sha_update(SHA_INFO *, const BYTE *, unsigned int); void sha_final(unsigned char [SHA_DIGESTSIZE], SHA_INFO *); #define SHA_VERSION 1 @@ -135,7 +127,7 @@ void sha_final(unsigned char [SHA_DIGESTSIZE], SHA_INFO *); static void sha_transform(SHA_INFO *sha_info) { int i; - LONG W[80]; + Sha1::LONG W[80]; const BYTE* dp = sha_info->data; @@ -151,7 +143,7 @@ nether regions of the anatomy... #define SWAP_DONE for (i = 0; i < 16; ++i) { - const LONG T = *((LONG *) dp); + const Sha1::LONG T = *((Sha1::LONG *) dp); dp += 4; W[i] = ((T << 24) & 0xff000000) | ((T << 8) & 0x00ff0000) | ((T >> 8) & 0x0000ff00) | ((T >> 24) & 0x000000ff); @@ -162,7 +154,7 @@ nether regions of the anatomy... #define SWAP_DONE for (i = 0; i < 16; ++i) { - const LONG T = *((LONG *) dp); + const Sha1::LONG T = *((Sha1::LONG *) dp); dp += 4; W[i] = T32(T); } @@ -172,7 +164,7 @@ nether regions of the anatomy... #define SWAP_DONE for (i = 0; i < 16; i += 2) { - LONG T = *((LONG *) dp); + Sha1::LONG T = *((Sha1::LONG *) dp); dp += 8; W[i] = ((T << 24) & 0xff000000) | ((T << 8) & 0x00ff0000) | ((T >> 8) & 0x0000ff00) | ((T >> 24) & 0x000000ff); @@ -186,7 +178,7 @@ nether regions of the anatomy... #define SWAP_DONE for (i = 0; i < 16; i += 2) { - const LONG T = *((LONG *) dp); + const Sha1::LONG T = *((Sha1::LONG *) dp); dp += 8; W[i] = T32(T >> 32); W[i + 1] = T32(T); @@ -204,13 +196,13 @@ nether regions of the anatomy... W[i] = R32(W[i], 1); #endif // SHA_VERSION } - LONG A = sha_info->digest[0]; - LONG B = sha_info->digest[1]; - LONG C = sha_info->digest[2]; - LONG D = sha_info->digest[3]; - LONG E = sha_info->digest[4]; - const LONG* WP = W; - LONG T; + Sha1::LONG A = sha_info->digest[0]; + Sha1::LONG B = sha_info->digest[1]; + Sha1::LONG C = sha_info->digest[2]; + Sha1::LONG D = sha_info->digest[3]; + Sha1::LONG E = sha_info->digest[4]; + const Sha1::LONG* WP = W; + Sha1::LONG T; #ifdef UNRAVEL FA(1); FB(1); FC(1); FD(1); FE(1); FT(1); FA(1); FB(1); FC(1); FD(1); FE(1); FT(1); FA(1); FB(1); FC(1); FD(1); FE(1); FT(1); FA(1); FB(1); @@ -265,17 +257,17 @@ void sha_init(SHA_INFO *sha_info) // update the SHA digest -void sha_update(SHA_INFO *sha_info, const BYTE *buffer, int count) +void sha_update(SHA_INFO *sha_info, const BYTE *buffer, unsigned int count) { - const LONG clo = T32(sha_info->count_lo + ((LONG) count << 3)); + const Sha1::LONG clo = T32(sha_info->count_lo + ((Sha1::LONG) count << 3)); if (clo < sha_info->count_lo) { ++sha_info->count_hi; } sha_info->count_lo = clo; - sha_info->count_hi += (LONG) count >> 29; + sha_info->count_hi += (Sha1::LONG) count >> 29; if (sha_info->local) { - int i = SHA_BLOCKSIZE - sha_info->local; + unsigned int i = SHA_BLOCKSIZE - sha_info->local; if (i > count) { i = count; } @@ -305,9 +297,9 @@ void sha_update(SHA_INFO *sha_info, const BYTE *buffer, int count) void sha_final(unsigned char digest[SHA_DIGESTSIZE], SHA_INFO *sha_info) { - const LONG lo_bit_count = sha_info->count_lo; - const LONG hi_bit_count = sha_info->count_hi; - int count = (int) ((lo_bit_count >> 3) & 0x3f); + const Sha1::LONG lo_bit_count = sha_info->count_lo; + const Sha1::LONG hi_bit_count = sha_info->count_hi; + unsigned int count = (int) ((lo_bit_count >> 3) & 0x3f); sha_info->data[count++] = 0x80; if (count > SHA_BLOCKSIZE - 8) { @@ -349,57 +341,58 @@ void sha_final(unsigned char digest[SHA_DIGESTSIZE], SHA_INFO *sha_info) digest[19] = (unsigned char) ((sha_info->digest[4] ) & 0xff); } -inline char conv_bin2ascii(ULONG l) -{ - return "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[l & 0x3f]; -} +} // anonymous namespace -typedef Firebird::HalfStaticArray BinHash; +namespace Firebird { -void base64(Firebird::string& b64, const BinHash& bin) -{ - b64.erase(); - const unsigned char* f = bin.begin(); - for (int i = bin.getCount(); i > 0; i -= 3, f += 3) + void Sha1::hashBased64(Firebird::string& hash, const Firebird::string& data) { - if (i >= 3) + SHA_INFO si; + sha_init(&si); + sha_update(&si, reinterpret_cast(data.c_str()), data.length()); + UCharBuffer b; + sha_final(b.getBuffer(SHA_DIGESTSIZE), &si); + fb_utils::base64(hash, b); + } + + Sha1::Sha1() + : active(false) + { + reset(); + } + + void Sha1::process(unsigned int length, const void* bytes) + { + sha_update(&handle, reinterpret_cast(bytes), length); + } + + void Sha1::getHash(UCharBuffer& hash) + { + fb_assert(active); + sha_final(hash.getBuffer(HASH_SIZE), &handle); + } + + void Sha1::reset() + { + clear(); + sha_init(&handle); + active = true; + } + + Sha1::~Sha1() + { + clear(); + } + + void Sha1::clear() + { + if (active) { - const ULONG l = (ULONG(f[0]) << 16) | (ULONG(f[1]) << 8) | f[2]; - b64 += conv_bin2ascii(l >> 18); - b64 += conv_bin2ascii(l >> 12); - b64 += conv_bin2ascii(l >> 6); - b64 += conv_bin2ascii(l); - } - else - { - ULONG l = ULONG(f[0]) << 16; - if (i == 2) - l |= (ULONG(f[1]) << 8); - b64 += conv_bin2ascii(l >> 18); - b64 += conv_bin2ascii(l >> 12); - b64 += (i == 1 ? '=' : conv_bin2ascii(l >> 6)); - b64 += '='; + unsigned char tmp[HASH_SIZE]; + sha_final(tmp, &handle); + active = false; } } -} -} // anon namespace - -void Jrd::CryptSupport::hash(Firebird::string& hashValue, const Firebird::string& data) -{ - SHA_INFO si; - sha_init(&si); - sha_update(&si, reinterpret_cast(data.c_str()), data.length()); - BinHash bh; - sha_final(bh.getBuffer(SHA_DIGESTSIZE), &si); - base64(hashValue, bh); -} - -void Jrd::CryptSupport::random(Firebird::string& randomValue, size_t length) -{ - BinHash binRand; - Firebird::GenerateRandomBytes(binRand.getBuffer(length), length); - base64(randomValue, binRand); - randomValue.resize(length, '$'); -} +} // namespace Firebird diff --git a/src/common/sha.h b/src/common/sha.h index 6314eb1841..952cd16ffc 100644 --- a/src/common/sha.h +++ b/src/common/sha.h @@ -20,24 +20,64 @@ * Contributor(s): ______________________________________. */ -#ifndef JRD_OS_SHA_H -#define JRD_OS_SHA_H +#ifndef COMMON_SHA_H +#define COMMON_SHA_H #include "firebird.h" +#include "../common/classes/alloc.h" +#include "../common/classes/array.h" #include "../common/classes/fb_string.h" -namespace Jrd -{ - class CryptSupport - { - private: - CryptSupport() {} - ~CryptSupport() {} - public: - // hash and random return base64-coded values - static void hash(Firebird::string& hashValue, const Firebird::string& data); - static void random(Firebird::string& randomValue, size_t length); - }; -} +namespace Firebird { -#endif //JRD_OS_SHA_H +class Sha1 : public GlobalStorage +{ +public: + Sha1(); + + static const unsigned int HASH_SIZE = 20; + static const unsigned int BLOCK_SIZE = 64; + typedef unsigned long LONG; + + struct ShaInfo + { + LONG digest[5]; // message digest + LONG count_lo, count_hi; // 64-bit bit count + UCHAR data[BLOCK_SIZE]; // SHA data buffer + unsigned int local; // unprocessed amount in data + }; + + void process(unsigned int length, const void* bytes); + + void process(const UCharBuffer& bytes) + { + process(bytes.getCount(), bytes.begin()); + } + + void process(const string& str) + { + process(str.length(), str.c_str()); + } + + void process(const char* str) + { + process(strlen(str), str); + } + + void getHash(UCharBuffer& h); + void reset(); + ~Sha1(); + + // return base64-coded values + static void hashBased64(Firebird::string& hashBase64, const Firebird::string& data); + +private: + void clear(); + + ShaInfo handle; + bool active; +}; + +} // namespace Firebird + +#endif // COMMON_SHA_H diff --git a/src/common/thd.cpp b/src/common/thd.cpp index e018b47fa4..83cbc77c9b 100644 --- a/src/common/thd.cpp +++ b/src/common/thd.cpp @@ -254,7 +254,7 @@ void ThreadCleanup::remove(FPTR_VOID_PTR cleanup, void* arg) delete toDelete; } -#else // USE_POSIX_THREADS +#else // USE_THREAD_DESTRUCTOR ThreadCleanup** ThreadCleanup::findCleanup(FPTR_VOID_PTR, void*) { @@ -273,4 +273,4 @@ void ThreadCleanup::remove(FPTR_VOID_PTR, void*) { } -#endif // USE_POSIX_THREADS +#endif // USE_THREAD_DESTRUCTOR diff --git a/src/common/utils.cpp b/src/common/utils.cpp index 5a815834c9..ebae9d48de 100644 --- a/src/common/utils.cpp +++ b/src/common/utils.cpp @@ -28,6 +28,7 @@ #include "firebird.h" #include "../common/common.h" +#include "../common/os/guid.h" #ifdef HAVE_SYS_TYPES_H #include @@ -1084,6 +1085,7 @@ unsigned int statusLength(const ISC_STATUS* const status) throw() } } +// moves DB path information (from limbo transaction) to another buffer void getDbPathInfo(unsigned int& itemsLength, const unsigned char*& items, unsigned int& bufferLength, unsigned char*& buffer, Firebird::Array& newItemsBuffer, const Firebird::PathName& dbpath) @@ -1178,4 +1180,45 @@ bool isRunningCheck(const UCHAR* items, unsigned int length) return state == S_RUN; } +static inline char conv_bin2ascii(ULONG l) +{ + return "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[l & 0x3f]; +} + +// converts bytes to BASE64 representation +void base64(Firebird::string& b64, const Firebird::UCharBuffer& bin) +{ + b64.erase(); + const unsigned char* f = bin.begin(); + for (int i = bin.getCount(); i > 0; i -= 3, f += 3) + { + if (i >= 3) + { + const ULONG l = (ULONG(f[0]) << 16) | (ULONG(f[1]) << 8) | f[2]; + b64 += conv_bin2ascii(l >> 18); + b64 += conv_bin2ascii(l >> 12); + b64 += conv_bin2ascii(l >> 6); + b64 += conv_bin2ascii(l); + } + else + { + ULONG l = ULONG(f[0]) << 16; + if (i == 2) + l |= (ULONG(f[1]) << 8); + b64 += conv_bin2ascii(l >> 18); + b64 += conv_bin2ascii(l >> 12); + b64 += (i == 1 ? '=' : conv_bin2ascii(l >> 6)); + b64 += '='; + } + } +} + +void random64(Firebird::string& randomValue, size_t length) +{ + Firebird::UCharBuffer binRand; + Firebird::GenerateRandomBytes(binRand.getBuffer(length), length); + base64(randomValue, binRand); + randomValue.resize(length, '$'); +} + } // namespace fb_utils diff --git a/src/common/utils_proto.h b/src/common/utils_proto.h index 441765bce7..8aeb93d923 100644 --- a/src/common/utils_proto.h +++ b/src/common/utils_proto.h @@ -152,12 +152,20 @@ namespace fb_utils // Add appropriate file prefix. Firebird::PathName getPrefix(FB_DIR prefType, const char* name); + // moves DB path information (from limbo transaction) to another buffer void getDbPathInfo(unsigned int& itemsLength, const unsigned char*& items, unsigned int& bufferLength, unsigned char*& buffer, Firebird::Array& newItemsBuffer, const Firebird::PathName& dbpath); + // returns true if passed info items work with running svc thread bool isRunningCheck(const UCHAR* items, unsigned int length); + // converts bytes to BASE64 representation + void base64(Firebird::string& b64, const Firebird::UCharBuffer& bin); + + // generate random string in BASE64 representation + void random64(Firebird::string& randomValue, size_t length); + } // namespace fb_utils #endif // INCLUDE_UTILS_PROTO_H diff --git a/src/include/consts_pub.h b/src/include/consts_pub.h index da1c60f39b..c1d9251e4f 100644 --- a/src/include/consts_pub.h +++ b/src/include/consts_pub.h @@ -112,6 +112,10 @@ #define isc_dpb_utf8_filename 77 #define isc_dpb_ext_call_depth 78 #define isc_dpb_auth_block 79 +// This 3 will not be used in protocol 13, therefore may be reused +#define isc_dpb_specific_auth_data isc_dpb_trusted_auth +#define isc_dpb_auth_plugin_list isc_dpb_password +#define isc_dpb_auth_plugin_name isc_dpb_password_enc /**************************************************/ /* clumplet tags used inside isc_dpb_address_path */ @@ -266,12 +270,16 @@ #define isc_spb_trusted_role 113 #define isc_spb_verbint 114 #define isc_spb_auth_block 115 - +#define isc_spb_auth_plugin_name 116 +#define isc_spb_auth_plugin_list 117 #define isc_spb_connect_timeout isc_dpb_connect_timeout #define isc_spb_dummy_packet_interval isc_dpb_dummy_packet_interval #define isc_spb_sql_role_name isc_dpb_sql_role_name +// This will not be used in protocol 13, therefore may be reused +#define isc_spb_specific_auth_data isc_spb_trusted_auth + /***************************** * Service action items * *****************************/ diff --git a/src/auth/AuthInterface.h b/src/include/firebird/Auth.h similarity index 72% rename from src/auth/AuthInterface.h rename to src/include/firebird/Auth.h index 5b7a2bb562..6e4f7758c9 100644 --- a/src/auth/AuthInterface.h +++ b/src/include/firebird/Auth.h @@ -1,6 +1,6 @@ /* * PROGRAM: Firebird authentication - * MODULE: AuthInterface.h + * MODULE: Auth.h * DESCRIPTION: Interfaces, used by authentication plugins * * The contents of this file are subject to the Initial @@ -48,42 +48,46 @@ public: }; #define FB_AUTH_WRITER_VERSION (FB_VERSIONED_VERSION + 3) -class IClumplets : public Firebird::IVersioned +// Representation of auth-related data, passed to server auth plugin +class IServerBlock : public Firebird::IRefCounted { public: - virtual int FB_CARG find(UCHAR tag) = 0; - virtual void FB_CARG add(UCHAR tag, const void* bytes, unsigned int count) = 0; - virtual void FB_CARG drop() = 0; - virtual const unsigned char* FB_CARG get(unsigned int* cntPtr) = 0; + virtual const char* FB_CARG getLogin() = 0; + virtual const unsigned char* FB_CARG getData(unsigned int* length) = 0; + virtual void FB_CARG putData(unsigned int length, const void* data) = 0; }; -#define FB_AUTH_CLUMPLETS_VERSION (FB_VERSIONED_VERSION + 4) +#define FB_AUTH_SERVER_BLOCK_VERSION (FB_VERSIONED_VERSION + 3) -// This struct defines auth-related tags (including legacy ones) in parameter blocks -struct AuthTags +// Representation of auth-related data, passed to client auth plugin +class IClientBlock : public Firebird::IRefCounted { - UCHAR authBlock, trustedAuth, trustedRole; - UCHAR service; // non-zero if we work with service connection +public: + virtual const char* FB_CARG getLogin() = 0; + virtual const char* FB_CARG getPassword() = 0; + virtual const unsigned char* FB_CARG getData(unsigned int* length) = 0; + virtual void FB_CARG putData(unsigned int length, const void* data) = 0; }; +#define FB_AUTH_CLIENT_BLOCK_VERSION (FB_VERSIONED_VERSION + 4) +// server part of authentication plugin class IServer : public Firebird::IPluginBase { public: - virtual Result FB_CARG startAuthentication(Firebird::IStatus* status, const AuthTags* tags, IClumplets* dpb, - IWriter* writerInterface) = 0; - virtual Result FB_CARG contAuthentication(Firebird::IStatus* status, const unsigned char* data, - unsigned int size, IWriter* writerInterface) = 0; - virtual void FB_CARG getData(const unsigned char** data, unsigned short* dataSize) = 0; + virtual Result FB_CARG authenticate(Firebird::IStatus* status, IServerBlock* sBlock, IWriter* writerInterface) = 0; + virtual Result FB_CARG getSessionKey(Firebird::IStatus* status, + const unsigned char** key, unsigned int* keyLen) = 0; }; -#define FB_AUTH_SERVER_VERSION (FB_PLUGIN_VERSION + 3) +#define FB_AUTH_SERVER_VERSION (FB_PLUGIN_VERSION + 2) +// .. and corresponding client class IClient : public Firebird::IPluginBase { public: - virtual Result FB_CARG startAuthentication(Firebird::IStatus* status, const AuthTags* tags, IClumplets* dpb) = 0; - virtual Result FB_CARG contAuthentication(Firebird::IStatus* status, const unsigned char* data, unsigned int size) = 0; - virtual void FB_CARG getData(const unsigned char** data, unsigned short* dataSize) = 0; + virtual Result FB_CARG authenticate(Firebird::IStatus* status, IClientBlock* cBlock) = 0; + virtual Result FB_CARG getSessionKey(Firebird::IStatus* status, + const unsigned char** key, unsigned int* keyLen) = 0; }; -#define FB_AUTH_CLIENT_VERSION (FB_PLUGIN_VERSION + 3) +#define FB_AUTH_CLIENT_VERSION (FB_PLUGIN_VERSION + 2) class IUserField : public Firebird::IVersioned { diff --git a/src/include/firebird/Plugin.h b/src/include/firebird/Plugin.h index a74d3184b6..4a2a59f5e2 100644 --- a/src/include/firebird/Plugin.h +++ b/src/include/firebird/Plugin.h @@ -186,9 +186,9 @@ public: // in case when plugin's version is less than desired // If caller already has an interface for firebird.conf, it may be passed here // If parameter is missing, plugins will get access to default (non database specific) config - virtual IPluginSet* FB_CARG getPlugins(unsigned int interfaceType, const char* namesList, - int desiredVersion, UpgradeInfo* ui, - IFirebirdConf* firebirdConf) = 0; + virtual IPluginSet* FB_CARG getPlugins(IStatus* status, unsigned int interfaceType, + const char* namesList, int desiredVersion, + UpgradeInfo* ui, IFirebirdConf* firebirdConf) = 0; // Get generic config interface for given file virtual IConfig* FB_CARG getConfig(const char* filename) = 0; // Plugins must be released using this function - use of plugin's release() @@ -210,10 +210,19 @@ namespace PluginType { static const unsigned int AuthUserManagement = 13; static const unsigned int ExternalEngine = 14; static const unsigned int Trace = 15; + static const unsigned int Crypt = 15; static const unsigned int MaxType = 16; // keep in sync please }; +class ICrypt : public IPluginBase +{ +public: + virtual void FB_CARG setKey(IStatus* status, unsigned int length, const void* key) = 0; + virtual void FB_CARG transform(IStatus* status, unsigned int length, void* to, const void* from) = 0; +}; +#define FB_CRYPT_VERSION (FB_PLUGIN_VERSION + 1) + } // namespace Firebird diff --git a/src/include/firebird/Provider.h b/src/include/firebird/Provider.h index ce987a102c..aba8b4bd69 100644 --- a/src/include/firebird/Provider.h +++ b/src/include/firebird/Provider.h @@ -115,6 +115,7 @@ class IStatement : public IRefCounted { public: // Prepare flags. + static const unsigned PREPARE_PREFETCH_NONE = 0x0; static const unsigned PREPARE_PREFETCH_TYPE = 0x01; static const unsigned PREPARE_PREFETCH_INPUT_PARAMETERS = 0x02; static const unsigned PREPARE_PREFETCH_OUTPUT_PARAMETERS = 0x04; diff --git a/src/include/gen/codetext.h b/src/include/gen/codetext.h index 45f51b70c2..a6b288464a 100644 --- a/src/include/gen/codetext.h +++ b/src/include/gen/codetext.h @@ -730,6 +730,7 @@ static const struct { {"ee_blr_mismatch_null", 335545026}, {"ee_blr_mismatch_length", 335545027}, {"ss_out_of_bounds", 335545028}, + {"missing_data_structures", 335545029}, {"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 7b99831931..79d3986034 100644 --- a/src/include/gen/iberror.h +++ b/src/include/gen/iberror.h @@ -764,6 +764,7 @@ const ISC_STATUS isc_spb_no_id = 335545025L; const ISC_STATUS isc_ee_blr_mismatch_null = 335545026L; const ISC_STATUS isc_ee_blr_mismatch_length = 335545027L; const ISC_STATUS isc_ss_out_of_bounds = 335545028L; +const ISC_STATUS isc_missing_data_structures = 335545029L; const ISC_STATUS isc_gfix_db_name = 335740929L; const ISC_STATUS isc_gfix_invalid_sw = 335740930L; const ISC_STATUS isc_gfix_incmp_sw = 335740932L; @@ -1202,7 +1203,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 = 1146; +const ISC_STATUS isc_err_max = 1147; #else /* c definitions */ @@ -1936,6 +1937,7 @@ const ISC_STATUS isc_err_max = 1146; #define isc_ee_blr_mismatch_null 335545026L #define isc_ee_blr_mismatch_length 335545027L #define isc_ss_out_of_bounds 335545028L +#define isc_missing_data_structures 335545029L #define isc_gfix_db_name 335740929L #define isc_gfix_invalid_sw 335740930L #define isc_gfix_incmp_sw 335740932L @@ -2374,7 +2376,7 @@ const ISC_STATUS isc_err_max = 1146; #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 1146 +#define isc_err_max 1147 #endif diff --git a/src/include/gen/msgs.h b/src/include/gen/msgs.h index e63643a257..f49c9ca969 100644 --- a/src/include/gen/msgs.h +++ b/src/include/gen/msgs.h @@ -733,6 +733,7 @@ Data source : @4"}, /* eds_statement */ {335545026, "External BLR message mismatch: invalid null descriptor at field @1"}, /* ee_blr_mismatch_null */ {335545027, "External BLR message mismatch: length = @1, expected @2"}, /* ee_blr_mismatch_length */ {335545028, "Subscript @1 out of bounds [@2, @3]"}, /* ss_out_of_bounds */ + {335545029, "Install incomplete, please read chapter \"Initializing security database\" in Quick Start Guide"}, /* missing_data_structures */ {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 e2f6e7ed99..d64b9ed818 100644 --- a/src/include/gen/sql_code.h +++ b/src/include/gen/sql_code.h @@ -729,6 +729,7 @@ static const struct { {335545026, -901}, /* 706 ee_blr_mismatch_null */ {335545027, -901}, /* 707 ee_blr_mismatch_length */ {335545028, -406}, /* 708 ss_out_of_bounds */ + {335545029, -902}, /* 709 missing_data_structures */ {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 397395b70b..98b3ff2c41 100644 --- a/src/include/gen/sql_state.h +++ b/src/include/gen/sql_state.h @@ -729,6 +729,7 @@ static const struct { {335545026, "42000"}, // 706 ee_blr_mismatch_null {335545027, "42000"}, // 707 ee_blr_mismatch_length {335545028, "42000"}, // 708 ss_out_of_bounds + {335545029, "28000"}, // 709 missing_data_structures {335740929, "00000"}, // 1 gfix_db_name {335740930, "00000"}, // 2 gfix_invalid_sw {335740932, "00000"}, // 4 gfix_incmp_sw diff --git a/src/jrd/SysFunction.cpp b/src/jrd/SysFunction.cpp index ebadf5b7a4..54a50d2c6d 100644 --- a/src/jrd/SysFunction.cpp +++ b/src/jrd/SysFunction.cpp @@ -2337,7 +2337,7 @@ dsc* evlHash(thread_db* tdbb, const SysFunction*, const NestValueArray& args, { impure->vlu_misc.vlu_int64 = (impure->vlu_misc.vlu_int64 << 4) + *address; - const SINT64 n = impure->vlu_misc.vlu_int64 & CONST64(0xF000000000000000); + const SINT64 n = impure->vlu_misc.vlu_int64 & FB_CONST64(0xF000000000000000); if (n) impure->vlu_misc.vlu_int64 ^= n >> 56; impure->vlu_misc.vlu_int64 &= ~n; @@ -2355,7 +2355,7 @@ dsc* evlHash(thread_db* tdbb, const SysFunction*, const NestValueArray& args, { impure->vlu_misc.vlu_int64 = (impure->vlu_misc.vlu_int64 << 4) + *address; - const SINT64 n = impure->vlu_misc.vlu_int64 & CONST64(0xF000000000000000); + const SINT64 n = impure->vlu_misc.vlu_int64 & FB_CONST64(0xF000000000000000); if (n) impure->vlu_misc.vlu_int64 ^= n >> 56; impure->vlu_misc.vlu_int64 &= ~n; diff --git a/src/jrd/UserManagement.h b/src/jrd/UserManagement.h index ce03595ae6..d383e9122a 100644 --- a/src/jrd/UserManagement.h +++ b/src/jrd/UserManagement.h @@ -29,7 +29,7 @@ #include "../jrd/ibase.h" #include "../jrd/DatabaseSnapshot.h" #include "../jrd/recsrc/RecordSource.h" -#include "../auth/AuthInterface.h" +#include "firebird/Auth.h" #include "../common/security.h" namespace Jrd { diff --git a/src/jrd/jrd.cpp b/src/jrd/jrd.cpp index e0e2f5cdd8..7100a2945a 100644 --- a/src/jrd/jrd.cpp +++ b/src/jrd/jrd.cpp @@ -6649,9 +6649,8 @@ static void getUserInfo(UserId& user, const DatabaseOptions& options, const RefP { // stub instead mapUser(....); AuthReader auth(options.dpb_auth_block); - string dummy; PathName secureDb; - if (auth.getInfo(&name, &dummy, &secureDb)) + if (auth.getInfo(&name, NULL, &secureDb)) { if (secureDb.hasData()) { @@ -6663,7 +6662,7 @@ static void getUserInfo(UserId& user, const DatabaseOptions& options, const RefP else { auth.moveNext(); - auth.getInfo(&trusted_role, &dummy, &secureDb); + auth.getInfo(&trusted_role, NULL, NULL); } } } diff --git a/src/jrd/svc.cpp b/src/jrd/svc.cpp index 91b14e8254..155ce8599e 100644 --- a/src/jrd/svc.cpp +++ b/src/jrd/svc.cpp @@ -52,7 +52,6 @@ #include "../jrd/thread_proto.h" #include "../yvalve/why_proto.h" #include "../jrd/jrd_proto.h" -#include "../common/enc_proto.h" #include "../common/classes/alloc.h" #include "../common/classes/init.h" #include "../common/classes/ClumpletWriter.h" @@ -750,8 +749,7 @@ Service::Service(const TEXT* service_name, USHORT spb_length, const UCHAR* spb_d // stub instead mapUser(....); AuthReader auth(svc_auth_block); Firebird::string method; - PathName secDb; - if (auth.getInfo(&svc_username, &method, &secDb) && method == "Win_Sspi") + if (auth.getInfo(&svc_username, &method, NULL) && method == "Win_Sspi") { auth.moveNext(); if (!auth.isEof()) diff --git a/src/jrd/tra.cpp b/src/jrd/tra.cpp index e2002b92bd..f918a1e1ce 100644 --- a/src/jrd/tra.cpp +++ b/src/jrd/tra.cpp @@ -62,7 +62,6 @@ #include "../jrd/tpc_proto.h" #include "../jrd/tra_proto.h" #include "../jrd/vio_proto.h" -#include "../common/enc_proto.h" #include "../jrd/jrd_proto.h" #include "../common/classes/ClumpletWriter.h" #include "../common/classes/TriState.h" diff --git a/src/jrd/trace/TraceCmdLine.cpp b/src/jrd/trace/TraceCmdLine.cpp index 815c5dc5b7..3b6dd69bf2 100644 --- a/src/jrd/trace/TraceCmdLine.cpp +++ b/src/jrd/trace/TraceCmdLine.cpp @@ -431,10 +431,9 @@ void fbtrace(UtilSvc* uSvc, TraceSvcIntf* traceSvc) authBlock.add(bytes, authBlockSize); AuthReader auth(authBlock); - string dummy; PathName secureDb; - if (auth.getInfo(&user, &dummy, &secureDb)) + if (auth.getInfo(&user, NULL, &secureDb)) { pwd = ""; adminRole = false; @@ -442,7 +441,7 @@ void fbtrace(UtilSvc* uSvc, TraceSvcIntf* traceSvc) { auth.moveNext(); string trusted_role; - if (auth.getInfo(&trusted_role, &dummy, &secureDb)) + if (auth.getInfo(&trusted_role, NULL, NULL)) { adminRole = true; } diff --git a/src/jrd/trace/TraceManager.h b/src/jrd/trace/TraceManager.h index 6f8a03ebcf..dbab9570e7 100644 --- a/src/jrd/trace/TraceManager.h +++ b/src/jrd/trace/TraceManager.h @@ -115,7 +115,7 @@ public: { if (changeNumber != getStorage()->getChangeNumber()) update_sessions(); - return trace_needs & (CONST64(1) << e); + return trace_needs & (FB_CONST64(1) << e); } /* DSQL-friendly routines to call Trace API hooks. diff --git a/src/msgs/facilities2.sql b/src/msgs/facilities2.sql index 16d2c7c0f0..6eba8c5134 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 (?, ?, ?, ?); -- -('2011-07-29 16:58:38', 'JRD', 0, 709) +('2011-12-22 18:36:31', 'JRD', 0, 710) ('2010-03-15 06:59:09', 'QLI', 1, 531) -- --('2008-11-28 20:27:04', 'GDEF', 2, 346) diff --git a/src/msgs/messages2.sql b/src/msgs/messages2.sql index c9efb0278c..3cbc8e4df8 100644 --- a/src/msgs/messages2.sql +++ b/src/msgs/messages2.sql @@ -816,6 +816,7 @@ Data source : @4', NULL, NULL) ('ee_blr_mismatch_null', NULL, 'met.epp', NULL, 0, 706, NULL, 'External BLR message mismatch: invalid null descriptor at field @1', NULL, NULL) ('ee_blr_mismatch_length', NULL, 'met.epp', NULL, 0, 707, NULL, 'External BLR message mismatch: length = @1, expected @2', NULL, NULL) ('ss_out_of_bounds', NULL, 'sdl.cpp', NULL, 0, 708, NULL, 'Subscript @1 out of bounds [@2, @3]', NULL, NULL) +('missing_data_structures', NULL, 'server.cpp', NULL, 0, 709, NULL, 'Install incomplete, please read chapter "Initializing security database" in Quick Start Guide', NULL, NULL) -- QLI (NULL, NULL, NULL, NULL, 1, 0, NULL, 'expected type', NULL, NULL); (NULL, NULL, NULL, NULL, 1, 1, NULL, 'bad block type', NULL, NULL); diff --git a/src/msgs/system_errors2.sql b/src/msgs/system_errors2.sql index b11ee46c41..f30090e0e6 100644 --- a/src/msgs/system_errors2.sql +++ b/src/msgs/system_errors2.sql @@ -714,6 +714,7 @@ set bulk_insert INSERT INTO SYSTEM_ERRORS (SQL_CODE, SQL_CLASS, SQL_SUBCLASS, FA (-901, '42', '000', 0, 706, 'ee_blr_mismatch_null', NULL, NULL) (-901, '42', '000', 0, 707, 'ee_blr_mismatch_length', NULL, NULL) (-406, '42', '000', 0, 708, 'ss_out_of_bounds', NULL, NULL) +(-902, '28', '000', 0, 709, 'missing_data_structures', 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/remote/client/interface.cpp b/src/remote/client/interface.cpp index 35053186fc..dff077a585 100644 --- a/src/remote/client/interface.cpp +++ b/src/remote/client/interface.cpp @@ -53,7 +53,6 @@ #include "../remote/remot_proto.h" #include "../remote/proto_proto.h" #include "../common/cvt.h" -#include "../common/enc_proto.h" #include "../yvalve/gds_proto.h" #include "../common/isc_f_proto.h" #include "../common/sdl_proto.h" @@ -67,6 +66,7 @@ #include "../common/StatementMetadata.h" #include "../auth/SecurityDatabase/LegacyClient.h" +#include "../auth/SecureRemotePassword/client/SrpClient.h" #include "../auth/trusted/AuthSspi.h" @@ -105,34 +105,15 @@ const char* const WNET_LOCALHOST = "\\\\."; using namespace Firebird; namespace { + MakeUpgradeInfo<> upInfo; + // Success vector for general use const ISC_STATUS success_vector[] = {isc_arg_gds, FB_SUCCESS, isc_arg_end}; - - // this sets of parameters help use same functions - // for both services and databases attachments - struct ParametersSet - { - UCHAR dummy_packet_interval, user_name, - password, password_enc, address_path, process_id, process_name; - }; - const ParametersSet dpbParam = {isc_dpb_dummy_packet_interval, - isc_dpb_user_name, - isc_dpb_password, - isc_dpb_password_enc, - isc_dpb_address_path, - isc_dpb_process_id, - isc_dpb_process_name}; - const ParametersSet spbParam = {isc_spb_dummy_packet_interval, - isc_spb_user_name, - isc_spb_password, - isc_spb_password_enc, - isc_spb_address_path, - isc_spb_process_id, - isc_spb_process_name}; } namespace Remote { +// Provider stuff class Attachment; class Blob : public Firebird::RefCntIface @@ -552,6 +533,7 @@ void registerRedirector(Firebird::IPluginManager* iPlugin) iPlugin->registerPluginFactory(Firebird::PluginType::Provider, "Loopback", &loopbackFactory); Auth::registerLegacyClient(iPlugin); + Auth::registerSrpClient(iPlugin); #ifdef TRUSTED_AUTH Auth::registerTrustedClient(iPlugin); #endif @@ -570,17 +552,13 @@ extern "C" void FB_PLUGIN_ENTRY_POINT(IMaster* master) namespace Remote { -typedef GetPlugins AuthClientPlugins; -static MakeUpgradeInfo<> upInfo; - static Rvnt* add_event(rem_port*); static void add_other_params(rem_port*, ClumpletWriter&, const ParametersSet&); static void add_working_directory(ClumpletWriter&, const PathName&); -static rem_port* analyze(PathName&, bool, ClumpletReader&, PathName&, bool); +static rem_port* analyze(ClntAuthBlock&, PathName&, bool, ClumpletReader&, PathName&, bool); static rem_port* analyze_service(PathName&, bool, ClumpletReader&, bool); static void batch_gds_receive(rem_port*, struct rmtque *, USHORT); static void batch_dsql_fetch(rem_port*, struct rmtque *, USHORT); -static void check_response(IStatus*, Rdb*, PACKET *); static void clear_queue(rem_port*); static void clear_stmt_que(rem_port*, Rsr*); static void disconnect(rem_port*); @@ -592,8 +570,8 @@ static Rvnt* find_event(rem_port*, SLONG); static bool get_new_dpb(ClumpletWriter&, const ParametersSet&); static void handle_error(ISC_STATUS); static void info(IStatus*, Rdb*, P_OP, USHORT, USHORT, USHORT, - const UCHAR*, USHORT, const UCHAR*, ULONG, UCHAR*, AuthClientPlugins* authItr = NULL); -static void init(IStatus*, rem_port*, P_OP, PathName&, ClumpletWriter&); + const UCHAR*, USHORT, const UCHAR*, ULONG, UCHAR*, ClntAuthBlock* cBlock = NULL); +static void init(IStatus*, ClntAuthBlock&, rem_port*, P_OP, PathName&, ClumpletWriter&); static Rtr* make_transaction(Rdb*, USHORT); static void mov_dsql_message(const UCHAR*, const rem_fmt*, UCHAR*, const rem_fmt*); static void move_error(const Arg::StatusVector& v); @@ -618,11 +596,12 @@ static void server_death(rem_port*); static void svcstart(IStatus*, Rdb*, P_OP, USHORT, USHORT, USHORT, const UCHAR*); static void unsupported(); static void zap_packet(PACKET *); +static void cleanDpb(Firebird::ClumpletWriter&, const ParametersSet*); -static void authFillParametersBlock(AuthClientPlugins& authItr, ClumpletWriter& dpb, - const Auth::AuthTags* tags, rem_port* port); -static void authReceiveResponse(AuthClientPlugins& authItr, rem_port* port, Rdb* rdb, - const Auth::AuthTags* tags, IStatus* status, PACKET* packet); +static void authFillParametersBlock(ClntAuthBlock& authItr, ClumpletWriter& dpb, + const ParametersSet* tags, const PathName* filename, rem_port* port); +static void authReceiveResponse(ClntAuthBlock& authItr, rem_port* port, Rdb* rdb, + const ParametersSet* tags, IStatus* status, PACKET* packet); static AtomicCounter remote_event_id; @@ -682,7 +661,9 @@ Firebird::IAttachment* Provider::attach(IStatus* status, const char* filename, PathName expanded_name(filename); PathName node_name; - rem_port* port = analyze(expanded_name, user_verification, newDpb, node_name, loopback); + + ClntAuthBlock cBlock(&expanded_name); + rem_port* port = analyze(cBlock, expanded_name, user_verification, newDpb, node_name, loopback); if (!port) { @@ -700,7 +681,7 @@ Firebird::IAttachment* Provider::attach(IStatus* status, const char* filename, add_other_params(port, newDpb, dpbParam); add_working_directory(newDpb, node_name); - init(status, port, op_attach, expanded_name, newDpb); + init(status, cBlock, port, op_attach, expanded_name, newDpb); Attachment* a = new Attachment(port->port_context, filename); a->addRef(); @@ -1226,7 +1207,9 @@ Firebird::IAttachment* Provider::create(IStatus* status, const char* filename, PathName expanded_name(filename); PathName node_name; - rem_port* port = analyze(expanded_name, user_verification, newDpb, node_name, loopback); + + ClntAuthBlock cBlock(&expanded_name); + rem_port* port = analyze(cBlock, expanded_name, user_verification, newDpb, node_name, loopback); if (!port) { @@ -1244,7 +1227,7 @@ Firebird::IAttachment* Provider::create(IStatus* status, const char* filename, add_other_params(port, newDpb, dpbParam); add_working_directory(newDpb, node_name); - init(status, port, op_create, expanded_name, newDpb); + init(status, cBlock, port, op_create, expanded_name, newDpb); Firebird::IAttachment* a = new Attachment(rdb, filename); a->addRef(); @@ -1757,7 +1740,7 @@ Firebird::ITransaction* Statement::execute(IStatus* status, Firebird::ITransacti receive_packet(port, packet); if (packet->p_operation != op_sql_response) - check_response(status, rdb, packet); + REMOTE_check_response(status, rdb, packet); else { port->port_statement->rsr_message->msg_address = NULL; @@ -1955,7 +1938,7 @@ Firebird::ITransaction* Attachment::execute(IStatus* status, Firebird::ITransact receive_packet(rdb->rdb_port, packet); if (packet->p_operation != op_sql_response) - check_response(status, rdb, packet); + REMOTE_check_response(status, rdb, packet); else { message->msg_address = NULL; @@ -3161,7 +3144,7 @@ int Attachment::getSlice(IStatus* status, ITransaction* apiTra, ISC_QUAD* array_ if (packet->p_operation != op_slice) { - check_response(status, rdb, packet); + REMOTE_check_response(status, rdb, packet); } return response->p_slr_length; @@ -4137,7 +4120,11 @@ Firebird::IService* Provider::attachSvc(IStatus* status, const char* service, add_other_params(port, newSpb, spbParam); - init(status, port, op_service_attach, expanded_name, newSpb); + ClntAuthBlock cBlock(NULL); + cBlock.load(newSpb, &spbParam); + init(status, cBlock, port, op_service_attach, expanded_name, newSpb); + + cBlock.saveServiceDataTo(port); Firebird::IService* s = new Service(rdb); s->addRef(); @@ -4241,11 +4228,6 @@ void Service::query(IStatus* status, * Functional description * Provide information on service object. * - * NOTE: The parameter RESERVED must not be used - * for any purpose as there are networking issues - * involved (as with any handle that goes over the - * network). This parameter will be implemented at - * a later date. **************************************/ try { @@ -4263,11 +4245,12 @@ void Service::query(IStatus* status, unsupported(); } - AuthClientPlugins authItr(PluginType::AuthClient, FB_AUTH_CLIENT_VERSION, upInfo); + ClntAuthBlock cBlock(NULL); + cBlock.loadServiceDataFrom(port); info(status, rdb, op_service_info, rdb->rdb_id, 0, sendLength, sendItems, receiveLength, receiveItems, - bufferLength, buffer, &authItr); + bufferLength, buffer, &cBlock); } catch (const Exception& ex) { @@ -4610,7 +4593,7 @@ void Attachment::transactRequest(IStatus* status, ITransaction* apiTra, if (packet->p_operation != op_transact_response) { - check_response(status, rdb, packet); + REMOTE_check_response(status, rdb, packet); } } catch (const Exception& ex) @@ -4842,7 +4825,30 @@ static void add_working_directory(ClumpletWriter& dpb, const PathName& node_name } -static rem_port* analyze(PathName& file_name, +static void authenticateStep0(ClntAuthBlock& wire) +{ + for (LocalStatus s; wire.plugins.hasData(); wire.plugins.next()) + { + switch(wire.plugins.plugin()->authenticate(&s, &wire)) + { + case Auth::AUTH_SUCCESS: + case Auth::AUTH_MORE_DATA: + return; + case Auth::AUTH_FAILED: + gds__log_status("Authentication, client plugin:", s.get()); + (Arg::Gds(isc_login) +#ifdef DEV_BUILD + << Arg::StatusVector(s.get()) +#endif + ).raise(); + break; // compiler silencer + } + } +} + + +static rem_port* analyze(ClntAuthBlock& cBlock, + PathName& file_name, bool uv_flag, ClumpletReader& dpb, PathName& node_name, @@ -4869,10 +4875,13 @@ static rem_port* analyze(PathName& file_name, // Analyze the file name to see if a remote connection is required. If not, // quietly (sic) return. + cBlock.load(dpb, &dpbParam); + authenticateStep0(cBlock); + #ifdef WIN_NT if (ISC_analyze_protocol(PROTOCOL_XNET, file_name, node_name)) { - return XNET_analyze(file_name, uv_flag); + return XNET_analyze(&cBlock, file_name, uv_flag); } if (ISC_analyze_protocol(PROTOCOL_WNET, file_name, node_name) || @@ -4882,7 +4891,7 @@ static rem_port* analyze(PathName& file_name, { node_name = WNET_LOCALHOST; } - return WNET_analyze(file_name, node_name.c_str(), uv_flag); + return WNET_analyze(&cBlock, file_name, node_name.c_str(), uv_flag); } #endif @@ -4893,7 +4902,7 @@ static rem_port* analyze(PathName& file_name, { node_name = INET_LOCALHOST; } - return INET_analyze(file_name, node_name.c_str(), uv_flag, dpb); + return INET_analyze(&cBlock, file_name, node_name.c_str(), uv_flag, dpb); } // We have a local connection string. If it's a file on a network share, @@ -4907,7 +4916,7 @@ static rem_port* analyze(PathName& file_name, if (ISC_analyze_pclan(expanded_name, node_name)) { - port = WNET_analyze(expanded_name, node_name.c_str(), uv_flag); + port = WNET_analyze(&cBlock, expanded_name, node_name.c_str(), uv_flag); } #endif @@ -4917,7 +4926,7 @@ static rem_port* analyze(PathName& file_name, PathName expanded_name = file_name; if (ISC_analyze_nfs(expanded_name, node_name)) { - port = INET_analyze(expanded_name, node_name.c_str(), uv_flag, dpb); + port = INET_analyze(&cBlock, expanded_name, node_name.c_str(), uv_flag, dpb); } } #endif @@ -4932,17 +4941,17 @@ static rem_port* analyze(PathName& file_name, #ifdef WIN_NT if (!port) { - port = XNET_analyze(file_name, uv_flag); + port = XNET_analyze(&cBlock, file_name, uv_flag); } if (!port) { - port = WNET_analyze(file_name, WNET_LOCALHOST, uv_flag); + port = WNET_analyze(&cBlock, file_name, WNET_LOCALHOST, uv_flag); } #endif if (!port) { - port = INET_analyze(file_name, INET_LOCALHOST, uv_flag, dpb); + port = INET_analyze(&cBlock, file_name, INET_LOCALHOST, uv_flag, dpb); } } } @@ -4971,15 +4980,15 @@ static rem_port* analyze_service(PathName& service_name, * Otherwise, return NULL. * **************************************/ - PathName node_name; - // Analyze the service name to see if a remote connection is required. If not, // quietly (sic) return. + PathName node_name; + #if defined(WIN_NT) if (ISC_analyze_protocol(PROTOCOL_XNET, service_name, node_name)) { - return XNET_analyze(service_name, uv_flag); + return XNET_analyze(cBlock, service_name, uv_flag); } if (ISC_analyze_protocol(PROTOCOL_WNET, service_name, node_name) || @@ -4989,7 +4998,7 @@ static rem_port* analyze_service(PathName& service_name, { node_name = WNET_LOCALHOST; } - return WNET_analyze(service_name, node_name.c_str(), uv_flag); + return WNET_analyze(cBlock, service_name, node_name.c_str(), uv_flag); } #endif @@ -5000,7 +5009,7 @@ static rem_port* analyze_service(PathName& service_name, { node_name = INET_LOCALHOST; } - return INET_analyze(service_name, node_name.c_str(), uv_flag, spb); + return INET_analyze(NULL, service_name, node_name.c_str(), uv_flag, spb); } rem_port* port = NULL; @@ -5016,17 +5025,17 @@ static rem_port* analyze_service(PathName& service_name, #if defined(WIN_NT) if (!port) { - port = XNET_analyze(service_name, uv_flag); + port = XNET_analyze(cBlock, service_name, uv_flag); } if (!port) { - port = WNET_analyze(service_name, WNET_LOCALHOST, uv_flag); + port = WNET_analyze(cBlock, service_name, WNET_LOCALHOST, uv_flag); } #endif if (!port) { - port = INET_analyze(service_name, INET_LOCALHOST, uv_flag, spb); + port = INET_analyze(NULL, service_name, INET_LOCALHOST, uv_flag, spb); } } } @@ -5165,7 +5174,7 @@ static void batch_dsql_fetch(rem_port* port, statement->rsr_flags.set(Rsr::STREAM_ERR); try { - check_response(&status, rdb, packet); + REMOTE_check_response(&status, rdb, packet); statement->saveException(status.get(), false); } catch (const Exception& ex) @@ -5321,7 +5330,7 @@ static void batch_gds_receive(rem_port* port, try { LocalStatus status; - check_response(&status, rdb, packet); + REMOTE_check_response(&status, rdb, packet); #ifdef DEBUG fprintf(stderr, "End of batch. rows pending = %d\n", tail->rrq_rows_pending); #endif @@ -5368,85 +5377,6 @@ static void batch_gds_receive(rem_port* port, } -static void check_response(IStatus* warning, Rdb* rdb, PACKET* packet) -{ -/************************************** - * - * c h e c k _ r e s p o n s e - * - ************************************** - * - * Functional description - * Check response to a remote call. - * - **************************************/ - - // Get status vector - - const ISC_STATUS* vector = success_vector; - if (packet->p_resp.p_resp_status_vector) - { - vector = packet->p_resp.p_resp_status_vector->value(); - } - - // Translate any gds codes into local operating specific codes - - SimpleStatusVector newVector; - rem_port* port = rdb->rdb_port; - - while (*vector != isc_arg_end) - { - const ISC_STATUS vec = *vector++; - newVector.push(vec); - - switch ((USHORT) vec) - { - case isc_arg_warning: - case isc_arg_gds: - if (port->port_protocol < PROTOCOL_VERSION10) - { - fb_assert(vec == isc_arg_gds); - newVector.push(gds__encode(*vector++, 0)); - } - else - newVector.push(*vector++); - break; - - case isc_arg_cstring: - newVector.push(*vector++); - // fall down - - default: - newVector.push(*vector++); - break; - } - } - - newVector.push(isc_arg_end); - vector = newVector.begin(); - - const ISC_STATUS pktErr = vector[1]; - if (pktErr == isc_shutdown || pktErr == isc_att_shutdown) - { - port->port_flags |= PORT_rdb_shutdown; - } - - if ((packet->p_operation == op_response || packet->p_operation == op_response_piggyback) && - !vector[1]) - { - warning->set(vector); - return; - } - - if (!vector[1]) - { - Arg::Gds(isc_net_read_err).raise(); - } - - status_exception::raise(vector); -} - - static void clear_queue(rem_port* port) { /************************************** @@ -5668,7 +5598,7 @@ static int fetch_blob(IStatus* status, if (packet->p_operation == op_fetch_response) receive_response(status, rdb, packet); else - check_response(status, rdb, packet); + REMOTE_check_response(status, rdb, packet); return packet->p_sqldata.p_sqldata_status; } @@ -5721,25 +5651,6 @@ static bool get_new_dpb(ClumpletWriter& dpb, const ParametersSet& par) } } -/* #ifndef NO_PASSWORD_ENCRYPTION */ -#ifdef NEVERDEF - if (dpb.find(par.password)) - { - string password; - dpb.getString(password); - dpb.deleteClumplet(); - - if (!dpb.find(isc_dpb_utf8_filename)) - ISC_systemToUtf8(password); - ISC_unescape(password); - - TEXT pwt[Auth::MAX_PASSWORD_LENGTH + 2]; - ENC_crypt(pwt, sizeof pwt, password.c_str(), Auth::PASSWORD_SALT); - password = pwt + 2; - dpb.insertString(par.password_enc, password); - } -#endif - return dpb.find(par.user_name); } @@ -5774,7 +5685,7 @@ static void info(IStatus* status, const UCHAR* recv_items, ULONG buffer_length, UCHAR* buffer, - AuthClientPlugins* authItr) + ClntAuthBlock* cBlock) { /************************************** * @@ -5817,8 +5728,9 @@ static void info(IStatus* status, if (operation == op_service_info) { // Probably communicate with services auth - fb_assert(authItr); - authReceiveResponse(*authItr, rdb->rdb_port, rdb, &Auth::SVC_QUERY_LIST, status, packet); + fb_assert(cBlock); + HANDSHAKE_DEBUG(fprintf(stderr, "info() calls authReceiveResponse\n")); + authReceiveResponse(*cBlock, rdb->rdb_port, rdb, &spbInfoParam, status, packet); } else { @@ -5836,42 +5748,40 @@ static void info(IStatus* status, } // Let plugins try to add data to DPB in order to avoid extra network roundtrip -static void authFillParametersBlock(AuthClientPlugins& authItr, ClumpletWriter& dpb, - const Auth::AuthTags* tags, rem_port* port) +static void authFillParametersBlock(ClntAuthBlock& cBlock, ClumpletWriter& dpb, + const ParametersSet* tags, const PathName* filename, rem_port* port) { LocalStatus s; - Auth::DpbImplementation di(dpb); - bool working = true; + cBlock.resetDataFromPlugin(); - while (working && authItr.hasData()) + for (; cBlock.plugins.hasData(); cBlock.plugins.next()) { if (port->port_protocol >= PROTOCOL_VERSION13 || - (port->port_protocol >= PROTOCOL_VERSION11 && Auth::legacy(authItr.name()))) + REMOTE_legacy_auth(cBlock.plugins.name(), port->port_protocol)) { + cBlock.resetDataFromPlugin(); // OK to use plugin - switch(authItr.plugin()->startAuthentication(&s, tags, &di)) + switch(cBlock.plugins.plugin()->authenticate(&s, &cBlock)) { case Auth::AUTH_SUCCESS: - working = false; - break; + case Auth::AUTH_MORE_DATA: + HANDSHAKE_DEBUG(fprintf(stderr, "FPB: plugin %s is OK\n", cBlock.plugins.name())); + cleanDpb(dpb, tags); + cBlock.extractDataFromPluginTo(dpb, tags, port->port_protocol); + return; case Auth::AUTH_FAILED: + HANDSHAKE_DEBUG(fprintf(stderr, "FPB: plugin %s FAILED\n", cBlock.plugins.name())); (Arg::Gds(isc_login) << Arg::StatusVector(s.get())).raise(); break; // compiler silencer - default: - authItr.next(); - break; } } - else - { - authItr.next(); - } + HANDSHAKE_DEBUG(fprintf(stderr, "FPB: try next plugin, %s skipped\n", cBlock.plugins.name())); } } -static void authReceiveResponse(AuthClientPlugins& authItr, rem_port* port, Rdb* rdb, - const Auth::AuthTags* tags, IStatus* status, PACKET* packet) +static void authReceiveResponse(ClntAuthBlock& cBlock, rem_port* port, Rdb* rdb, + const ParametersSet* tags, IStatus* status, PACKET* packet) { LocalStatus s; @@ -5887,27 +5797,31 @@ static void authReceiveResponse(AuthClientPlugins& authItr, rem_port* port, Rdb* switch(packet->p_operation) { case op_trusted_auth: + HANDSHAKE_DEBUG(fprintf(stderr, "RR:TA\n")); d = &packet->p_trau.p_trau_data; break; case op_cont_auth: d = &packet->p_auth_cont.p_data; n = &packet->p_auth_cont.p_name; + HANDSHAKE_DEBUG(fprintf(stderr, "RR:CA d=%d n=%d '%.*s' 0x%x\n", d->cstr_length, n->cstr_length, + n->cstr_length, n->cstr_address, n->cstr_address ? n->cstr_address[0] : 0)); break; default: - check_response(status, rdb, packet); + HANDSHAKE_DEBUG(fprintf(stderr, "RR: Default answer\n")); + REMOTE_check_response(status, rdb, packet); // successfully attached + HANDSHAKE_DEBUG(fprintf(stderr, "RR: OK!\n")); rdb->rdb_id = packet->p_resp.p_resp_object; return; } - bool contFlag = true; - if (n && n->cstr_length && authItr.hasData()) + if (n && n->cstr_length && cBlock.plugins.hasData()) { // if names match, do not change instance - if (strlen(authItr.name()) == n->cstr_length && - memcmp(authItr.name(), n->cstr_address, n->cstr_length) == 0) + if (strlen(cBlock.plugins.name()) == n->cstr_length && + memcmp(cBlock.plugins.name(), n->cstr_address, n->cstr_length) == 0) { n = NULL; } @@ -5916,63 +5830,37 @@ static void authReceiveResponse(AuthClientPlugins& authItr, rem_port* port, Rdb* if (n && n->cstr_length) { // switch to other plugin - string tmp(n->cstr_address, n->cstr_length); - authItr.set(tmp.c_str()); - - if (authItr.hasData()) + PathName tmp(n->cstr_address, n->cstr_length); + if (!cBlock.checkPluginName(tmp)) { - Auth::Result rc = authItr.plugin()->startAuthentication(&s, tags, NULL); - - if (rc == Auth::AUTH_FAILED) - { - break; - } - if (rc != Auth::AUTH_MORE_DATA) - { - contFlag = false; - } - } - else - { - contFlag = false; - packet->p_trau.p_trau_data.cstr_length = 0; + break; } + cBlock.plugins.set(tmp.c_str()); } - if (contFlag) + if (!cBlock.plugins.hasData()) { - // continue auth - if (!authItr.hasData()) - { - break; - } - if (authItr.plugin()->contAuthentication(&s, d->cstr_address, d->cstr_length) == Auth::AUTH_FAILED) - { - break; - } + break; } - if (!authItr.hasData()) + cBlock.storeDataForPlugin(d->cstr_length, d->cstr_address); + if (cBlock.plugins.plugin()->authenticate(&s, &cBlock) == Auth::AUTH_FAILED) { break; } // send answer (may be empty) to server - packet->p_operation = op_trusted_auth; - d = &packet->p_trau.p_trau_data; - d->cstr_allocated = 0; - // violate constness here safely - send operation does not modify data - authItr.plugin()->getData(const_cast(&d->cstr_address), - &d->cstr_length); - + packet->p_operation = op_cont_auth; + cBlock.extractDataFromPluginTo(&packet->p_auth_cont); send_packet(port, packet); + memset(&packet->p_auth_cont, 0, sizeof packet->p_auth_cont); } // If we have exited from the cycle, this mean auth failed (Arg::Gds(isc_login) << Arg::StatusVector(s.get())).raise(); } -static void init(IStatus* status, rem_port* port, P_OP op, PathName& file_name, +static void init(IStatus* status, ClntAuthBlock& cBlock, rem_port* port, P_OP op, PathName& file_name, ClumpletWriter& dpb) { /************************************** @@ -5994,11 +5882,6 @@ static void init(IStatus* status, rem_port* port, P_OP op, PathName& file_name, MemoryPool& pool = *getDefaultMemoryPool(); port->port_deferred_packets = FB_NEW(pool) PacketQueue(pool); - AuthClientPlugins authItr(PluginType::AuthClient, FB_AUTH_CLIENT_VERSION, upInfo); - authFillParametersBlock(authItr, dpb, - op == op_service_attach ? &Auth::SVC_ATTACH_LIST : &Auth::DB_ATTACH_LIST, - port); - if (port->port_protocol < PROTOCOL_VERSION12) { // This is FB < 2.5. Lets remove that not recognized DPB and convert the UTF8 @@ -6034,6 +5917,12 @@ static void init(IStatus* status, rem_port* port, P_OP op, PathName& file_name, } } + HANDSHAKE_DEBUG(fprintf(stderr, "init calls authFillParametersBlock\n")); + authFillParametersBlock(cBlock, dpb, + op == op_service_attach ? &spbParam : &dpbParam, + op == op_service_attach ? NULL : &file_name, + port); + // Make attach packet P_ATCH* attach = &packet->p_atch; packet->p_operation = op; @@ -6044,9 +5933,8 @@ static void init(IStatus* status, rem_port* port, P_OP op, PathName& file_name, send_packet(port, packet); - authReceiveResponse(authItr, port, rdb, - op == op_service_attach ? &Auth::SVC_ATTACH_LIST : &Auth::DB_ATTACH_LIST, - status, packet); + authReceiveResponse(cBlock, port, rdb, + op == op_service_attach ? &spbParam : &dpbParam, status, packet); } catch (const Exception&) { @@ -6205,7 +6093,7 @@ static void receive_after_start(Rrq* request, USHORT msg_type) try { LocalStatus status; - check_response(&status, rdb, packet); + REMOTE_check_response(&status, rdb, packet); request->saveStatus(&status); } catch (const Exception& ex) @@ -6321,7 +6209,7 @@ static void receive_packet_noqueue(rem_port* port, PACKET* packet) try { LocalStatus status; - check_response(&status, rdb, &p->packet); + REMOTE_check_response(&status, rdb, &p->packet); statement->saveException(status.get(), false); } catch (const Exception& ex) @@ -6464,7 +6352,7 @@ static void receive_response(IStatus* status, Rdb* rdb, PACKET* packet) **************************************/ receive_packet(rdb->rdb_port, packet); - check_response(status, rdb, packet); + REMOTE_check_response(status, rdb, packet); } @@ -6924,8 +6812,10 @@ static void svcstart(IStatus* status, // Get ready for multi-hop auth ClumpletWriter send(ClumpletReader::SpbStart, MAX_DPB_SIZE, items, item_length); - AuthClientPlugins authItr(PluginType::AuthClient, FB_AUTH_CLIENT_VERSION, upInfo); - authFillParametersBlock(authItr, send, &Auth::SVC_START_LIST, rdb->rdb_port); + ClntAuthBlock cBlock(NULL); + cBlock.loadServiceDataFrom(rdb->rdb_port); + HANDSHAKE_DEBUG(fprintf(stderr, "start calls authFillParametersBlock\n")); + authFillParametersBlock(cBlock, send, &spbStartParam, NULL, rdb->rdb_port); // Build the primary packet to get the operation started. PACKET* packet = &rdb->rdb_packet; @@ -6945,7 +6835,7 @@ static void svcstart(IStatus* status, try { - authReceiveResponse(authItr, rdb->rdb_port, rdb, &Auth::SVC_START_LIST, status, packet); + authReceiveResponse(cBlock, rdb->rdb_port, rdb, &spbStartParam, status, packet); } catch (const Exception&) { @@ -7059,4 +6949,193 @@ Transaction* Attachment::remoteTransactionInterface(ITransaction* apiTra) return static_cast(valid); } +static void cleanDpb(Firebird::ClumpletWriter& dpb, const ParametersSet* tags) +{ + dpb.deleteWithTag(tags->password); + dpb.deleteWithTag(tags->password_enc); + dpb.deleteWithTag(tags->trusted_auth); +} + } //namespace Remote + +ClntAuthBlock::ClntAuthBlock(const Firebird::PathName* fileName) + : pluginList(getPool()), userName(getPool()), password(getPool()), + dataForPlugin(getPool()), dataFromPlugin(getPool()), + hasCryptKey(false), + plugins(PluginType::AuthClient, FB_AUTH_CLIENT_VERSION, upInfo), + authComplete(false), firstTime(true) +{ + reset(fileName); +} + +void ClntAuthBlock::resetDataFromPlugin() +{ + dataFromPlugin.clear(); +} + +void ClntAuthBlock::extractDataFromPluginTo(Firebird::ClumpletWriter& dpb, + const ParametersSet* tags, + int protocol) +{ + if (!dataFromPlugin.hasData()) + { + return; + } + + PathName pluginName = getPluginName(); + if (protocol >= PROTOCOL_VERSION13) + { + if (firstTime) + { + fb_assert(tags->plugin_name && tags->plugin_list); + if (pluginName.hasData()) + { + dpb.insertPath(tags->plugin_name, pluginName); + } + dpb.insertPath(tags->plugin_list, pluginList); + firstTime = false; + HANDSHAKE_DEBUG(fprintf(stderr, "first time - added plugName & pluginList\n")); + } + fb_assert(tags->specific_data); + dpb.insertBytes(tags->specific_data, dataFromPlugin.begin(), dataFromPlugin.getCount()); + + HANDSHAKE_DEBUG(fprintf(stderr, "Added %" SIZEFORMAT " bytes of spec data with tag %d\n", + dataFromPlugin.getCount(), tags->specific_data)); + + return; + } + + if (REMOTE_legacy_auth(pluginName.c_str(), PROTOCOL_VERSION10)) // dataFromPlugin is encrypted password + { + fb_assert(tags->password_enc); + dpb.insertBytes(tags->password_enc, dataFromPlugin.begin(), dataFromPlugin.getCount()); + return; + } + + fb_assert(REMOTE_legacy_auth(pluginName.c_str(), protocol)); // dataFromPlugin must be trustedAuth + fb_assert(tags->trusted_auth); + dpb.insertBytes(tags->trusted_auth, dataFromPlugin.begin(), dataFromPlugin.getCount()); +} + +static inline void makeUtfString(bool uft8Convert, Firebird::string& s) +{ + if (uft8Convert) + { + ISC_systemToUtf8(s); + } + ISC_unescape(s); +} + +void ClntAuthBlock::load(Firebird::ClumpletReader& dpb, const ParametersSet* tags) +{ + bool uft8Convert = !dpb.find(isc_dpb_utf8_filename); + + for (dpb.rewind(); !dpb.isEof(); dpb.moveNext()) + { + const UCHAR t = dpb.getClumpTag(); + if (t == tags->user_name) + { + dpb.getString(userName); + makeUtfString(uft8Convert, userName); + HANDSHAKE_DEBUG(fprintf(stderr, "Loaded from PB user = %s\n", userName.c_str())); + userName.upper(); + } + else if (t == tags->password) + { + makeUtfString(uft8Convert, password); + dpb.getString(password); + HANDSHAKE_DEBUG(fprintf(stderr, "Loaded from PB password = %s\n", password.c_str())); + } + else if (t == tags->encrypt_key) + { + hasCryptKey = true; + HANDSHAKE_DEBUG(fprintf(stderr, "PB contains crypt key - need encrypted line to pass\n")); + } + } +} + +void ClntAuthBlock::extractDataFromPluginTo(P_AUTH_CONT* to) +{ + to->p_data.cstr_length = dataFromPlugin.getCount(); + to->p_data.cstr_address = dataFromPlugin.begin(); + to->p_data.cstr_allocated = 0; + + PathName pluginName = getPluginName(); + to->p_name.cstr_length = pluginName.length(); + to->p_name.cstr_address = (UCHAR*)(pluginName.c_str()); + to->p_name.cstr_allocated = 0; + + HANDSHAKE_DEBUG(fprintf(stderr, "extractDataFromPluginTo added plugin name (%d) and data (%d)\n", + to->p_name.cstr_length, to->p_data.cstr_length)); + + if (firstTime) + { + to->p_list.cstr_length = pluginList.length(); + to->p_list.cstr_address = (UCHAR*)(pluginList.c_str()); + to->p_list.cstr_allocated = 0; + HANDSHAKE_DEBUG(fprintf(stderr, "extractDataFromPluginTo added plugin list (%d len) to packet\n", + to->p_list.cstr_length)); + firstTime = false; + } + else + { + to->p_list.cstr_length = 0; + } +} + +const char* ClntAuthBlock::getLogin() +{ + return userName.nullStr(); +} + +const char* ClntAuthBlock::getPassword() +{ + return password.nullStr(); +} + +const unsigned char* ClntAuthBlock::getData(unsigned int* length) +{ + *length = dataForPlugin.getCount(); + return (*length) ? dataForPlugin.begin() : NULL; +} + +void ClntAuthBlock::putData(unsigned int length, const void* data) +{ + void* to = dataFromPlugin.getBuffer(length); + memcpy(to, data, length); +} + +int ClntAuthBlock::release() +{ + if (--refCounter != 0) + return 1; + + delete this; + return 0; +} + +bool ClntAuthBlock::checkPluginName(Firebird::PathName& nameToCheck) +{ + Remote::ParsedList parsed; + REMOTE_parseList(parsed, pluginList); + for (unsigned i = 0; i < parsed.getCount(); ++i) + { + if (parsed[i] == nameToCheck) + { + return true; + } + } + return false; +} + +void ClntAuthBlock::saveServiceDataTo(rem_port* port) +{ + port->port_user_name = userName; + port->port_password = password; +} + +void ClntAuthBlock::loadServiceDataFrom(rem_port* port) +{ + userName = port->port_user_name; + password = port->port_password; +} diff --git a/src/remote/inet.cpp b/src/remote/inet.cpp index c994475901..69f46d9b50 100644 --- a/src/remote/inet.cpp +++ b/src/remote/inet.cpp @@ -531,10 +531,11 @@ static Firebird::GlobalPtr port_mutex; static Firebird::GlobalPtr inet_ports; -rem_port* INET_analyze(const Firebird::PathName& file_name, +rem_port* INET_analyze(ClntAuthBlock* cBlock, + const Firebird::PathName& file_name, const TEXT* node_name, bool uv_flag, - Firebird::ClumpletReader &dpb) + ClumpletReader &dpb) { /************************************** * @@ -559,7 +560,12 @@ rem_port* INET_analyze(const Firebird::PathName& file_name, PACKET* packet = &rdb->rdb_packet; // Pick up some user identification information - Firebird::ClumpletWriter user_id(Firebird::ClumpletReader::UnTagged, MAX_DPB_SIZE); + Firebird::ClumpletWriter user_id(Firebird::ClumpletReader::UnTagged, 64000); + if (cBlock) + { + cBlock->extractDataFromPluginTo(user_id); + } + Firebird::string buffer; int eff_gid; int eff_uid; @@ -604,7 +610,7 @@ rem_port* INET_analyze(const Firebird::PathName& file_name, copy_p_cnct_repeat_array(cnct->p_cnct_versions, protocols_to_try1, cnct->p_cnct_count); - // Try connection using first set of protocols. punt if error + // Try connection using first set of protocols rem_port* port = inet_try_connect(packet, rdb, file_name, node_name, dpb); @@ -630,15 +636,51 @@ rem_port* INET_analyze(const Firebird::PathName& file_name, port = inet_try_connect(packet, rdb, file_name, node_name, dpb); } - if (packet->p_operation != op_accept) + P_ACPT* accept = NULL; + switch (packet->p_operation) { + case op_accept_data: + accept = &packet->p_acpd; + if (cBlock) + { + cBlock->storeDataForPlugin(packet->p_acpd.p_acpt_data.cstr_length, + packet->p_acpd.p_acpt_data.cstr_address); + cBlock->authComplete = packet->p_acpd.p_acpt_authenticated; + } + break; + + case op_accept: + if (cBlock) + { + cBlock->reset(&file_name); + } + accept = &packet->p_acpt; + break; + + case op_response: + try + { + Firebird::LocalStatus warning; // Ignore connect warnings for a while + REMOTE_check_response(&warning, rdb, packet); + } + catch(const Firebird::Exception&) + { + disconnect(port); + delete rdb; + throw; + } + // fall through - response is not a required accept + + default: disconnect(port); delete rdb; - Arg::Gds(isc_connect_reject).raise(); + break; } - port->port_protocol = packet->p_acpt.p_acpt_version; + fb_assert(accept); + fb_assert(port); + port->port_protocol = accept->p_acpt_version; // once we've decided on a protocol, concatenate the version // string to reflect it... @@ -647,19 +689,19 @@ rem_port* INET_analyze(const Firebird::PathName& file_name, delete port->port_version; port->port_version = REMOTE_make_string(temp.c_str()); - if (packet->p_acpt.p_acpt_architecture == ARCHITECTURE) { + if (accept->p_acpt_architecture == ARCHITECTURE) { port->port_flags |= PORT_symmetric; } - if (packet->p_acpt.p_acpt_type == ptype_rpc) { + if (accept->p_acpt_type == ptype_rpc) { port->port_flags |= PORT_rpc; } - if (packet->p_acpt.p_acpt_type != ptype_out_of_band) { + if (accept->p_acpt_type != ptype_out_of_band) { port->port_flags |= PORT_no_oob; } - if (packet->p_acpt.p_acpt_type == ptype_lazy_send) { + if (accept->p_acpt_type == ptype_lazy_send) { port->port_flags |= PORT_lazy; } @@ -1192,11 +1234,8 @@ static bool accept_connection(rem_port* port, const P_CNCT* cnct) } // end scope #endif // !WIN_NT - // store FULL user identity in port_user_name for security purposes - - Firebird::string temp; - temp.printf("%s.%ld.%ld", name.c_str(), eff_gid, eff_uid); - port->port_user_name = REMOTE_make_string(temp.c_str()); + // store user identity in port_user_name + port->port_user_name = name; port->port_protocol_str = REMOTE_make_string("TCPv4"); @@ -3076,6 +3115,7 @@ static bool packet_receive(rem_port* port, UCHAR* buffer, SSHORT buffer_length, } n = recv(port->port_handle, reinterpret_cast(buffer), buffer_length, 0); + // ->decrypt inetErrNo = INET_ERRNO; if (n != -1 || !INTERRUPT_ERROR(inetErrNo)) @@ -3144,6 +3184,8 @@ static bool packet_send( rem_port* port, const SCHAR* buffer, SSHORT buffer_leng const char* data = buffer; SSHORT length = buffer_length; + // ->encrypt + while (length) { #ifdef DEBUG diff --git a/src/remote/inet_proto.h b/src/remote/inet_proto.h index b095e3903d..6c42ea79d9 100644 --- a/src/remote/inet_proto.h +++ b/src/remote/inet_proto.h @@ -31,7 +31,8 @@ namespace Firebird class ClumpletReader; } -rem_port* INET_analyze(const Firebird::PathName&, const TEXT*, bool, Firebird::ClumpletReader&); +rem_port* INET_analyze(ClntAuthBlock*, const Firebird::PathName&, const TEXT*, + bool, Firebird::ClumpletReader&); rem_port* INET_connect(const TEXT*, struct packet*, USHORT, Firebird::ClumpletReader*); rem_port* INET_reconnect(SOCKET); rem_port* INET_server(SOCKET); diff --git a/src/remote/os/win32/wnet.cpp b/src/remote/os/win32/wnet.cpp index 64b1ecae5c..e787413aeb 100644 --- a/src/remote/os/win32/wnet.cpp +++ b/src/remote/os/win32/wnet.cpp @@ -507,7 +507,7 @@ static bool accept_connection( rem_port* port, const P_CNCT* cnct) { case CNCT_user: id.getString(name); - port->port_user_name = REMOTE_make_string(name.c_str()); + port->port_user_name = name; break; case CNCT_passwd: diff --git a/src/remote/protocol.cpp b/src/remote/protocol.cpp index afef1d1a60..555dedb661 100644 --- a/src/remote/protocol.cpp +++ b/src/remote/protocol.cpp @@ -246,6 +246,7 @@ bool_t xdr_protocol(XDR* xdrs, PACKET* p) p_cnct::p_cnct_repeat* tail; const rem_port* port; P_ACPT *accept; + P_ACPD *accept_with_data; P_ATCH *attach; P_RESP *response; P_CMPL *compile; @@ -329,6 +330,18 @@ bool_t xdr_protocol(XDR* xdrs, PACKET* p) DEBUG_PRINTSIZE(xdrs, p->p_operation); return P_TRUE(xdrs, p); + case op_accept_data: + accept_with_data = &p->p_acpd; + MAP(xdr_short, reinterpret_cast(accept_with_data->p_acpt_version)); + MAP(xdr_enum, reinterpret_cast(accept_with_data->p_acpt_architecture)); + MAP(xdr_u_short, accept_with_data->p_acpt_type); + MAP(xdr_cstring, accept_with_data->p_acpt_data); + MAP(xdr_cstring, accept_with_data->p_acpt_plugin); + MAP(xdr_u_short, accept_with_data->p_acpt_authenticated); +//??? fprintf(stderr, "data length %d\n", accept_with_data->p_acpt_data.cstr_length); + DEBUG_PRINTSIZE(xdrs, p->p_operation); + return P_TRUE(xdrs, p); + case op_connect_request: case op_aux_connect: request = &p->p_req; @@ -755,6 +768,7 @@ bool_t xdr_protocol(XDR* xdrs, PACKET* p) P_AUTH_CONT* auth = &p->p_auth_cont; MAP(xdr_cstring, auth->p_data); MAP(xdr_cstring, auth->p_name); + MAP(xdr_cstring, auth->p_list); DEBUG_PRINTSIZE(xdrs, p->p_operation); return P_TRUE(xdrs, p); diff --git a/src/remote/protocol.h b/src/remote/protocol.h index b83fcf76bf..7ca2d9c300 100644 --- a/src/remote/protocol.h +++ b/src/remote/protocol.h @@ -99,8 +99,7 @@ const USHORT FB_PROTOCOL_MASK = static_cast(~FB_PROTOCOL_FLAG); const USHORT PROTOCOL_VERSION11 = (FB_PROTOCOL_FLAG | 11); // Protocol 12 has support for asynchronous call op_cancel. -// Currently implemented asynchronously only for TCP/IP -// on superserver and superclassic. +// Currently implemented asynchronously only for TCP/IP. const USHORT PROTOCOL_VERSION12 = (FB_PROTOCOL_FLAG | 12); @@ -305,6 +304,8 @@ enum P_OP op_ping = 93, + op_accept_data = 94, // Server accepts connection and returns some data to client + op_max }; @@ -397,7 +398,10 @@ const UCHAR CNCT_host = 4; const UCHAR CNCT_group = 5; // Effective Unix group id const UCHAR CNCT_user_verification = 6; // Attach/create using this connection // will use user verification - +const UCHAR CNCT_specific_data = 7; // Some data, needed for user verification on server +const UCHAR CNCT_plugin_name = 8; // Name of plugin, which generated that data +const UCHAR CNCT_login = 9; // Same data as isc_dpb_user_name +const UCHAR CNCT_plugin_list = 10; // List of plugins, available on client typedef struct bid // BLOB ID { @@ -430,6 +434,16 @@ typedef struct p_acpt USHORT p_acpt_type; // Minimum type } P_ACPT; +// Accept Block with Data (Server response to connect block, start with P.13) + +struct p_acpd : public p_acpt +{ + CSTRING p_acpt_data; // Returned auth data + CSTRING p_acpt_plugin; // Plugin to continue with + USHORT p_acpt_authenticated; // Auth complete in single step (few! strange...) +}; +typedef p_acpd P_ACPD; + // Generic Response block typedef struct p_resp @@ -644,8 +658,9 @@ typedef struct p_trau typedef struct p_auth_continue { - CSTRING p_data; // Request + CSTRING p_data; // Specific data CSTRING p_name; // Plugin name + CSTRING p_list; // Plugin list } P_AUTH_CONT; struct p_update_account @@ -684,6 +699,7 @@ typedef struct packet P_OP p_operation; // Operation/packet type P_CNCT p_cnct; // Connect block P_ACPT p_acpt; // Accept connection + P_ACPD p_acpd; // Accept connection with data P_RESP p_resp; // Generic response to a call P_ATCH p_atch; // Attach or create database P_RLSE p_rlse; // Release object diff --git a/src/remote/remot_proto.h b/src/remote/remot_proto.h index 93747dcaa1..532cfcc0ec 100644 --- a/src/remote/remot_proto.h +++ b/src/remote/remot_proto.h @@ -24,11 +24,27 @@ #ifndef REMOTE_REMOT_PROTO_H #define REMOTE_REMOT_PROTO_H +#include "../common/classes/fb_string.h" +#include "../common/config/config.h" +#include "../common/classes/RefCounted.h" +#include "../common/classes/objects_array.h" +#include "../common/xdr.h" +#include "../remote/protocol.h" + namespace Firebird { class ClumpletReader; } +namespace Remote +{ + typedef Firebird::ObjectsArray ParsedList; +} + +struct rem_port; +struct rem_fmt; +struct Rdb; + void REMOTE_cleanup_transaction (struct Rtr *); ULONG REMOTE_compute_batch_size (rem_port*, USHORT, P_OP, const rem_fmt*); void REMOTE_get_timeout_params(rem_port* port, Firebird::ClumpletReader* pb); @@ -41,6 +57,12 @@ void REMOTE_reset_request (struct Rrq *, struct RMessage*); void REMOTE_reset_statement (struct Rsr *); void REMOTE_save_status_strings (ISC_STATUS *); bool_t REMOTE_getbytes (XDR*, SCHAR*, u_int); +bool REMOTE_legacy_auth(const char* nm, int protocol); +Firebird::RefPtr REMOTE_get_config(const Firebird::PathName* dbName); +void REMOTE_parseList(Remote::ParsedList&, Firebird::PathName); +void REMOTE_mergeList(Firebird::PathName& list, const Remote::ParsedList& parsed); +void REMOTE_check_response(Firebird::IStatus* warning, Rdb* rdb, PACKET* packet); + +#define HANDSHAKE_DEBUG(A) #endif // REMOTE_REMOT_PROTO_H - diff --git a/src/remote/remote.cpp b/src/remote/remote.cpp index ccb508d263..b30c57f40a 100644 --- a/src/remote/remote.cpp +++ b/src/remote/remote.cpp @@ -35,6 +35,7 @@ #include "../jrd/thread_proto.h" #include "../common/config/config.h" #include "../common/classes/init.h" +#include "../common/db_alias.h" #include "firebird/Provider.h" #ifdef DEV_BUILD @@ -781,9 +782,9 @@ rem_port::~rem_port() port_events_shutdown(this); } + delete port_srv_auth; delete port_version; delete port_connection; - delete port_user_name; delete port_host; delete port_protocol_str; delete port_address_str; @@ -792,7 +793,7 @@ rem_port::~rem_port() delete port_packet_vector; #endif - delete port_auth; + delete port_crypt_keys; #ifdef DEV_BUILD --portCounter; @@ -873,3 +874,270 @@ Firebird::string rem_port::getRemoteId() const return id; } + +bool REMOTE_legacy_auth(const char* nm, int p) +{ + const char* legacyTrusted = "WIN_SSPI"; + if (fb_utils::stricmp(legacyTrusted, nm) == 0 && + (p == PROTOCOL_VERSION11 || p == PROTOCOL_VERSION12)) + { + return true; + } + + const char* legacyAuth = "LEGACY_AUTH"; + if (fb_utils::stricmp(legacyAuth, nm) == 0 && p < PROTOCOL_VERSION11) + { + return true; + } + + return false; +} + +Firebird::PathName ClntAuthBlock::getPluginName() +{ + return plugins.hasData() ? plugins.name() : ""; +} + +void ClntAuthBlock::extractDataFromPluginTo(Firebird::ClumpletWriter& user_id) +{ + // Add user login name + if (userName.hasData()) + { + user_id.insertString(CNCT_login, userName); + } + + // Add plugin name + Firebird::PathName pluginName = getPluginName(); + if (pluginName.hasData()) + { + user_id.insertPath(CNCT_plugin_name, pluginName); + } + + // Add plugin list + if (pluginList.hasData()) + { + user_id.insertPath(CNCT_plugin_list, pluginList); + } + + // This is specially tricky field - user_id is limited with 255 bytes per entry, + // and we have no ways to override this limit cause it can be send to any version server. + // Therefore divide data into 254-byte parts, leaving first byte for the number of that part. + // This appears more reliable than put them in strict order. + unsigned int remaining = dataFromPlugin.getCount(); + UCHAR part = 0; + UCHAR buffer[255]; + const UCHAR* ptr = dataFromPlugin.begin(); + while (remaining > 0) + { + unsigned int step = remaining; + if (step > 254) + step = 254; + remaining -= step; + buffer[0] = part++; + fb_assert(part || remaining == 0); + memcpy(&buffer[1], ptr, step); + ptr += step; + + user_id.insertBytes(CNCT_specific_data, buffer, step + 1); + } +} + +void ClntAuthBlock::reset(const Firebird::PathName* fileName) +{ + dataForPlugin.clear(); + dataFromPlugin.clear(); + authComplete = false; + firstTime = true; + pluginList = REMOTE_get_config(fileName)->getPlugins(Firebird::PluginType::AuthClient); + plugins.set(pluginList.c_str()); +} + +void ClntAuthBlock::storeDataForPlugin(unsigned int length, const unsigned char* data) +{ + dataForPlugin.assign(data, length); + HANDSHAKE_DEBUG(fprintf(stderr, "Cln: accepted data for plugin length=%d\n", length)); +} + +Firebird::RefPtr REMOTE_get_config(const Firebird::PathName* dbName) +{ + if (dbName) + { + Firebird::RefPtr rc; + Firebird::PathName dummy; + ResolveDatabaseAlias(*dbName, dummy, &rc); + return rc; + } + return Config::getDefaultConfig(); +} + +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); + list.ltrim(" \t,;"); + } +} + +void REMOTE_mergeList(Firebird::PathName& list, const Remote::ParsedList& parsed) +{ + list.erase(); + for (unsigned i = 0; i < parsed.getCount(); ++i) + { + if (list.hasData()) + { + list += ' '; + } + list += parsed[i]; + } +} + +void REMOTE_check_response(Firebird::IStatus* warning, Rdb* rdb, PACKET* packet) +{ +/************************************** + * + * R E M O T E _ c h e c k _ r e s p o n s e + * + ************************************** + * + * Functional description + * Check response to a remote call. + * + **************************************/ + + // Get status vector + + const ISC_STATUS success_vector[] = {isc_arg_gds, FB_SUCCESS, isc_arg_end}; + const ISC_STATUS *vector = success_vector; + if (packet->p_resp.p_resp_status_vector) + { + vector = packet->p_resp.p_resp_status_vector->value(); + } + + // Translate any gds codes into local operating specific codes + + Firebird::SimpleStatusVector newVector; + rem_port* port = rdb->rdb_port; + + while (*vector != isc_arg_end) + { + const ISC_STATUS vec = *vector++; + newVector.push(vec); + + switch ((USHORT) vec) + { + case isc_arg_warning: + case isc_arg_gds: + if (port->port_protocol < PROTOCOL_VERSION10) + { + fb_assert(vec == isc_arg_gds); + newVector.push(gds__encode(*vector++, 0)); + } + else + newVector.push(*vector++); + break; + + case isc_arg_cstring: + newVector.push(*vector++); + // fall down + + default: + newVector.push(*vector++); + break; + } + } + + newVector.push(isc_arg_end); + vector = newVector.begin(); + + const ISC_STATUS pktErr = vector[1]; + if (pktErr == isc_shutdown || pktErr == isc_att_shutdown) + { + port->port_flags |= PORT_rdb_shutdown; + } + + if ((packet->p_operation == op_response || packet->p_operation == op_response_piggyback) && + !vector[1]) + { + warning->set(vector); + return; + } + + if (!vector[1]) + { + Firebird::Arg::Gds(isc_net_read_err).raise(); + } + + Firebird::status_exception::raise(vector); +} + +const ParametersSet dpbParam = {isc_dpb_dummy_packet_interval, + isc_dpb_user_name, + isc_dpb_auth_block, + isc_dpb_password, + isc_dpb_password_enc, + isc_dpb_trusted_auth, + isc_dpb_auth_plugin_name, + isc_dpb_auth_plugin_list, + isc_dpb_specific_auth_data, + isc_dpb_address_path, + isc_dpb_process_id, + isc_dpb_process_name, + isc_dpb_encrypt_key}; + +const ParametersSet spbParam = {isc_spb_dummy_packet_interval, + isc_spb_user_name, + isc_spb_auth_block, + isc_spb_password, + isc_spb_password_enc, + isc_spb_trusted_auth, + isc_spb_auth_plugin_name, + isc_spb_auth_plugin_list, + isc_spb_specific_auth_data, + isc_spb_address_path, + isc_spb_process_id, + isc_spb_process_name, + 0}; + +const ParametersSet spbStartParam = {0, + 0, + isc_spb_auth_block, + 0, + 0, + isc_spb_trusted_auth, + isc_spb_auth_plugin_name, + isc_spb_auth_plugin_list, + isc_spb_specific_auth_data, + 0, + 0, + 0, + 0}; // Need new parameter here + +const ParametersSet spbInfoParam = {0, + 0, + isc_info_svc_auth_block, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0}; diff --git a/src/remote/remote.h b/src/remote/remote.h index 5bcd1f00bf..c9d2f58a91 100644 --- a/src/remote/remote.h +++ b/src/remote/remote.h @@ -35,14 +35,17 @@ #include "../common/ThreadData.h" #include "../common/ThreadStart.h" #include "../common/thd.h" +#include "../common/Auth.h" #include "../common/classes/objects_array.h" #include "../common/classes/fb_string.h" #include "../common/classes/ClumpletWriter.h" #include "../common/classes/RefMutex.h" #include "../common/StatusHolder.h" #include "../common/classes/RefCounted.h" +#include "../common/classes/GetPlugins.h" #include "firebird/Provider.h" +#include "firebird/Auth.h" #ifndef WIN_NT #include @@ -83,11 +86,10 @@ const int BLOB_LENGTH = 16384; // fwd. decl. namespace Firebird { class Exception; -} -namespace Firebird { class IEventCallback; } struct rem_port; +class RemCryptKey; typedef Firebird::AutoPtr > UCharArrayAutoPtr; @@ -100,6 +102,20 @@ typedef Firebird::RefPtr ServEvents; typedef Firebird::RefPtr ServService; +// this set of parameters help use same functions +// for both services and databases attachments +struct ParametersSet +{ + UCHAR dummy_packet_interval, user_name, auth_block, + password, password_enc, trusted_auth, + plugin_name, plugin_list, specific_data, + address_path, process_id, process_name, + encrypt_key; +}; + +extern const ParametersSet dpbParam, spbParam, spbStartParam, spbInfoParam; + + struct Svc : public Firebird::GlobalStorage { ServService svc_iface; // service interface @@ -578,9 +594,109 @@ class ServerAuthBase { public: virtual ~ServerAuthBase(); - virtual bool authenticate(rem_port* port, PACKET* send, const cstring* data) = 0; + virtual bool authenticate(rem_port* port, PACKET* send) = 0; }; +typedef Firebird::GetPlugins AuthClientPlugins; + +// Representation of authentication data, visible for plugin +// Transfered in format, depending upon type of the packet (phaze of handshake) +class ClntAuthBlock : public Firebird::StdPlugin +{ +private: + Firebird::PathName pluginList; // To be passed to server + Firebird::string userName, password; // Used by plugin, taken from DPB + // That two are legacy encrypted password, trusted auth data and so on - what plugin needs + Firebird::UCharBuffer dataForPlugin, dataFromPlugin; + + bool hasCryptKey; // DPB contains disk crypt key, may be passed only over crypted wire + +public: + AuthClientPlugins plugins; + bool authComplete; // Set as response from client that authentication accepted + bool firstTime; // Invoked first time after reset + + ClntAuthBlock(const Firebird::PathName* fileName); + + void storeDataForPlugin(unsigned int length, const unsigned char* data); + void resetDataFromPlugin(); + void extractDataFromPluginTo(Firebird::ClumpletWriter& dpb, const ParametersSet* tags, int protocol); + void extractDataFromPluginTo(P_AUTH_CONT* to); + void load(Firebird::ClumpletReader& dpb, const ParametersSet*); + void extractDataFromPluginTo(Firebird::ClumpletWriter& user_id); + void reset(const Firebird::PathName* fileName); + bool checkPluginName(Firebird::PathName& nameToCheck); + void saveServiceDataTo(rem_port*); + void loadServiceDataFrom(rem_port*); + Firebird::PathName getPluginName(); + +// Auth::IClientBlock implementation + int release(); + const char* FB_CARG getLogin(); + const char* FB_CARG getPassword(); + const unsigned char* FB_CARG getData(unsigned int* length); + void FB_CARG putData(unsigned int length, const void* data); +}; + +// Representation of authentication data, visible for plugin +// Transfered from client data in format, suitable for plugins access +typedef Firebird::GetPlugins AuthServerPlugins; + +class SrvAuthBlock : public Firebird::StdPlugin +{ +private: + Firebird::string userName; + Firebird::PathName pluginName, pluginList; + + // That two may be legacy encrypted password, trusted auth data and so on + Firebird::UCharBuffer dataForPlugin, dataFromPlugin; + + Firebird::PathName dbPath; + bool flComplete, firstTime; + +public: + AuthServerPlugins* plugins; + Auth::WriterImplementation authBlockWriter; + + SrvAuthBlock() + : userName(getPool()), pluginName(getPool()), pluginList(getPool()), + dataForPlugin(getPool()), dataFromPlugin(getPool()), + dbPath(getPool()), + flComplete(false), firstTime(true), + plugins(NULL) + { } + + ~SrvAuthBlock() + { + delete plugins; + } + + void extractDataFromPluginTo(cstring* to); + void extractDataFromPluginTo(P_AUTH_CONT* to); + bool authCompleted(bool flag = false); + void setPath(const Firebird::PathName* dbPath); + void setUser(const Firebird::string& user); + const char* getPath(); + void load(Firebird::ClumpletReader& userId); + const char* getPluginName(); + void setPluginList(const Firebird::string& name); + const char* getPluginList(); + void setPluginName(const Firebird::string& name); + void extractPluginName(cstring* to); + void setDataForPlugin(const Firebird::UCharBuffer& data); + void setDataForPlugin(const cstring& data); + void createPluginsItr(); + void setDataForPlugin(const p_auth_continue* data); + void reset(); + +// Auth::IServerBlock implementation + int release(); + const char* FB_CARG getLogin(); + const unsigned char* FB_CARG getData(unsigned int* length); + void FB_CARG putData(unsigned int length, const void* data); +}; + + // port_flags const USHORT PORT_symmetric = 0x0001; // Server/client architectures are symmetic const USHORT PORT_rpc = 0x0002; // Protocol is remote procedure call @@ -669,8 +785,8 @@ struct rem_port : public Firebird::GlobalStorage, public Firebird::RefCounted rem_str* port_version; rem_str* port_host; // Our name rem_str* port_connection; // Name of connection - rem_str* port_user_name; - rem_str* port_passwd; + Firebird::string port_user_name; + Firebird::string port_password; rem_str* port_protocol_str; // String containing protocol name for this port rem_str* port_address_str; // Protocol-specific address string for the port Rpr* port_rpr; // port stored procedure reference @@ -682,7 +798,16 @@ struct rem_port : public Firebird::GlobalStorage, public Firebird::RefCounted OBJCT port_last_object_id; // cached last id Firebird::ObjectsArray< Firebird::Array > port_queue; size_t port_qoffset; // current packet in the queue - ServerAuthBase* port_auth; + + // Authentication and crypt stuff + ServerAuthBase* port_srv_auth; + Firebird::RefPtr port_srv_auth_block; + RemCryptKey* port_crypt_keys; // linked list of available wire crypt keys + bool port_need_disk_crypt; // set when appropriate DPB/SPB item is present + // requires wire crypt active before attachDatabase() + Firebird::RefPtr port_send_cipher, port_recv_cipher; + // when not NULL - used to encrypt transferred wire data and to decrypt received one + UCharArrayAutoPtr port_buffer; public: @@ -705,11 +830,14 @@ public: port_packet_vector(0), #endif port_objects(getPool()), port_version(0), port_host(0), - port_connection(0), port_user_name(0), port_passwd(0), port_protocol_str(0), + port_connection(0), port_user_name(getPool()), port_password(getPool()), port_protocol_str(0), port_address_str(0), port_rpr(0), port_statement(0), port_receive_rmtque(0), port_requests_queued(0), port_xcc(0), port_deferred_packets(0), port_last_object_id(0), port_queue(getPool()), port_qoffset(0), - port_auth(0), port_buffer(FB_NEW(getPool()) UCHAR[rpt]) + port_srv_auth(NULL), port_srv_auth_block(NULL), + port_crypt_keys(NULL), port_need_disk_crypt(false), + port_send_cipher(NULL), port_recv_cipher(NULL), + port_buffer(FB_NEW(getPool()) UCHAR[rpt]) { addRef(); memset (&port_linger, 0, sizeof port_linger); @@ -899,7 +1027,6 @@ public: }; - // Queuing structure for Client batch fetches typedef void (*t_rmtque_fn)(rem_port*, rmtque*, USHORT); @@ -949,4 +1076,45 @@ private: Firebird::Mutex m_mutex; }; + +// Element of known crypt keys list +class RemCryptKey : public Firebird::GlobalStorage +{ +private: + RemCryptKey* next; + Firebird::UCharBuffer key; + Firebird::string name; + + RemCryptKey(unsigned int len, const UCHAR* data, const char* nm) + : next(NULL), key(getPool()), name(getPool()) + { + key.assign(data, len); + name = nm; + } + +public: + void append(unsigned int len, const UCHAR* data, const char* nm) + { + if (name == nm) + return; + if (next) + next->append(len, data, nm); + else + next = new RemCryptKey(len, data, nm); + } + + Firebird::UCharBuffer* choose() + { + RemCryptKey* ptr = next; + Firebird::UCharBuffer* rc = &key; + while (ptr) + { + if (ptr->key.getCount() > rc->getCount()) + rc = &ptr->key; + ptr = ptr->next; + } + return rc; + } +}; + #endif // REMOTE_REMOTE_H diff --git a/src/remote/server/os/posix/inet_server.cpp b/src/remote/server/os/posix/inet_server.cpp index 66a1a5324d..0c43ec8d5a 100644 --- a/src/remote/server/os/posix/inet_server.cpp +++ b/src/remote/server/os/posix/inet_server.cpp @@ -91,6 +91,7 @@ #include "../common/classes/ImplementHelper.h" #include "../auth/SecurityDatabase/LegacyServer.h" #include "../auth/trusted/AuthSspi.h" +#include "../auth/SecureRemotePassword/server/SrpServer.h" #ifdef UNIX #ifdef NETBSD @@ -368,6 +369,7 @@ int CLIB_ROUTINE main( int argc, char** argv) { // scope for interface ptr Firebird::PluginManagerInterfacePtr pi; Auth::registerLegacyServer(pi); + Auth::registerSrpServer(pi); #ifdef TRUSTED_AUTH Auth::registerTrustedServer(pi); #endif diff --git a/src/remote/server/server.cpp b/src/remote/server/server.cpp index ff5cdf63ad..d0dd7acedd 100644 --- a/src/remote/server/server.cpp +++ b/src/remote/server/server.cpp @@ -71,7 +71,6 @@ #include "../common/classes/DbImplementation.h" #include "../common/Auth.h" #include "../common/classes/GetPlugins.h" -#include "../common/db_alias.h" #include "../common/StatementMetadata.h" #include "../common/isc_f_proto.h" @@ -105,15 +104,6 @@ public: namespace { -// this sets of parameters help use same functions -// for both services and databases attachments -struct ParametersSet -{ - UCHAR address_path; -}; -const ParametersSet dpbParam = {isc_dpb_address_path}; -const ParametersSet spbParam = {isc_spb_address_path}; - // Disable attempts to brute-force logins/passwords class FailedLogin { @@ -200,7 +190,7 @@ public: } if (getCount() >= MAX_CONCURRENT_FAILURES) { - // it seems we are under attack - too many wrong logins !!! + // it seems we are under attack - too many wrong logins !! return true; } @@ -236,48 +226,137 @@ class ServerAuth : public GlobalStorage, public ServerAuthBase public: virtual void accept(rem_port* port, PACKET* send, Auth::WriterImplementation* authBlock) = 0; - ServerAuth(const PathName* dbName, ClumpletWriter* aPb, const Auth::AuthTags& aTags) - : config(getConfig(dbName)), - iPb(*aPb), - authItr(PluginType::AuthServer, FB_AUTH_SERVER_VERSION, upInfo, config), + ServerAuth(const PathName* aDbName, ClumpletWriter* aPb, const ParametersSet& aTags, rem_port* port) + : authItr(NULL), userName(getPool()), authServer(NULL), - tags(&aTags) + tags(&aTags), + srvrBlock(port->port_srv_auth_block), + dbName(getPool()) { - if (aPb->find(isc_dpb_user_name)) + if (!srvrBlock) + { + port->port_srv_auth_block = srvrBlock = new SrvAuthBlock; + } + + HANDSHAKE_DEBUG(fprintf(stderr, "ServerAuth()\n")); + + if (aPb->find(tags->user_name)) { aPb->getString(userName); + userName.upper(); + if (srvrBlock->getLogin() && userName != srvrBlock->getLogin()) + { + (Arg::Gds(isc_login) << Arg::Gds(isc_random) << "Client error - login does not match").raise(); + } + srvrBlock->setUser(userName); + HANDSHAKE_DEBUG(fprintf(stderr, "ServerAuth(): user name=%s\n", userName.c_str())); } + + const char* oldPath = srvrBlock->getPath(); + if (aDbName) + { + dbName = *aDbName; + if (oldPath && (dbName != oldPath)) + { + HANDSHAKE_DEBUG(fprintf(stderr, "old='%s' new='%s'\n", oldPath, dbName.c_str())); + (Arg::Gds(isc_login) << Arg::Gds(isc_random) << "Client error - database name does not match").raise(); + } + srvrBlock->setPath(aDbName); + HANDSHAKE_DEBUG(fprintf(stderr, "ServerAuth(): db name=%s\n", dbName.c_str())); + } + + string x; + UCharBuffer u; + if (port->port_protocol >= PROTOCOL_VERSION13) + { + if (aPb->find(tags->plugin_name)) + { + aPb->getString(x); + srvrBlock->setPluginName(x); + HANDSHAKE_DEBUG(fprintf(stderr, "ServerAuth(): plugin name=%s\n", x.c_str())); + } + if (aPb->find(tags->plugin_list)) + { + aPb->getString(x); + srvrBlock->setPluginList(x); + HANDSHAKE_DEBUG(fprintf(stderr, "ServerAuth(): plugin list=%s\n", x.c_str())); + } + if (aPb->find(tags->specific_data)) + { + aPb->getData(u); + srvrBlock->setDataForPlugin(u); + HANDSHAKE_DEBUG(fprintf(stderr, "ServerAuth(): plugin data is %" SIZEFORMAT " len\n", u.getCount())); + } + else + { + HANDSHAKE_DEBUG(fprintf(stderr, "ServerAuth(): miss data with tag %d\n", tags->specific_data)); + } + } + else if (srvrBlock->getLogin() && aPb->find(tags->password_enc)) + { + srvrBlock->setPluginName("Legacy_Auth"); + srvrBlock->setPluginList("Legacy_Auth"); + aPb->getData(u); + srvrBlock->setDataForPlugin(u); + } +#ifdef WIN_NT + else if (aPb->find(tags->trusted_auth) && port->port_protocol >= PROTOCOL_VERSION11) + { + srvrBlock->setPluginName("Win_Sspi"); + srvrBlock->setPluginList("Win_Sspi"); + aPb->getData(u); + srvrBlock->setDataForPlugin(u); + } +#endif } ~ServerAuth() { } - bool authenticate(rem_port* port, PACKET* send, const cstring* data) + bool authenticate(rem_port* port, PACKET* send) { + if (srvrBlock->authCompleted()) + { + accept(port, send, &srvrBlock->authBlockWriter); + return true; + } + bool working = true; LocalStatus st; - while (working && authItr.hasData()) + srvrBlock->createPluginsItr(); + authItr = port->port_srv_auth_block->plugins; + if (!authItr) + { + if (port->port_protocol >= PROTOCOL_VERSION13) + { + send->p_operation = op_cont_auth; + srvrBlock->extractDataFromPluginTo(&send->p_auth_cont); + port->send(send); + memset(&send->p_auth_cont, 0, sizeof send->p_auth_cont); + return false; + } + } + + while (authItr && working && authItr->hasData()) { - bool first = false; if (! authServer) { - authServer = authItr.plugin(); - first = true; + authServer = authItr->plugin(); + srvrBlock->authBlockWriter.setMethod(authItr->name()); } - fb_assert(first || data); - authBlockInterface.setMethod(authItr.name()); - Auth::Result ar = first ? - authServer->startAuthentication(&st, tags, &iPb, &authBlockInterface) : - authServer->contAuthentication(&st, data->cstr_address, data->cstr_length, &authBlockInterface); + HANDSHAKE_DEBUG(fprintf(stderr, "ServerAuth calls plug %s\n", authItr->name())); + Auth::Result ar = authServer->authenticate(&st, srvrBlock, &srvrBlock->authBlockWriter); + srvrBlock->setPluginName(authItr->name()); cstring* s; switch(ar) { case Auth::AUTH_MORE_DATA: + HANDSHAKE_DEBUG(fprintf(stderr, "plugin wants more data\n")); if (port->port_protocol < PROTOCOL_VERSION11) { authServer = NULL; @@ -288,38 +367,18 @@ public: if (port->port_protocol >= PROTOCOL_VERSION13) { send->p_operation = op_cont_auth; - - s = &send->p_auth_cont.p_data; - s->cstr_allocated = 0; - // violate constness here safely - send operation does not modify data - authServer->getData(const_cast(&s->cstr_address), - &s->cstr_length); - - s = &send->p_auth_cont.p_name; - s->cstr_allocated = 0; - if (first) - { - // violate constness here safely - send operation does not modify data - s->cstr_address = (UCHAR*) authItr.name(); - s->cstr_length = strlen(authItr.name()); - } - else - { - s->cstr_length = 0; - } + srvrBlock->extractDataFromPluginTo(&send->p_auth_cont); } else { - if (Auth::legacy(authItr.name())) + if (REMOTE_legacy_auth(authItr->name(), port->port_protocol)) { // compatibility with FB 2.1 trusted send->p_operation = op_trusted_auth; s = &send->p_trau.p_trau_data; s->cstr_allocated = 0; - // violate constness here safely - send operation does not modify data - authServer->getData(const_cast(&s->cstr_address), - &s->cstr_length); + srvrBlock->extractDataFromPluginTo(s); } else { @@ -330,21 +389,27 @@ public: } port->send(send); + memset(&send->p_auth_cont, 0, sizeof send->p_auth_cont); return false; case Auth::AUTH_CONTINUE: - authItr.next(); + HANDSHAKE_DEBUG(fprintf(stderr, "Next plug suggested\n")); + authItr->next(); authServer = NULL; continue; case Auth::AUTH_SUCCESS: + HANDSHAKE_DEBUG(fprintf(stderr, "Ahh - success\n")); usernameFailedLogins->loginSuccess(userName); remoteFailedLogins->loginSuccess(port->getRemoteId()); authServer = NULL; - accept(port, send, &authBlockInterface); + srvrBlock->authCompleted(true); + accept(port, send, &srvrBlock->authBlockWriter); return true; case Auth::AUTH_FAILED: + HANDSHAKE_DEBUG(fprintf(stderr, "No luck today...\n")); + isc_print_status(st.get()); authServer = NULL; working = false; break; @@ -361,57 +426,51 @@ public: THREAD_SLEEP(FAILURE_DELAY * 1000); } - (Arg::Gds(isc_login) << Arg::StatusVector(st.get())).raise(); + Arg::Gds loginError(isc_login); +#ifndef DEV_BUILD + if (st.get()[1] == isc_missing_data_structures) +#endif + { + loginError << Arg::StatusVector(st.get()); + } + status_exception::raise(loginError.value()); return false; // compiler warning silencer } private: - RefPtr config; - Auth::DpbImplementation iPb; - Auth::WriterImplementation authBlockInterface; - GetPlugins authItr; + AuthServerPlugins* authItr; string userName; Auth::IServer* authServer; - const Auth::AuthTags* tags; + const ParametersSet* tags; + SrvAuthBlock* srvrBlock; - RefPtr getConfig(const PathName* dbName) - { - if (dbName) - { - RefPtr config; - PathName dummy; - ResolveDatabaseAlias(*dbName, dummy, &config); - return config; - } - return Config::getDefaultConfig(); - } +protected: + PathName dbName; }; class DatabaseAuth : public ServerAuth { public: - DatabaseAuth(const PathName& adbName, ClumpletWriter* dpb, P_OP op) - : ServerAuth(&adbName, dpb, Auth::DB_ATTACH_LIST), - dbName(getPool(), adbName), - pb(dpb), - operation(op) + DatabaseAuth(rem_port* port, const PathName& adbName, ClumpletWriter* dpb, P_OP op) + : ServerAuth(&adbName, dpb, dpbParam, port), + operation(op), + pb(dpb) { } void accept(rem_port* port, PACKET* send, Auth::WriterImplementation* authBlock); private: - PathName dbName; - AutoPtr pb; P_OP operation; + AutoPtr pb; }; class ServiceAttachAuth : public ServerAuth { public: - ServiceAttachAuth(const PathName& pmanagerName, ClumpletWriter* spb) - : ServerAuth(NULL, spb, Auth::SVC_ATTACH_LIST), + ServiceAttachAuth(rem_port* port, const PathName& pmanagerName, ClumpletWriter* spb) + : ServerAuth(NULL, spb, spbParam, port), managerName(getPool(), pmanagerName), pb(spb) { } @@ -584,6 +643,7 @@ static void append_request_next(server_req_t*, server_req_t**); static void attach_database(rem_port*, P_OP, P_ATCH*, PACKET*); static void attach_service(rem_port*, P_ATCH*, PACKET*); static void trusted_auth(rem_port*, const P_TRAU*, PACKET*); +static void continue_authentication(rem_port*, const p_auth_continue*, PACKET*); #ifdef NOT_USED_OR_REPLACED static void aux_connect(rem_port*, P_REQ*, PACKET*); @@ -610,6 +670,7 @@ static void release_sql_request(Rsr*); static void release_transaction(Rtr*); static void send_error(rem_port* port, PACKET* apacket, ISC_STATUS errcode); +static void send_error(rem_port* port, PACKET* apacket, const Firebird::Arg::StatusVector&); static void set_server(rem_port*, USHORT); static int shut_server(const int, const int, void*); static THREAD_ENTRY_DECLARE loopThread(THREAD_ENTRY_PARAM); @@ -1248,7 +1309,6 @@ static bool accept_connection(rem_port* port, P_CNCT* connect, PACKET* send) // Accept the physical connection send->p_operation = op_reject; - P_ACPT* accept = &send->p_acpt; if (!port->accept(connect)) { @@ -1256,7 +1316,6 @@ static bool accept_connection(rem_port* port, P_CNCT* connect, PACKET* send) return false; } - // Select the most appropriate protocol (this will get smarter) P_ARCH architecture = arch_generic; USHORT version = 0; @@ -1288,19 +1347,106 @@ static bool accept_connection(rem_port* port, P_CNCT* connect, PACKET* send) version = protocol->p_cnct_version; architecture = protocol->p_cnct_architecture; type = MIN(protocol->p_cnct_max_type, ptype_lazy_send); - send->p_operation = op_accept; + } + } + + HANDSHAKE_DEBUG(fprintf(stderr, "protoaccept a=%d (v>=13)=%d %d %d\n", + accepted, version >= PROTOCOL_VERSION13, version, PROTOCOL_VERSION13)); + + // We are going to try authentication handshake + LocalStatus status; + P_ACPT* accept = &send->p_acpt; + bool returnData = false; + if (accepted && version >= PROTOCOL_VERSION13) + { + HANDSHAKE_DEBUG(fprintf(stderr, "accept connection creates port_srv_auth_block\n")); + port->port_srv_auth_block = new SrvAuthBlock; + send->p_acpd.p_acpt_authenticated = 0; + + Firebird::ClumpletReader id(Firebird::ClumpletReader::UnTagged, + connect->p_cnct_user_id.cstr_address, + connect->p_cnct_user_id.cstr_length); + HANDSHAKE_DEBUG(fprintf(stderr, "accept connection is going to load data to port_srv_auth_block\n")); + port->port_srv_auth_block->load(id); + if (port->port_srv_auth_block->getLogin()) + { + port->port_user_name = port->port_srv_auth_block->getLogin(); + } + + HANDSHAKE_DEBUG(fprintf(stderr, "accept connection finished with port_srv_auth_block prepare, a=%d\n", accepted)); + + if (port->port_srv_auth_block->getPluginName()) + { + accept = &send->p_acpd; + Firebird::PathName file(connect->p_cnct_file.cstr_address, connect->p_cnct_file.cstr_length); + port->port_srv_auth_block->setPath(&file); + HANDSHAKE_DEBUG(fprintf(stderr, "accept connection calls createPluginsItr\n")); + port->port_srv_auth_block->createPluginsItr(); + + if (port->port_srv_auth_block->plugins) // We have all required data and iterator was created + { + HANDSHAKE_DEBUG(fprintf(stderr, "call plugin %s\n", port->port_srv_auth_block->getPluginName())); + + AuthServerPlugins* plugins = port->port_srv_auth_block->plugins; + for (; plugins->hasData(); plugins->next()) + { + port->port_srv_auth_block->authBlockWriter.setMethod(plugins->name()); + switch (port->port_srv_auth_block->plugins->plugin()-> + authenticate(&status, port->port_srv_auth_block, + &port->port_srv_auth_block->authBlockWriter)) + { + case Auth::AUTH_SUCCESS: + port->port_srv_auth_block->authCompleted(true); + send->p_acpd.p_acpt_authenticated = 1; + break; + case Auth::AUTH_FAILED: + { + Arg::Gds loginError(isc_login); +#ifndef DEV_BUILD + if (status.get()[1] == isc_missing_data_structures) +#endif + { + loginError << Arg::StatusVector(status.get()); + } + status.set(loginError.value()); + } + accepted = false; + break; + case Auth::AUTH_CONTINUE: + // try next plugin + continue; + case Auth::AUTH_MORE_DATA: + port->port_srv_auth_block->extractDataFromPluginTo(&send->p_acpd.p_acpt_data); + port->port_srv_auth_block->extractPluginName(&send->p_acpd.p_acpt_plugin); + returnData = true; + break; + } + break; + } + if (!plugins->hasData()) + { + accepted = false; + } + } } } // Send off out gracious acceptance or flag rejection if (!accepted) { - port->send(send); + if (!status.isSuccess()) + port->send_response(send, 0, 0, status.get(), false); + else + port->send(send); return false; } + accept->p_acpt_version = port->port_protocol = version; accept->p_acpt_architecture = architecture; accept->p_acpt_type = type; + send->p_operation = returnData ? op_accept_data : op_accept; + + HANDSHAKE_DEBUG(fprintf(stderr, "accepted ud=%d v=%x\n", returnData, version)); // and modify the version string to reflect the chosen protocol @@ -1502,13 +1648,13 @@ static void attach_database(rem_port* port, P_OP operation, P_ATCH* attach, PACK ClumpletReader::dpbList, MAX_DPB_SIZE, attach->p_atch_dpb.cstr_address, attach->p_atch_dpb.cstr_length); - port->port_auth = new DatabaseAuth(PathName(attach->p_atch_file.cstr_address, + port->port_srv_auth = new DatabaseAuth(port, PathName(attach->p_atch_file.cstr_address, attach->p_atch_file.cstr_length), wrt, operation); - if (port->port_auth->authenticate(port, send, NULL)) + if (port->port_srv_auth->authenticate(port, send)) { - delete port->port_auth; - port->port_auth = NULL; + delete port->port_srv_auth; + port->port_srv_auth = NULL; } } @@ -2082,22 +2228,6 @@ void rem_port::disconnect(PACKET* sendL, PACKET* receiveL) delete this->port_version; this->port_version = NULL; } - if (this->port_passwd) - { -#ifdef DEBUG_REMOTE_MEMORY - printf("disconnect(server) free string %x\n", this->port_passwd); -#endif - delete this->port_passwd; - this->port_passwd = NULL; - } - if (this->port_user_name) - { -#ifdef DEBUG_REMOTE_MEMORY - printf("disconnect(server) free string %x\n", this->port_user_name); -#endif - delete this->port_user_name; - this->port_user_name = NULL; - } if (this->port_host) { #ifdef DEBUG_REMOTE_MEMORY @@ -3071,11 +3201,11 @@ ISC_STATUS rem_port::get_slice(P_SLC * stuff, PACKET* sendL) class ServiceQueryAuth : public ServerAuth { public: - ServiceQueryAuth(OBJCT pinfoObject, ClumpletWriter* spb, + ServiceQueryAuth(rem_port* port, OBJCT pinfoObject, ClumpletWriter* spb, unsigned int pSendLength, const UCHAR* pSendItems, unsigned int pReceiveLength, const UCHAR* pReceiveItems, unsigned int pBufferLength) - : ServerAuth(NULL, spb, Auth::SVC_QUERY_LIST), + : ServerAuth(NULL, spb, spbInfoParam, port), pb(spb), sendItems(getPool(), ClumpletReader::SpbSendItems, MAX_DPB_SIZE, pSendItems, pSendLength, 0), receiveItems(getPool()), @@ -3255,17 +3385,24 @@ void rem_port::info(P_OP op, P_INFO* stuff, PACKET* sendL) FB_NEW(*getDefaultMemoryPool()) ClumpletWriter(*getDefaultMemoryPool(), ClumpletReader::WideUnTagged, MAX_DPB_SIZE); - delete port_auth; - - ///port_auth = NULL; - port_auth = new ServiceQueryAuth(stuff->p_info_object, wrt, + if (port_srv_auth_block) + { + port_srv_auth_block->reset(); + } + delete port_srv_auth; + // port_srv_auth = NULL; + port_srv_auth = new ServiceQueryAuth(this, stuff->p_info_object, wrt, stuff->p_info_items.cstr_length, stuff->p_info_items.cstr_address, info_len, info_buffer, stuff->p_info_buffer_length); - - if (port_auth->authenticate(this, sendL, NULL)) + if (port_user_name.hasData()) { - delete port_auth; - port_auth = NULL; + port_srv_auth_block->setUser(port_user_name); + } + + if (port_srv_auth->authenticate(this, sendL)) + { + delete port_srv_auth; + port_srv_auth = NULL; } return; } @@ -3638,11 +3775,10 @@ static bool process_packet(rem_port* port, PACKET* sendL, PACKET* receive, rem_p case op_connect: if (!accept_connection(port, &receive->p_cnct, sendL)) { - rem_str* string = port->port_user_name; - if (string) + const string& s = port->port_user_name; + if (s.hasData()) { - gds__log("SERVER/process_packet: connection rejected for %*.*s", - string->str_length, string->str_length, string->str_data); + gds__log("SERVER/process_packet: connection rejected for %s", s.c_str()); } if (port->port_server->srvr_flags & SRVR_multi_client) { port->port_state = rem_port::BROKEN; @@ -3669,10 +3805,14 @@ static bool process_packet(rem_port* port, PACKET* sendL, PACKET* receive, rem_p attach_service(port, &receive->p_atch, sendL); break; - case op_trusted_auth: + case op_trusted_auth: // PROTOCOL < 13 trusted_auth(port, &receive->p_trau, sendL); break; + case op_cont_auth: // PROTOCOL >= 13 + continue_authentication(port, &receive->p_auth_cont, sendL); + break; + case op_update_account_info: case op_authenticate_user: send_error(port, sendL, isc_wish_list); @@ -3937,16 +4077,56 @@ static void trusted_auth(rem_port* port, const P_TRAU* p_trau, PACKET* send) * Server side of trusted auth handshake. * **************************************/ - ServerAuthBase* sa = port->port_auth; + ServerAuthBase* sa = port->port_srv_auth; if (! sa) { send_error(port, send, isc_unavailable); } - if (sa->authenticate(port, send, &p_trau->p_trau_data)) + if (port->port_protocol < PROTOCOL_VERSION11 || port->port_protocol >= PROTOCOL_VERSION13) + { + send_error(port, send, (Arg::Gds(isc_random) << "Operation not supported for network protocol")); + } + + HANDSHAKE_DEBUG(fprintf(stderr, "trusted_auth\n")); + port->port_srv_auth_block->setDataForPlugin(p_trau->p_trau_data); + if (sa->authenticate(port, send)) { delete sa; - port->port_auth = NULL; + port->port_srv_auth = NULL; + } +} + + +static void continue_authentication(rem_port* port, const p_auth_continue* p_auth_c, PACKET* send) +{ +/************************************** + * + * c o n t i n u e _ a u t h e n t i c a t i o n + * + ************************************** + * + * Functional description + * Server side of multi-hop auth handshake. + * + **************************************/ + ServerAuthBase* sa = port->port_srv_auth; + if (! sa) + { + send_error(port, send, isc_unavailable); + } + + if (port->port_protocol < PROTOCOL_VERSION13) + { + send_error(port, send, (Arg::Gds(isc_random) << "Operation not supported for network protocol")); + } + + HANDSHAKE_DEBUG(fprintf(stderr, "continue_authentication\n")); + port->port_srv_auth_block->setDataForPlugin(p_auth_c); + if (sa->authenticate(port, send)) + { + delete sa; + port->port_srv_auth = NULL; } } @@ -4671,6 +4851,14 @@ static void send_error(rem_port* port, PACKET* apacket, ISC_STATUS errcode) port->send_response(apacket, 0, 0, &status_vector, false); } +// Maybe this can be a member of rem_port? +static void send_error(rem_port* port, PACKET* apacket, const Firebird::Arg::StatusVector& err) +{ + LocalStatus status_vector; + err.copyTo(&status_vector); + port->send_response(apacket, 0, 0, &status_vector, false); +} + static void attach_service(rem_port* port, P_ATCH* attach, PACKET* sendL) { @@ -4682,13 +4870,13 @@ static void attach_service(rem_port* port, P_ATCH* attach, PACKET* sendL) // Check credentials in default way right now ClumpletWriter* wrt = FB_NEW(*getDefaultMemoryPool()) ClumpletWriter(*getDefaultMemoryPool(), ClumpletReader::spbList, MAX_DPB_SIZE, attach->p_atch_dpb.cstr_address, attach->p_atch_dpb.cstr_length); - port->port_auth = new ServiceAttachAuth(PathName(attach->p_atch_file.cstr_address, + port->port_srv_auth = new ServiceAttachAuth(port, PathName(attach->p_atch_file.cstr_address, attach->p_atch_file.cstr_length), wrt); - if (port->port_auth->authenticate(port, sendL, NULL)) + if (port->port_srv_auth->authenticate(port, sendL)) { - delete port->port_auth; - port->port_auth = NULL; + delete port->port_srv_auth; + port->port_srv_auth = NULL; } } else @@ -4696,6 +4884,11 @@ static void attach_service(rem_port* port, P_ATCH* attach, PACKET* sendL) // Delay authentication till service start/info request ClumpletWriter spb(ClumpletReader::spbList, MAX_DPB_SIZE, attach->p_atch_dpb.cstr_address, attach->p_atch_dpb.cstr_length); + if (spb.find(isc_spb_user_name)) + { + spb.getString(port->port_user_name); + port->port_user_name.upper(); + } port->service_attach(manager.c_str(), &spb, sendL, false); } } @@ -4831,8 +5024,8 @@ ISC_STATUS rem_port::service_end(P_RLSE* /*release*/, PACKET* sendL) class ServiceStartAuth : public ServerAuth { public: - ServiceStartAuth(PathName& dbName, ClumpletWriter* authSpb, ClumpletWriter* origSpb) - : ServerAuth(dbName.hasData() ? &dbName : NULL, authSpb, Auth::SVC_START_LIST), + ServiceStartAuth(rem_port* port, PathName& dbName, ClumpletWriter* authSpb, ClumpletWriter* origSpb) + : ServerAuth(dbName.hasData() ? &dbName : NULL, authSpb, spbStartParam, port), authPb(authSpb), startPb(origSpb) { } @@ -4912,8 +5105,10 @@ void rem_port::service_start(P_INFO * stuff, PACKET* sendL) case isc_spb_dbname: spb->getPath(dbName); break; - case isc_spb_trusted_auth: - authParams->insertBytes(isc_spb_trusted_auth, spb->getBytes(), spb->getClumpLength()); + case isc_spb_specific_auth_data: + case isc_spb_auth_plugin_list: + case isc_spb_auth_plugin_name: + authParams->insertBytes(spb->getClumpTag(), spb->getBytes(), spb->getClumpLength()); spb->deleteClumplet(); fDel = true; break; @@ -4924,14 +5119,22 @@ void rem_port::service_start(P_INFO * stuff, PACKET* sendL) } } - delete port_auth; - ///port_auth = NULL; - port_auth = new ServiceStartAuth(dbName, authParams, spb); - - if (port_auth->authenticate(this, sendL, NULL)) + if (port_srv_auth_block) { - delete port_auth; - port_auth = NULL; + port_srv_auth_block->reset(); + } + delete port_srv_auth; + ///port_srv_auth = NULL; + port_srv_auth = new ServiceStartAuth(this, dbName, authParams, spb); + if (port_user_name.hasData()) + { + port_srv_auth_block->setUser(port_user_name); + } + + if (port_srv_auth->authenticate(this, sendL)) + { + delete port_srv_auth; + port_srv_auth = NULL; } } } @@ -5666,3 +5869,258 @@ static int shut_server(const int, const int, void*) server_shutdown = true; return 0; } + +void SrvAuthBlock::extractDataFromPluginTo(cstring* to) +{ + to->cstr_allocated = 0; + to->cstr_length = dataFromPlugin.getCount(); + to->cstr_address = dataFromPlugin.begin(); +} + +bool SrvAuthBlock::authCompleted(bool flag) +{ + if (flag) + flComplete = true; + return flComplete; +} + +void SrvAuthBlock::setPath(const Firebird::PathName* aDbPath) +{ + if (aDbPath) + dbPath = *aDbPath; + else + dbPath = ""; +} + +void SrvAuthBlock::setUser(const Firebird::string& user) +{ + userName = user; +} + +const char* SrvAuthBlock::getPath() +{ + return dbPath.nullStr(); +} + +void SrvAuthBlock::load(Firebird::ClumpletReader& id) +{ + // This array is needed only to make sure that all parts of specific data is present + UCHAR checkBytes[256]; + memset(checkBytes, 0, sizeof(checkBytes)); + UCHAR top = 0; + + for (id.rewind(); !id.isEof(); id.moveNext()) + { + switch (id.getClumpTag()) + { + case CNCT_login: + id.getString(userName); + userName.upper(); + HANDSHAKE_DEBUG(fprintf(stderr, "login %s\n", userName.c_str())); + break; + case CNCT_plugin_name: + id.getPath(pluginName); + firstTime = false; + HANDSHAKE_DEBUG(fprintf(stderr, "plugin %s\n", pluginName.c_str())); + break; + case CNCT_plugin_list: + id.getPath(pluginList); + HANDSHAKE_DEBUG(fprintf(stderr, "plugin list %s\n", pluginList.c_str())); + break; + case CNCT_specific_data: + { + string tmp; + const UCHAR* specData = id.getBytes(); + size_t len = id.getClumpLength(); + if (len > 1) + { + --len; + unsigned offset = specData[0]; + + if (offset + 1 > top) + top = offset + 1; + checkBytes[offset] = 1; + + offset *= 254; + ++specData; + dataForPlugin.grow(offset + len); + memcpy(&dataForPlugin[offset], specData, len); + } + } + break; + } + } + + for (UCHAR segment = 0; segment < top; ++segment) + { + if (!checkBytes[segment]) + { + (Arg::Gds(isc_random) << "Missing segment in specific data").raise(); + } + } + + HANDSHAKE_DEBUG(fprintf(stderr, "data %" SIZEFORMAT "\n", dataForPlugin.getCount())); +} + +const char* SrvAuthBlock::getPluginName() +{ + return pluginName.nullStr(); +} + +void SrvAuthBlock::setPluginName(const Firebird::string& name) +{ + pluginName = name.ToPathName(); +} + +void SrvAuthBlock::setPluginList(const Firebird::string& list) +{ + if (firstTime) + { + pluginList = list.ToPathName(); + } + if (pluginList.hasData()) + { + firstTime = false; + } +} + +void SrvAuthBlock::setDataForPlugin(const Firebird::UCharBuffer& data) +{ + dataForPlugin.assign(data.begin(), data.getCount()); +} + +void SrvAuthBlock::setDataForPlugin(const cstring& data) +{ + dataForPlugin.assign(data.cstr_address, data.cstr_length); +} + +void SrvAuthBlock::setDataForPlugin(const p_auth_continue* data) +{ + dataForPlugin.assign(data->p_data.cstr_address, data->p_data.cstr_length); + HANDSHAKE_DEBUG(fprintf(stderr, "setDataForPlugin=%d firstTime = %d nm=%d ls=%d login='%s'\n", + data->p_data.cstr_length, firstTime, data->p_name.cstr_length, + data->p_list.cstr_length, userName.c_str())); + if (firstTime) + { + pluginName.assign(data->p_name.cstr_address, data->p_name.cstr_length); + pluginList.assign(data->p_list.cstr_address, data->p_list.cstr_length); + + firstTime = false; + } +} + +void SrvAuthBlock::extractDataFromPluginTo(P_AUTH_CONT* to) +{ + to->p_data.cstr_length = dataFromPlugin.getCount(); + to->p_data.cstr_address = dataFromPlugin.begin(); + to->p_data.cstr_allocated = 0; + + extractPluginName(&to->p_name); +} + +void SrvAuthBlock::extractPluginName(cstring* to) +{ + to->cstr_length = pluginName.length(); + to->cstr_address = (UCHAR*)(pluginName.c_str()); + to->cstr_allocated = 0; +} + +int SrvAuthBlock::release() +{ + if (--refCounter != 0) + return 1; + + delete this; + return 0; +} + +const char* SrvAuthBlock::getLogin() +{ + return userName.nullStr(); +} + +const unsigned char* SrvAuthBlock::getData(unsigned int* length) +{ + *length = dataForPlugin.getCount(); + return (*length) ? dataForPlugin.begin() : NULL; +} + +void SrvAuthBlock::putData(unsigned int length, const void* data) +{ + memcpy(dataFromPlugin.getBuffer(length), data, length); +} + +void SrvAuthBlock::createPluginsItr() +{ + if (firstTime || plugins) + { + return; + } + + Remote::ParsedList fromClient; + REMOTE_parseList(fromClient, pluginList); + + RefPtr myConfig = REMOTE_get_config(dbPath.hasData() ? &dbPath : NULL); + Remote::ParsedList onServer; + REMOTE_parseList(onServer, myConfig->getPlugins(PluginType::AuthServer)); + + Remote::ParsedList final; + for (unsigned s = 0; s < onServer.getCount(); ++s) + { + // do not expect too long lists, therefore use double loop + for (unsigned c = 0; c < fromClient.getCount(); ++c) + { + if (onServer[s] == fromClient[c]) + { + final.push(onServer[s]); + } + } + } + + if (final.getCount() == 0) + { + HANDSHAKE_DEBUG(fprintf(stderr, "No matching plugins on server\n")); + (Arg::Gds(isc_login) +#ifdef DEV_BUILD + << Arg::Gds(isc_random) << "No matching plugins on server" +#endif + ).raise(); + } + + // reorder to make it match first, already passed, plugin data + for (unsigned f = 1; f < final.getCount(); ++f) + { + if (final[f] == pluginName) + { + final[f] = final[0]; + final[0] = pluginName; + break; + } + } + + // special case - last plugin from the list on the server may be used to check + // correctness of what previous one added to auth parameters block + if (final[final.getCount() - 1] != onServer[onServer.getCount() - 1]) + { + final.push(onServer[onServer.getCount() - 1]); + } + + REMOTE_mergeList(pluginList, final); + + plugins = new AuthServerPlugins(PluginType::AuthServer, FB_AUTH_SERVER_VERSION, upInfo, + myConfig, pluginList.c_str()); +} + +void SrvAuthBlock::reset() +{ + pluginName = ""; + pluginList = ""; + dataForPlugin.clear(); + dataFromPlugin.clear(); + flComplete = false; + firstTime = true; + + authBlockWriter.reset(); + delete plugins; + plugins = NULL; +} diff --git a/src/utilities/gsec/gsec.cpp b/src/utilities/gsec/gsec.cpp index 37891aa5ed..5b35334f8e 100644 --- a/src/utilities/gsec/gsec.cpp +++ b/src/utilities/gsec/gsec.cpp @@ -248,14 +248,6 @@ int gsec(Firebird::UtilSvc* uSvc) uSvc->checkService(); - Auth::Get getPlugin(pseudoConfig); - manager = getPlugin.plugin(); - if (!manager) - { - GSEC_error_redirect((Firebird::Arg::Gds(isc_random) << "Missing management plugin").value(), GsecMsg15); - } - manager->addRef(); - fb_assert(user_data->trustedUser.entered() || user_data->authenticationBlock.hasData()); if (user_data->trustedUser.entered() || user_data->authenticationBlock.hasData()) { @@ -314,10 +306,25 @@ int gsec(Firebird::UtilSvc* uSvc) }; Firebird::LocalStatus st; - GsecInfo info(user_data->trustedUser.get(), user_data->role.get(), - user_data->trustedRole && !user_data->role.entered(), - network_protocol.c_str(), remote_address.c_str(), &user_data->authenticationBlock); - manager->start(&st, &info); + try + { + Auth::Get getPlugin(pseudoConfig); + manager = getPlugin.plugin(); + if (!manager) + { + GSEC_error_redirect((Firebird::Arg::Gds(isc_random) << "Missing management plugin").value(), GsecMsg15); + } + manager->addRef(); + + GsecInfo info(user_data->trustedUser.get(), user_data->role.get(), + user_data->trustedRole && !user_data->role.entered(), + network_protocol.c_str(), remote_address.c_str(), &user_data->authenticationBlock); + manager->start(&st, &info); + } + catch (const Firebird::Exception& ex) + { + ex.stuffException(&st); + } if (!st.isSuccess()) { @@ -481,7 +488,6 @@ int gsec(Firebird::UtilSvc* uSvc) user_data->database.setEntered(databaseNameEntered); user_data->role.set(sqlRoleName.c_str()); user_data->role.setEntered(sqlRoleName.hasData()); - user_data->role.setSpecified(user_data->role.entered()); if (ret == 0) { @@ -1380,8 +1386,16 @@ void GSEC_error_redirect(const ISC_STATUS* status_vector, USHORT errcode) * **************************************/ - GSEC_print_status(status_vector); - GSEC_error(errcode); + tsec* tdsec = tsec::getSpecific(); + if (!tdsec->utilSvc->isService()) + { + GSEC_print_status(status_vector); + GSEC_error(errcode); + } + else + { + GSEC_error(errcode, status_vector); + } } void GSEC_diag(USHORT errcode) @@ -1403,7 +1417,7 @@ void GSEC_diag(USHORT errcode) GSEC_print(errcode); } -void GSEC_error(USHORT errcode) +void GSEC_error(USHORT errcode, const ISC_STATUS* status_vector) { /************************************** * @@ -1419,6 +1433,10 @@ void GSEC_error(USHORT errcode) tsec* tdsec = tsec::getSpecific(); tdsec->utilSvc->setServiceStatus(GSEC_MSG_FAC, errcode, dummy); + if (status_vector) + { + tdsec->utilSvc->setServiceStatus(status_vector); + } tdsec->utilSvc->started(); GSEC_print(errcode); diff --git a/src/utilities/gsec/gsec_proto.h b/src/utilities/gsec/gsec_proto.h index 18a8fe404f..e8f6a49ce6 100644 --- a/src/utilities/gsec/gsec_proto.h +++ b/src/utilities/gsec/gsec_proto.h @@ -7,7 +7,7 @@ // Output reporting utilities void GSEC_print_status(const ISC_STATUS*); void GSEC_error_redirect(const ISC_STATUS*, USHORT); -void GSEC_error(USHORT); +void GSEC_error(USHORT code, const ISC_STATUS* status = NULL); void GSEC_exit(); void GSEC_print(USHORT, const char* str = NULL); void GSEC_message(USHORT, const char* str = NULL); diff --git a/src/utilities/gstat/dba.epp b/src/utilities/gstat/dba.epp index 71664ab16d..b42cb1c61b 100644 --- a/src/utilities/gstat/dba.epp +++ b/src/utilities/gstat/dba.epp @@ -51,7 +51,6 @@ #include "../yvalve/gds_proto.h" #include "../common/isc_f_proto.h" #include "../common/thd.h" -#include "../common/enc_proto.h" #include "../common/utils_proto.h" #include "../common/classes/ClumpletWriter.h" #include "../jrd/constants.h" diff --git a/src/yvalve/DistributedTransaction.cpp b/src/yvalve/DistributedTransaction.cpp index 97848fde33..b4753646c7 100644 --- a/src/yvalve/DistributedTransaction.cpp +++ b/src/yvalve/DistributedTransaction.cpp @@ -37,7 +37,6 @@ #include "../jrd/inf_pub.h" #include "../common/isc_proto.h" #include "../jrd/acl.h" -///#include "../common/classes/init.h" using namespace Firebird; using namespace Why; diff --git a/src/yvalve/PluginManager.cpp b/src/yvalve/PluginManager.cpp index 1d7ca768c2..ffcc12c5b5 100644 --- a/src/yvalve/PluginManager.cpp +++ b/src/yvalve/PluginManager.cpp @@ -904,15 +904,23 @@ void FB_CARG PluginManager::unregisterModule(IPluginModule* cleanup) fb_shutdown(5000, fb_shutrsn_exit_called); } -IPluginSet* FB_CARG PluginManager::getPlugins(unsigned int interfaceType, const char* namesList, +IPluginSet* FB_CARG PluginManager::getPlugins(IStatus* status, unsigned int interfaceType, const char* namesList, int desiredVersion, UpgradeInfo* ui, IFirebirdConf* firebirdConf) { - MutexLockGuard g(plugins->mutex); + try + { + MutexLockGuard g(plugins->mutex); - IPluginSet* rc = new PluginSet(interfaceType, namesList, desiredVersion, ui, firebirdConf); - rc->addRef(); - return rc; + IPluginSet* rc = new PluginSet(interfaceType, namesList, desiredVersion, ui, firebirdConf); + rc->addRef(); + return rc; + } + catch (const Exception& ex) + { + ex.stuffException(status); + return NULL; + } } diff --git a/src/yvalve/PluginManager.h b/src/yvalve/PluginManager.h index b17362301f..34ece7299a 100644 --- a/src/yvalve/PluginManager.h +++ b/src/yvalve/PluginManager.h @@ -42,11 +42,11 @@ class PluginManager : public AutoIface 0) || ISC_check_if_remote(svcName, false)) {