diff --git a/doc/README.services_extension b/doc/README.services_extension index 1fb5bb0bc8..8f7a931b6a 100644 --- a/doc/README.services_extension +++ b/doc/README.services_extension @@ -191,3 +191,35 @@ value from isc_service_query(). A sample of how services API should be used for remote backup and restore can be found in source code of fbsvcmgr. + + +5) Services API extension - using services with non-default security database. +(Alex Peshkov, peshkoff@mail.ru, 2013) + +If one wants to use services API to access database which is configured to use +non-default security database new SPB item isc_spb_expected_db should be used +when attaching to services manager. Value of this item is a database which is +expected to be accessed. + +Formally this does not raise backward incompatibility - as long as one does not +use new FB3 feature (multiple security databases) he has no problems with old +programs using services API. In a case when one has really big need to use old +program for a database with non-default configuration there is a workaround: +setting environment variable FB_EXPECTED_DB which will be added to SPB +automatically. + +Example. Imagine we have the following lines in databases.conf: +employee = $(dir_sampledb)/employee.fdb +{ + SecurityDatabase = employee +} +i.e. employee database is configured as security database for itself. + +To access it using fbsvcmgr one should use the following command line: +fbsvcmgr host:service_mgr user sysdba password xxx expected_db employee action_db_stats dbname employee sts_data_pages + +or in advance set FB_EXPECTED_DB: +export FB_EXPECTED_DB=employee +fbsvcmgr host:service_mgr user sysdba password xxx action_db_stats dbname employee sts_data_pages + +Certainly any other database action can be used here. diff --git a/lang_helpers/gds_codes.ftn b/lang_helpers/gds_codes.ftn index f5548420d1..1ea4e2b4d5 100644 --- a/lang_helpers/gds_codes.ftn +++ b/lang_helpers/gds_codes.ftn @@ -1530,6 +1530,28 @@ C -- PARAMETER (GDS__protect_ownership = 335545058) INTEGER*4 GDS__badvarnum PARAMETER (GDS__badvarnum = 335545059) + INTEGER*4 GDS__sec_context + PARAMETER (GDS__sec_context = 335545060) + INTEGER*4 GDS__multi_segment + PARAMETER (GDS__multi_segment = 335545061) + INTEGER*4 GDS__login_changed + PARAMETER (GDS__login_changed = 335545062) + INTEGER*4 GDS__auth_handshake_limit + PARAMETER (GDS__auth_handshake_limit = 335545063) + INTEGER*4 GDS__wirecrypt_incompatible + PARAMETER (GDS__wirecrypt_incompatible = 335545064) + INTEGER*4 GDS__miss_wirecrypt + PARAMETER (GDS__miss_wirecrypt = 335545065) + INTEGER*4 GDS__wirecrypt_key + PARAMETER (GDS__wirecrypt_key = 335545066) + INTEGER*4 GDS__wirecrypt_plugin + PARAMETER (GDS__wirecrypt_plugin = 335545067) + INTEGER*4 GDS__secdb_name + PARAMETER (GDS__secdb_name = 335545068) + INTEGER*4 GDS__auth_data + PARAMETER (GDS__auth_data = 335545069) + INTEGER*4 GDS__auth_datalength + PARAMETER (GDS__auth_datalength = 335545070) INTEGER*4 GDS__gfix_db_name PARAMETER (GDS__gfix_db_name = 335740929) INTEGER*4 GDS__gfix_invalid_sw diff --git a/lang_helpers/gds_codes.pas b/lang_helpers/gds_codes.pas index 664b9d2191..72fefffdd2 100644 --- a/lang_helpers/gds_codes.pas +++ b/lang_helpers/gds_codes.pas @@ -772,6 +772,17 @@ const gds_include_miss = 335545057; gds_protect_ownership = 335545058; gds_badvarnum = 335545059; + gds_sec_context = 335545060; + gds_multi_segment = 335545061; + gds_login_changed = 335545062; + gds_auth_handshake_limit = 335545063; + gds_wirecrypt_incompatible = 335545064; + gds_miss_wirecrypt = 335545065; + gds_wirecrypt_key = 335545066; + gds_wirecrypt_plugin = 335545067; + gds_secdb_name = 335545068; + gds_auth_data = 335545069; + gds_auth_datalength = 335545070; gds_gfix_db_name = 335740929; gds_gfix_invalid_sw = 335740930; gds_gfix_incmp_sw = 335740932; diff --git a/src/auth/SecureRemotePassword/client/SrpClient.cpp b/src/auth/SecureRemotePassword/client/SrpClient.cpp index 6eea93ef4d..a96edec3f1 100644 --- a/src/auth/SecureRemotePassword/client/SrpClient.cpp +++ b/src/auth/SecureRemotePassword/client/SrpClient.cpp @@ -79,17 +79,18 @@ int SrpClient::authenticate(IStatus* status, IClientBlock* cb) } HANDSHAKE_DEBUG(fprintf(stderr, "Cli: SRP phase2\n")); - unsigned int length; + unsigned length; const unsigned char* saltAndKey = cb->getData(&length); if (!saltAndKey || length == 0) { - (Arg::Gds(isc_random) << "Missing data from server").raise(); + Arg::Gds(isc_auth_data).raise(); } - if (length > (RemotePassword::SRP_SALT_SIZE + RemotePassword::SRP_KEY_SIZE + 2) * 2) + const unsigned expectedLength = + (RemotePassword::SRP_SALT_SIZE + RemotePassword::SRP_KEY_SIZE + 2) * 2; + if (length > expectedLength) { - string msg; - msg.printf("Wrong length (%d) of data from server", length); - (Arg::Gds(isc_random) << msg).raise(); + (Arg::Gds(isc_auth_datalength) << Arg::Num(length) << + Arg::Num(expectedLength) << "data").raise(); } string salt, key; @@ -97,9 +98,8 @@ int SrpClient::authenticate(IStatus* status, IClientBlock* cb) 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(); + (Arg::Gds(isc_auth_datalength) << Arg::Num(charSize) << + Arg::Num(RemotePassword::SRP_SALT_SIZE * 2) << "salt").raise(); } salt.assign(saltAndKey, charSize); dumpIt("Clnt: salt", salt); @@ -108,11 +108,10 @@ int SrpClient::authenticate(IStatus* status, IClientBlock* cb) charSize = *saltAndKey++; charSize += ((unsigned) *saltAndKey++) << 8; - if (charSize + 2 != length) + if (charSize != length - 2) { - string msg; - msg.printf("Wrong length (%d) of key from server", charSize); - (Arg::Gds(isc_random) << msg).raise(); + (Arg::Gds(isc_auth_datalength) << Arg::Num(charSize) << + Arg::Num(length - 2) << "key").raise(); } key.assign(saltAndKey, charSize); dumpIt("Clnt: key(srvPub)", key); diff --git a/src/auth/SecureRemotePassword/manage/SrpManagement.cpp b/src/auth/SecureRemotePassword/manage/SrpManagement.cpp index 5554b8de5f..701cb42199 100644 --- a/src/auth/SecureRemotePassword/manage/SrpManagement.cpp +++ b/src/auth/SecureRemotePassword/manage/SrpManagement.cpp @@ -183,7 +183,7 @@ public: if (!(secDbName && secDbName[0])) { - (Firebird::Arg::Gds(isc_random) << "Error getting security database name").raise(); + Firebird::Arg::Gds(isc_secdb_name).raise(); } Firebird::ClumpletWriter dpb(Firebird::ClumpletReader::dpbList, MAX_DPB_SIZE); diff --git a/src/auth/SecureRemotePassword/server/SrpServer.cpp b/src/auth/SecureRemotePassword/server/SrpServer.cpp index 0c076152c6..69e2455b4c 100644 --- a/src/auth/SecureRemotePassword/server/SrpServer.cpp +++ b/src/auth/SecureRemotePassword/server/SrpServer.cpp @@ -99,6 +99,7 @@ int SrpServer::authenticate(IStatus* status, IServerBlock* sb, IWriter* writerIn unsigned int length; const unsigned char* val = sb->getData(&length); clientPubKey.assign(val, length); + dumpBin("Srv: clientPubKey", clientPubKey); if (!clientPubKey.hasData()) { @@ -116,7 +117,7 @@ int SrpServer::authenticate(IStatus* status, IServerBlock* sb, IWriter* writerIn secDbName = config->asString(secDbKey); if (!(secDbName && secDbName[0])) { - (Arg::Gds(isc_random) << "Error getting security database name").raise(); + Arg::Gds(isc_secdb_name).raise(); } ClumpletWriter dpb(ClumpletReader::dpbList, MAX_DPB_SIZE); @@ -221,14 +222,13 @@ int SrpServer::authenticate(IStatus* status, IServerBlock* sb, IWriter* writerIn data += char(serverPubKey.length() >> 8); data.append(serverPubKey); dumpIt("Srv: serverPubKey", serverPubKey); - dumpIt("Srv: data", data); + dumpBin("Srv: data", data); sb->putData(status, data.length(), data.c_str()); if (!status->isSuccess()) { return AUTH_FAILED; } - dumpIt("Srv: clientPubKey", clientPubKey); server->serverSessionKey(sessionKey, clientPubKey.c_str(), verifier); dumpIt("Srv: sessionKey", sessionKey); diff --git a/src/auth/SecureRemotePassword/srp.cpp b/src/auth/SecureRemotePassword/srp.cpp index 36001e72c0..5942033b5b 100644 --- a/src/auth/SecureRemotePassword/srp.cpp +++ b/src/auth/SecureRemotePassword/srp.cpp @@ -213,6 +213,14 @@ void dumpIt(const char* name, const Firebird::string& str) fprintf(stderr, "%s: '%s'\n", name, str.c_str()); } +void dumpBin(const char* name, const Firebird::string& str) +{ + fprintf(stderr, "%s (%ld)\n", name, str.length()); + for (size_t x=0; x(dpb.getBuffer())); @@ -307,7 +311,9 @@ int SecurityDatabase::verify(IWriter* authBlock, IServerBlock* sBlock) return AUTH_CONTINUE; } - string login(sBlock->getLogin()); + const char* user = sBlock->getLogin(); + string login(user ? user : ""); + unsigned length; const unsigned char* data = sBlock->getData(&length); string passwordEnc; @@ -468,7 +474,7 @@ int SecurityDatabaseServer::authenticate(Firebird::IStatus* status, IServerBlock const char* tmp = config->asString(secDbKey); if (!tmp) { - (Arg::Gds(isc_random) << "Error getting security database name").raise(); + Arg::Gds(isc_secdb_name).raise(); } secDbName = tmp; diff --git a/src/burp/burp.cpp b/src/burp/burp.cpp index 6423cc60b4..66f992fa0b 100644 --- a/src/burp/burp.cpp +++ b/src/burp/burp.cpp @@ -106,7 +106,7 @@ static SLONG get_number(const SCHAR*); static ULONG get_size(const SCHAR*, burp_fil*); static gbak_action open_files(const TEXT *, const TEXT**, bool, USHORT, const Firebird::ClumpletWriter&); -static int api_gbak(Firebird::UtilSvc*, const Switches& switches); +static int svc_api_gbak(Firebird::UtilSvc*, const Switches& switches); static void burp_output(bool err, const SCHAR*, ...) ATTRIBUTE_FORMAT(2,3); static void burp_usage(const Switches& switches); static Switches::in_sw_tab_t* findSwitchOrThrow(Switches& switches, Firebird::string& sw); @@ -149,11 +149,11 @@ THREAD_ENTRY_DECLARE BURP_main(THREAD_ENTRY_PARAM arg) } -static int api_gbak(Firebird::UtilSvc* uSvc, const Switches& switches) +static int svc_api_gbak(Firebird::UtilSvc* uSvc, const Switches& switches) { /********************************************** * - * a p i _ g b a k + * s v c _ a p i _ g b a k * ********************************************** * @@ -172,12 +172,18 @@ static int api_gbak(Firebird::UtilSvc* uSvc, const Switches& switches) Firebird::UtilSvc::ArgvType& argv = uSvc->argv; const int argc = uSvc->argv.getCount(); + Firebird::string files[2]; + unsigned fileIndex = 0; for (int itr = 1; itr < argc; ++itr) { const Switches::in_sw_tab_t* inSw = switches.findSwitch(argv[itr]); if (! inSw) { + if (argv[itr][0] && fileIndex < 2) + { + files[fileIndex++] = argv[itr]; + } continue; } @@ -249,6 +255,8 @@ static int api_gbak(Firebird::UtilSvc* uSvc, const Switches& switches) } } + Firebird::string* dbName = flag_restore ? &files[1] : &files[0]; + ISC_STATUS_ARRAY status; FB_API_HANDLE svc_handle = 0; @@ -269,6 +277,10 @@ static int api_gbak(Firebird::UtilSvc* uSvc, const Switches& switches) { spb.insertString(isc_spb_password, pswd); } + if (dbName->hasData()) + { + spb.insertString(isc_spb_expected_db, *dbName); + } #ifdef TRUSTED_AUTH if (flag_trusted) { @@ -450,7 +462,7 @@ int gbak(Firebird::UtilSvc* uSvc) // test for "-service" switch if (switches.exists(IN_SW_BURP_SE, argv.begin(), 1, argc)) - return api_gbak(uSvc, switches); + return svc_api_gbak(uSvc, switches); uSvc->started(); diff --git a/src/common/IntlParametersBlock.cpp b/src/common/IntlParametersBlock.cpp index 2f6615b835..517efd1806 100644 --- a/src/common/IntlParametersBlock.cpp +++ b/src/common/IntlParametersBlock.cpp @@ -186,6 +186,7 @@ IntlParametersBlock::TagType IntlSpb::checkTag(UCHAR tag) case isc_spb_trusted_auth: case isc_spb_trusted_role: case isc_spb_process_name: + case isc_spb_expected_db: return TAG_STRING; case isc_spb_command_line: diff --git a/src/common/classes/ClumpletReader.cpp b/src/common/classes/ClumpletReader.cpp index e85f44049c..0f02be7acd 100644 --- a/src/common/classes/ClumpletReader.cpp +++ b/src/common/classes/ClumpletReader.cpp @@ -877,4 +877,31 @@ bool AuthReader::getInfo(string* name, string* method, PathName* secureDb) return true; } +#ifdef AUTH_BLOCK_DEBUG +void dumpAuthBlock(const char* text, ClumpletReader* pb, unsigned char param) +{ + fprintf(stderr, "AuthBlock in %s:", text); + if (pb->find(param)) + { + Firebird::AuthReader::AuthBlock tmp; + tmp.assign(pb->getBytes(), pb->getClumpLength()); + Firebird::AuthReader rdr(tmp); + string name, method; + PathName secureDb; + bool x = false; + while (rdr.getInfo(&name, &method, &secureDb)) + { + fprintf(stderr, " %s::%s::%s", name.c_str(), method.c_str(), secureDb.c_str()); + x = true; + rdr.moveNext(); + } + fprintf(stderr, "%s\n", x ? "" : " "); + } + else + { + fprintf(stderr, " \n"); + } +} +#endif + } // namespace diff --git a/src/common/classes/ClumpletReader.h b/src/common/classes/ClumpletReader.h index f6546eb54e..8075eb02b8 100644 --- a/src/common/classes/ClumpletReader.h +++ b/src/common/classes/ClumpletReader.h @@ -201,6 +201,13 @@ public: bool getInfo(string* name, string* method, PathName* secDb); }; +//#define AUTH_BLOCK_DEBUG +#ifdef AUTH_BLOCK_DEBUG +void dumpAuthBlock(const char* text, ClumpletReader* pb, unsigned char param); +#else +static inline void dumpAuthBlock(const char*, ClumpletReader*, unsigned char) { } +#endif + } // namespace Firebird #endif // CLUMPLETREADER_H diff --git a/src/common/classes/array.h b/src/common/classes/array.h index fa8c173986..d026bbdb4e 100644 --- a/src/common/classes/array.h +++ b/src/common/classes/array.h @@ -140,8 +140,15 @@ protected: } public: - typedef T* iterator; - typedef const T* const_iterator; + typedef size_t size_type; + typedef ptrdiff_t difference_type; + typedef T* pointer; + typedef const T* const_pointer; + typedef T& reference; + typedef const T& const_reference; + typedef T value_type; + typedef pointer iterator; + typedef const_pointer const_iterator; Array& operator =(const Array& source) { diff --git a/src/common/classes/fb_string.h b/src/common/classes/fb_string.h index 9bccb9bf98..35e7962df7 100644 --- a/src/common/classes/fb_string.h +++ b/src/common/classes/fb_string.h @@ -228,6 +228,10 @@ namespace Firebird { return stringLength; } + size_type getCount() const + { + return stringLength; + } // Almost same as c_str(), but return 0, not "", // when string has no data. Useful when interacting // with old code, which does check for NULL. @@ -245,6 +249,10 @@ namespace Firebird void reserve(size_type n = 0); void resize(const size_type n, char_type c = ' '); + void grow(const size_type n) + { + resize(n); + } pointer getBuffer(size_t l) { diff --git a/src/common/config/config.cpp b/src/common/config/config.cpp index 7d3d653a18..6f754664cf 100644 --- a/src/common/config/config.cpp +++ b/src/common/config/config.cpp @@ -121,9 +121,6 @@ const char* AmNative = "native"; const char* AmTrusted = "trusted"; const char* AmMixed = "mixed"; -const char* WIRE_CRYPT_DISABLED = "DISABLED"; -const char* WIRE_CRYPT_ENABLED = "ENABLED"; -const char* WIRE_CRYPT_REQUIRED = "REQUIRED"; const Config::ConfigEntry Config::entries[MAX_CONFIG_KEY] = { @@ -677,7 +674,7 @@ const char* Config::getPlugins(unsigned int type) const return (const char*) values[KEY_PLUG_KEY_HOLDER]; } - (Firebird::Arg::Gds(isc_random) << "Internal error in Config::getPlugins()").raise(); + (Firebird::Arg::Gds(isc_random) << "Internal error in Config::getPlugins(): unknown plugin type requested").raise(); return NULL; // compiler warning silencer } @@ -717,8 +714,19 @@ const char* Config::getSecurityDatabase() const return get(KEY_SECURITY_DATABASE); } -const char* Config::getWireCrypt(WireCryptMode wcMode) const +int Config::getWireCrypt(WireCryptMode wcMode) const { - const char* rc = get(KEY_WIRE_CRYPT); - return rc ? rc : wcMode == WC_CLIENT ? WIRE_CRYPT_ENABLED : WIRE_CRYPT_REQUIRED; + const char* wc = get(KEY_WIRE_CRYPT); + if (!wc) + { + return wcMode == WC_CLIENT ? WIRE_CRYPT_ENABLED : WIRE_CRYPT_REQUIRED; + } + + Firebird::NoCaseString wireCrypt(wc); + if (wireCrypt == "DISABLED") + return WIRE_CRYPT_DISABLED; + else if (wireCrypt == "ENABLED") + return WIRE_CRYPT_ENABLED; + else // the safest choice + return WIRE_CRYPT_REQUIRED; } diff --git a/src/common/config/config.h b/src/common/config/config.h index f261d4e114..8a627a75d0 100644 --- a/src/common/config/config.h +++ b/src/common/config/config.h @@ -75,9 +75,9 @@ extern const char* AmMixed; enum AmCache {AM_UNKNOWN, AM_DISABLED, AM_ENABLED}; -extern const char* WIRE_CRYPT_DISABLED; -extern const char* WIRE_CRYPT_ENABLED; -extern const char* WIRE_CRYPT_REQUIRED; +const int WIRE_CRYPT_DISABLED = 0; +const int WIRE_CRYPT_ENABLED = 1; +const int WIRE_CRYPT_REQUIRED = 2; enum WireCryptMode {WC_CLIENT, WC_SERVER}; // Have different defaults @@ -335,7 +335,7 @@ public: const char* getSecurityDatabase() const; - const char* getWireCrypt(WireCryptMode wcMode) const; + int getWireCrypt(WireCryptMode wcMode) const; }; // Implementation of interface to access master configuration file diff --git a/src/include/consts_pub.h b/src/include/consts_pub.h index b233b42832..cb4db2d1a7 100644 --- a/src/include/consts_pub.h +++ b/src/include/consts_pub.h @@ -282,6 +282,7 @@ #define isc_spb_host_name 121 #define isc_spb_os_user 122 #define isc_spb_config 123 +#define isc_spb_expected_db 124 #define isc_spb_connect_timeout isc_dpb_connect_timeout #define isc_spb_dummy_packet_interval isc_dpb_dummy_packet_interval diff --git a/src/include/gen/codetext.h b/src/include/gen/codetext.h index 0cccce5ae2..15a1349dbd 100644 --- a/src/include/gen/codetext.h +++ b/src/include/gen/codetext.h @@ -761,6 +761,17 @@ static const struct { {"include_miss", 335545057}, {"protect_ownership", 335545058}, {"badvarnum", 335545059}, + {"sec_context", 335545060}, + {"multi_segment", 335545061}, + {"login_changed", 335545062}, + {"auth_handshake_limit", 335545063}, + {"wirecrypt_incompatible", 335545064}, + {"miss_wirecrypt", 335545065}, + {"wirecrypt_key", 335545066}, + {"wirecrypt_plugin", 335545067}, + {"secdb_name", 335545068}, + {"auth_data", 335545069}, + {"auth_datalength", 335545070}, {"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 e7067faf03..b82ffb482b 100644 --- a/src/include/gen/iberror.h +++ b/src/include/gen/iberror.h @@ -795,6 +795,17 @@ const ISC_STATUS isc_include_depth = 335545056L; const ISC_STATUS isc_include_miss = 335545057L; const ISC_STATUS isc_protect_ownership = 335545058L; const ISC_STATUS isc_badvarnum = 335545059L; +const ISC_STATUS isc_sec_context = 335545060L; +const ISC_STATUS isc_multi_segment = 335545061L; +const ISC_STATUS isc_login_changed = 335545062L; +const ISC_STATUS isc_auth_handshake_limit = 335545063L; +const ISC_STATUS isc_wirecrypt_incompatible = 335545064L; +const ISC_STATUS isc_miss_wirecrypt = 335545065L; +const ISC_STATUS isc_wirecrypt_key = 335545066L; +const ISC_STATUS isc_wirecrypt_plugin = 335545067L; +const ISC_STATUS isc_secdb_name = 335545068L; +const ISC_STATUS isc_auth_data = 335545069L; +const ISC_STATUS isc_auth_datalength = 335545070L; const ISC_STATUS isc_gfix_db_name = 335740929L; const ISC_STATUS isc_gfix_invalid_sw = 335740930L; const ISC_STATUS isc_gfix_incmp_sw = 335740932L; @@ -1239,7 +1250,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 = 1183; +const ISC_STATUS isc_err_max = 1194; #else /* c definitions */ @@ -2004,6 +2015,17 @@ const ISC_STATUS isc_err_max = 1183; #define isc_include_miss 335545057L #define isc_protect_ownership 335545058L #define isc_badvarnum 335545059L +#define isc_sec_context 335545060L +#define isc_multi_segment 335545061L +#define isc_login_changed 335545062L +#define isc_auth_handshake_limit 335545063L +#define isc_wirecrypt_incompatible 335545064L +#define isc_miss_wirecrypt 335545065L +#define isc_wirecrypt_key 335545066L +#define isc_wirecrypt_plugin 335545067L +#define isc_secdb_name 335545068L +#define isc_auth_data 335545069L +#define isc_auth_datalength 335545070L #define isc_gfix_db_name 335740929L #define isc_gfix_invalid_sw 335740930L #define isc_gfix_incmp_sw 335740932L @@ -2448,7 +2470,7 @@ const ISC_STATUS isc_err_max = 1183; #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 1183 +#define isc_err_max 1194 #endif diff --git a/src/include/gen/msgs.h b/src/include/gen/msgs.h index 61106fa9be..24caf9f52d 100644 --- a/src/include/gen/msgs.h +++ b/src/include/gen/msgs.h @@ -764,6 +764,17 @@ Data source : @4"}, /* eds_statement */ {335545057, "File to include not found"}, /* include_miss */ {335545058, "Only the owner can change the ownership"}, /* protect_ownership */ {335545059, "undefined variable number"}, /* badvarnum */ + {335545060, "Missing security context for database @1"}, /* sec_context */ + {335545061, "Missing segment @1 in multisegment connect block parameter"}, /* multi_segment */ + {335545062, "Different logins in connect and attach packets - client library error"}, /* login_changed */ + {335545063, "Exceeded exchange limit during authentication handshake"}, /* auth_handshake_limit */ + {335545064, "Incompatible wire encryption levels requested on client and server"}, /* wirecrypt_incompatible */ + {335545065, "Client attempted to attach unencrypted but wire encryption is required"}, /* miss_wirecrypt */ + {335545066, "Client attempted to start wire encryption using unknown key @1"}, /* wirecrypt_key */ + {335545067, "Client attempted to start wire encryption using unsupported plugin @1"}, /* wirecrypt_plugin */ + {335545068, "Error getting security database name from configuration file"}, /* secdb_name */ + {335545069, "Client authentication plugin is missing required data from server"}, /* auth_data */ + {335545070, "Client authentication plugin expected @2 bytes of @3 from server, got @1"}, /* auth_datalength */ {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 d7d585b7d6..39bbbec49a 100644 --- a/src/include/gen/sql_code.h +++ b/src/include/gen/sql_code.h @@ -760,6 +760,17 @@ static const struct { {335545057, -902}, /* 737 include_miss */ {335545058, -552}, /* 738 protect_ownership */ {335545059, -901}, /* 739 badvarnum */ + {335545060, -902}, /* 740 sec_context */ + {335545061, -902}, /* 741 multi_segment */ + {335545062, -902}, /* 742 login_changed */ + {335545063, -902}, /* 743 auth_handshake_limit */ + {335545064, -902}, /* 744 wirecrypt_incompatible */ + {335545065, -902}, /* 745 miss_wirecrypt */ + {335545066, -902}, /* 746 wirecrypt_key */ + {335545067, -902}, /* 747 wirecrypt_plugin */ + {335545068, -902}, /* 748 secdb_name */ + {335545069, -902}, /* 749 auth_data */ + {335545070, -902}, /* 750 auth_datalength */ {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 143b8ea537..6e3e270e93 100644 --- a/src/include/gen/sql_state.h +++ b/src/include/gen/sql_state.h @@ -760,6 +760,17 @@ static const struct { {335545057, "XX000"}, // 737 include_miss {335545058, "28000"}, // 738 protect_ownership {335545059, "HY000"}, // 739 badvarnum + {335545060, "28000"}, // 740 sec_context + {335545061, "28000"}, // 741 multi_segment + {335545062, "28000"}, // 742 login_changed + {335545063, "28000"}, // 743 auth_handshake_limit + {335545064, "28000"}, // 744 wirecrypt_incompatible + {335545065, "28000"}, // 745 miss_wirecrypt + {335545066, "28000"}, // 746 wirecrypt_key + {335545067, "28000"}, // 747 wirecrypt_plugin + {335545068, "28000"}, // 748 secdb_name + {335545069, "28000"}, // 749 auth_data + {335545070, "28000"}, // 750 auth_datalength {335740929, "00000"}, // 1 gfix_db_name {335740930, "00000"}, // 2 gfix_invalid_sw {335740932, "00000"}, // 4 gfix_incmp_sw diff --git a/src/jrd/jrd.cpp b/src/jrd/jrd.cpp index d48a473741..3fa6aee5d6 100644 --- a/src/jrd/jrd.cpp +++ b/src/jrd/jrd.cpp @@ -946,7 +946,7 @@ static void rollback(thread_db*, jrd_tra*, const bool); static bool shutdown_database(Database*, const bool); static void strip_quotes(string&); static void purge_attachment(thread_db*, JAttachment*, const bool); -static void getUserInfo(UserId&, const DatabaseOptions&, const RefPtr*); +static void getUserInfo(UserId&, const DatabaseOptions&, const char*, const RefPtr*); static THREAD_ENTRY_DECLARE shutdown_thread(THREAD_ENTRY_PARAM); @@ -955,7 +955,7 @@ TraceFailedConnection::TraceFailedConnection(const char* filename, const Databas m_filename(filename), m_options(options) { - getUserInfo(m_id, *m_options, NULL); + getUserInfo(m_id, *m_options, m_filename, NULL); } @@ -1276,7 +1276,7 @@ JAttachment* FB_CARG JProvider::attachDatabase(IStatus* user_status, const char* } // Check for correct credentials supplied - getUserInfo(userId, options, &config); + getUserInfo(userId, options, org_filename.c_str(), &config); } catch (const Exception& ex) { @@ -2363,7 +2363,7 @@ JAttachment* FB_CARG JProvider::createDatabase(IStatus* user_status, const char* } // Check for correct credentials supplied - getUserInfo(userId, options, &config); + getUserInfo(userId, options, org_filename.c_str(), &config); } catch (const Exception& ex) { @@ -5462,6 +5462,7 @@ void DatabaseOptions::get(const UCHAR* dpb, USHORT dpb_length, bool& invalid_cli } ClumpletReader rdr(ClumpletReader::dpbList, dpb, dpb_length, dpbErrorRaise); + dumpAuthBlock("DatabaseOptions::get()", &rdr, isc_dpb_auth_block); dpb_utf8_filename = rdr.find(isc_dpb_utf8_filename); @@ -6941,7 +6942,8 @@ static VdnResult verifyDatabaseName(const PathName& name, ISC_STATUS* status, bo @param **/ -static void getUserInfo(UserId& user, const DatabaseOptions& options, const RefPtr* config) +static void getUserInfo(UserId& user, const DatabaseOptions& options, + const char* dbName, const RefPtr* config) { bool wheel = false; int id = -1, group = -1; // CVC: This var contained trash @@ -6974,7 +6976,7 @@ static void getUserInfo(UserId& user, const DatabaseOptions& options, const RefP { if (config && (secureDb != (*config)->getSecurityDatabase())) { - (Arg::Gds(isc_login) << Arg::Gds(isc_random) << "No SecDb match").raise(); + (Arg::Gds(isc_sec_context) << dbName).raise(); } } else diff --git a/src/jrd/svc.cpp b/src/jrd/svc.cpp index 5c7e41bc97..accb4d52a4 100644 --- a/src/jrd/svc.cpp +++ b/src/jrd/svc.cpp @@ -756,6 +756,7 @@ Service::Service(const TEXT* service_name, USHORT spb_length, const UCHAR* spb_d // Process the service parameter block. ClumpletReader spb(ClumpletReader::spbList, spb_data, spb_length, spbVersionError); + dumpAuthBlock("Jrd::Service() ctor", &spb, isc_spb_auth_block); getOptions(spb); // Perhaps checkout the user in the security database. diff --git a/src/msgs/facilities2.sql b/src/msgs/facilities2.sql index 2e393abc21..38712df3f9 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 (?, ?, ?, ?); -- -('2013-09-04 11:02:00', 'JRD', 0, 740) +('2013-10-15 17:48:59', 'JRD', 0, 751) ('2012-01-23 20:10:30', 'QLI', 1, 532) ('2009-07-16 05:26:11', 'GFIX', 3, 121) ('1996-11-07 13:39:40', 'GPRE', 4, 1) diff --git a/src/msgs/messages2.sql b/src/msgs/messages2.sql index 9eb93f8c4e..d43057f1f0 100644 --- a/src/msgs/messages2.sql +++ b/src/msgs/messages2.sql @@ -847,6 +847,17 @@ Data source : @4', NULL, NULL) ('include_miss', NULL, 'config_file.cpp', NULL, 0, 737, NULL, 'File to include not found', NULL, NULL); ('protect_ownership', 'check_owner', 'vio.cpp', NULL, 0, 738, NULL, 'Only the owner can change the ownership', NULL, NULL); ('badvarnum', NULL, NULL, NULL, 0, 739, NULL, 'undefined variable number', NULL, NULL); +('sec_context', 'getUserInfo', 'jrd.cpp', NULL, 0, 740, NULL, 'Missing security context for database @1', NULL, NULL); +('multi_segment', 'getMultiPartConnectParameter', 'server.cpp', NULL, 0, 741, NULL, 'Missing segment @1 in multisegment connect block parameter', NULL, NULL); +('login_changed', 'ServerAuth::ServerAuth', 'server.cpp', NULL, 0, 742, NULL, 'Different logins in connect and attach packets - client library error', NULL, NULL); +('auth_handshake_limit', 'ServerAuth::authenticate', 'server.cpp', NULL, 0, 743, NULL, 'Exceeded exchange limit during authentication handshake', NULL, NULL); +('wirecrypt_incompatible', 'requiredEncryption', 'server.cpp', NULL, 0, 744, NULL, 'Incompatible wire encryption levels requested on client and server', NULL, NULL); +('miss_wirecrypt', NULL, 'server.cpp', NULL, 0, 745, NULL, 'Client attempted to attach unencrypted but wire encryption is required', NULL, NULL); +('wirecrypt_key', 'start_crypt', 'server.cpp', NULL, 0, 746, NULL, 'Client attempted to start wire encryption using unknown key @1', NULL, NULL); +('wirecrypt_plugin', 'start_crypt', 'server.cpp', NULL, 0, 747, NULL, 'Client attempted to start wire encryption using unsupported plugin @1', NULL, NULL); +('secdb_name', NULL, NULL, NULL, 0, 748, NULL, 'Error getting security database name from configuration file', NULL, NULL); +('auth_data', NULL, NULL, NULL, 0, 749, NULL, 'Client authentication plugin is missing required data from server', NULL, NULL); +('auth_datalength', NULL, NULL, NULL, 0, 750, NULL, 'Client authentication plugin expected @2 bytes of @3 from server, got @1', 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 90c3d48ec4..fd7fa8ea83 100644 --- a/src/msgs/system_errors2.sql +++ b/src/msgs/system_errors2.sql @@ -746,6 +746,17 @@ set bulk_insert INSERT INTO SYSTEM_ERRORS (SQL_CODE, SQL_CLASS, SQL_SUBCLASS, FA (-902, 'XX', '000', 0, 737, 'include_miss', NULL, NULL) (-552, '28', '000', 0, 738, 'protect_ownership', NULL, NULL) (-901, 'HY', '000', 0, 739, 'badvarnum', NULL, NULL) +(-902, '28', '000', 0, 740, 'sec_context', NULL, NULL); +(-902, '28', '000', 0, 741, 'multi_segment', NULL, NULL); +(-902, '28', '000', 0, 742, 'login_changed', NULL, NULL); +(-902, '28', '000', 0, 743, 'auth_handshake_limit', NULL, NULL); +(-902, '28', '000', 0, 744, 'wirecrypt_incompatible', NULL, NULL); +(-902, '28', '000', 0, 745, 'miss_wirecrypt', NULL, NULL); +(-902, '28', '000', 0, 746, 'wirecrypt_key', NULL, NULL); +(-902, '28', '000', 0, 747, 'wirecrypt_plugin', NULL, NULL); +(-902, '28', '000', 0, 748, 'secdb_name', NULL, NULL); +(-902, '28', '000', 0, 749, 'auth_data', NULL, NULL); +(-902, '28', '000', 0, 750, 'auth_datalength', 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 b98d727d91..d7e2e5ede9 100644 --- a/src/remote/client/interface.cpp +++ b/src/remote/client/interface.cpp @@ -661,8 +661,8 @@ namespace Remote { 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(ClntAuthBlock&, PathName&, bool, ClumpletWriter&, PathName&, bool); -static rem_port* analyze_service(ClntAuthBlock&, PathName&, bool, ClumpletReader&, bool); +static rem_port* analyze(ClntAuthBlock& cBlock, PathName& attach_name, unsigned flags, + ClumpletWriter& pb, const ParametersSet& parSet, PathName& node_name, PathName* ref_db_name); static void batch_gds_receive(rem_port*, struct rmtque *, USHORT); static void batch_dsql_fetch(rem_port*, struct rmtque *, USHORT); static void clear_queue(rem_port*); @@ -674,7 +674,7 @@ static THREAD_ENTRY_DECLARE event_thread(THREAD_ENTRY_PARAM); static Rvnt* find_event(rem_port*, SLONG); static bool get_new_dpb(ClumpletWriter&, const ParametersSet&); static void info(IStatus*, Rdb*, P_OP, USHORT, USHORT, USHORT, - const UCHAR*, USHORT, const UCHAR*, ULONG, UCHAR*, ClntAuthBlock* cBlock = NULL); + const UCHAR*, USHORT, const UCHAR*, ULONG, UCHAR*); static void init(IStatus*, ClntAuthBlock&, rem_port*, P_OP, PathName&, ClumpletWriter&, IntlParametersBlock&, ICryptKeyCallback* cryptCallback); static Rtr* make_transaction(Rdb*, USHORT); @@ -702,14 +702,17 @@ 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(ClntAuthBlock& authItr, ClumpletWriter& dpb, const ParametersSet* tags, rem_port* port); -static void authReceiveResponse(ClntAuthBlock& authItr, rem_port* port, Rdb* rdb, - IStatus* status, PACKET* packet, bool checkKeys); +static void authReceiveResponse(bool havePacket, ClntAuthBlock& authItr, rem_port* port, + Rdb* rdb, IStatus* status, PACKET* packet, bool checkKeys); static AtomicCounter remote_event_id; +static const unsigned ANALYZE_UV = 0x01; +static const unsigned ANALYZE_LOOPBACK = 0x02; +static const unsigned ANALYZE_MOUNTS = 0x03; + inline static void reset(IStatus* status) throw() { status->init(); @@ -753,13 +756,17 @@ IAttachment* Provider::attach(IStatus* status, const char* filename, unsigned in reset(status); ClumpletWriter newDpb(ClumpletReader::dpbList, MAX_DPB_SIZE, dpb, dpb_length); - const bool user_verification = get_new_dpb(newDpb, dpbParam); + unsigned flags = ANALYZE_MOUNTS; + if (get_new_dpb(newDpb, dpbParam)) + flags |= ANALYZE_UV; + if (loopback) + flags |= ANALYZE_LOOPBACK; PathName expanded_name(filename); PathName node_name; ClntAuthBlock cBlock(&expanded_name, &newDpb, &dpbParam); - rem_port* port = analyze(cBlock, expanded_name, user_verification, newDpb, node_name, loopback); + rem_port* port = analyze(cBlock, expanded_name, flags, newDpb, dpbParam, node_name, NULL); if (!port) { @@ -770,13 +777,13 @@ IAttachment* Provider::attach(IStatus* status, const char* filename, unsigned in RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION); // The client may have set a parameter for dummy_packet_interval. Add that to the - // the DPB so the server can pay attention to it. Note: allocation code must - // ensure sufficient space has been added. + // the DPB so the server can pay attention to it. add_other_params(port, newDpb, dpbParam); add_working_directory(newDpb, node_name); IntlDpb intl; + HANDSHAKE_DEBUG(fprintf(stderr, "Cli: call init for DB='%s'\n", expanded_name.c_str())); init(status, cBlock, port, op_attach, expanded_name, newDpb, intl, cryptCallback); Attachment* a = new Attachment(port->port_context, filename); @@ -1336,13 +1343,17 @@ Firebird::IAttachment* Provider::create(IStatus* status, const char* filename, ClumpletWriter newDpb(ClumpletReader::dpbList, MAX_DPB_SIZE, reinterpret_cast(dpb), dpb_length); - const bool user_verification = get_new_dpb(newDpb, dpbParam); + unsigned flags = ANALYZE_MOUNTS; + if (get_new_dpb(newDpb, dpbParam)) + flags |= ANALYZE_UV; + if (loopback) + flags |= ANALYZE_LOOPBACK; PathName expanded_name(filename); PathName node_name; ClntAuthBlock cBlock(&expanded_name, &newDpb, &dpbParam); - rem_port* port = analyze(cBlock, expanded_name, user_verification, newDpb, node_name, loopback); + rem_port* port = analyze(cBlock, expanded_name, flags, newDpb, dpbParam, node_name, NULL); if (!port) { @@ -4560,14 +4571,25 @@ Firebird::IService* Provider::attachSvc(IStatus* status, const char* service, { reset(status); - PathName expanded_name(service); + PathName node_name, expanded_name(service); ClumpletWriter newSpb(ClumpletReader::spbList, MAX_DPB_SIZE, spb, spbLength); const bool user_verification = get_new_dpb(newSpb, spbParam); ClntAuthBlock cBlock(NULL, &newSpb, &spbParam); - cBlock.loadClnt(newSpb, &spbParam); - rem_port* port = analyze_service(cBlock, expanded_name, user_verification, newSpb, loopback); + unsigned flags = 0; + if (get_new_dpb(newSpb, spbParam)) + flags |= ANALYZE_UV; + if (loopback) + flags |= ANALYZE_LOOPBACK; + + PathName refDbName; + if (newSpb.find(isc_spb_expected_db)) + { + newSpb.getPath(refDbName); + } + + rem_port* port = analyze(cBlock, expanded_name, flags, newSpb, spbParam, node_name, &refDbName); RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION); Rdb* rdb = port->port_context; @@ -4581,8 +4603,6 @@ Firebird::IService* Provider::attachSvc(IStatus* status, const char* service, IntlSpb intl; init(status, cBlock, port, op_service_attach, expanded_name, newSpb, intl, cryptCallback); - cBlock.saveServiceDataTo(port); - Firebird::IService* s = new Service(rdb); s->addRef(); return s; @@ -4718,12 +4738,9 @@ void Service::query(IStatus* status, rem_port* port = rdb->rdb_port; RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION); - ClntAuthBlock cBlock(NULL, NULL, NULL); - cBlock.loadServiceDataFrom(port); - info(status, rdb, op_service_info, rdb->rdb_id, 0, sendLength, sendItems, receiveLength, receiveItems, - bufferLength, buffer, &cBlock); + bufferLength, buffer); } catch (const Exception& ex) { @@ -5322,12 +5339,33 @@ static void authenticateStep0(ClntAuthBlock& cBlock) } -static rem_port* analyze(ClntAuthBlock& cBlock, - PathName& file_name, - bool uv_flag, - ClumpletWriter& dpb, - PathName& node_name, - bool loopback) +static void secureAuthentication(ClntAuthBlock& cBlock, rem_port* port) +{ + HANDSHAKE_DEBUG(fprintf(stderr, "Cli: secureAuthentication\n")); + + if (!port) + return; + + Rdb* rdb = port->port_context; + fb_assert(rdb); + PACKET* packet = &rdb->rdb_packet; + + HANDSHAKE_DEBUG(fprintf(stderr, "Cli: secureAuthentication: port OK, op=%d\n", packet->p_operation)); + + if (packet->p_operation == op_cond_accept) + { + LocalStatus st; + authReceiveResponse(true, cBlock, port, rdb, &st, packet, true); + if (!st.isSuccess()) + { + status_exception::raise(st.get()); + } + } +} + + +static rem_port* analyze(ClntAuthBlock& cBlock, PathName& attach_name, unsigned flags, + ClumpletWriter& pb, const ParametersSet& parSet, PathName& node_name, PathName* ref_db_name) { /************************************** * @@ -5336,9 +5374,9 @@ static rem_port* analyze(ClntAuthBlock& cBlock, ************************************** * * Functional description - * Analyze a file specification and determine whether + * Analyze an attach specification and determine whether * a remote server is required, and if so, what protocol - * to use. If the database can be accessed via the + * to use. If the target can be accessed via the * remote subsystem, return address of a port block * with which to communicate with the server. * Otherwise, return NULL. @@ -5347,196 +5385,140 @@ static rem_port* analyze(ClntAuthBlock& cBlock, * **************************************/ - // Analyze the file name to see if a remote connection is required. If not, - // quietly (sic) return. + rem_port* port = NULL; - cBlock.loadClnt(dpb, &dpbParam); + try + { + cBlock.loadClnt(pb, &parSet); authenticateStep0(cBlock); - rem_port* port = NULL; #ifdef WIN_NT - if (ISC_analyze_protocol(PROTOCOL_XNET, file_name, node_name)) + if (ISC_analyze_protocol(PROTOCOL_XNET, attach_name, node_name)) { - port = XNET_analyze(&cBlock, file_name, uv_flag, cBlock.getConfig()); + port = XNET_analyze(&cBlock, attach_name, flags & ANALYZE_UV, cBlock.getConfig(), ref_db_name); } - else if (ISC_analyze_protocol(PROTOCOL_WNET, file_name, node_name) || - ISC_analyze_pclan(file_name, node_name)) + else if (ISC_analyze_protocol(PROTOCOL_WNET, attach_name, node_name) || + ISC_analyze_pclan(attach_name, node_name)) { if (node_name.isEmpty()) + { node_name = WNET_LOCALHOST; + } else { ISC_unescape(node_name); ISC_utf8ToSystem(node_name); } - - port = WNET_analyze(&cBlock, file_name, node_name.c_str(), uv_flag, cBlock.getConfig()); + port = WNET_analyze(&cBlock, attach_name, node_name.c_str(), flags & ANALYZE_UV, + cBlock.getConfig(), ref_db_name); } else #endif - if (ISC_analyze_protocol(PROTOCOL_INET, file_name, node_name) || - ISC_analyze_tcp(file_name, node_name)) + if (ISC_analyze_protocol(PROTOCOL_INET, attach_name, node_name) || + ISC_analyze_tcp(attach_name, node_name)) { if (node_name.isEmpty()) + { node_name = INET_LOCALHOST; + } else { ISC_unescape(node_name); ISC_utf8ToSystem(node_name); } - - port = INET_analyze(&cBlock, file_name, node_name.c_str(), uv_flag, dpb, cBlock.getConfig()); + port = INET_analyze(&cBlock, attach_name, node_name.c_str(), flags & ANALYZE_UV, pb, + cBlock.getConfig(), ref_db_name); } // We have a local connection string. If it's a file on a network share, // try to connect to the corresponding host remotely. -#ifdef WIN_NT - PathName expanded_name = file_name; - ISC_expand_share(expanded_name); - - if (ISC_analyze_pclan(expanded_name, node_name)) + if (flags & ANALYZE_MOUNTS) { - ISC_unescape(node_name); - ISC_utf8ToSystem(node_name); +#ifdef WIN_NT + if (!port) + { + PathName expanded_name = attach_name; + ISC_expand_share(expanded_name); - port = WNET_analyze(&cBlock, expanded_name, node_name.c_str(), uv_flag, cBlock.getConfig()); - } + if (ISC_analyze_pclan(expanded_name, node_name)) + { + ISC_unescape(node_name); + ISC_utf8ToSystem(node_name); + + port = WNET_analyze(&cBlock, expanded_name, node_name.c_str(), flags & ANALYZE_UV, + cBlock.getConfig(), ref_db_name); + } + } #endif #ifndef NO_NFS - if (!port) - { - PathName expanded_name = file_name; - if (ISC_analyze_nfs(expanded_name, node_name)) + if (!port) { - ISC_unescape(node_name); - ISC_utf8ToSystem(node_name); + PathName expanded_name = attach_name; + if (ISC_analyze_nfs(expanded_name, node_name)) + { + ISC_unescape(node_name); + ISC_utf8ToSystem(node_name); - port = INET_analyze(&cBlock, expanded_name, node_name.c_str(), uv_flag, dpb, cBlock.getConfig()); + port = INET_analyze(&cBlock, expanded_name, node_name.c_str(), flags & ANALYZE_UV, pb, + cBlock.getConfig(), ref_db_name); + } } - } #endif + } - if (loopback) + if ((flags & ANALYZE_LOOPBACK) && !port) { - // We still have a local connection string but failed to connect so far. - // If we're a pure client, attempt connect to the localhost. + // We have a local connection string. + // If we are in loopback mode attempt connect to a localhost. if (node_name.isEmpty()) { #ifdef WIN_NT if (!port) { - port = XNET_analyze(&cBlock, file_name, uv_flag, cBlock.getConfig()); + port = XNET_analyze(&cBlock, attach_name, flags & ANALYZE_UV, cBlock.getConfig(), ref_db_name); } if (!port) { - port = WNET_analyze(&cBlock, file_name, WNET_LOCALHOST, uv_flag, cBlock.getConfig()); + port = WNET_analyze(&cBlock, attach_name, WNET_LOCALHOST, flags & ANALYZE_UV, + cBlock.getConfig(), ref_db_name); } #endif if (!port) { - port = INET_analyze(&cBlock, file_name, INET_LOCALHOST, uv_flag, dpb, cBlock.getConfig()); + port = INET_analyze(&cBlock, attach_name, INET_LOCALHOST, flags & ANALYZE_UV, pb, + cBlock.getConfig(), ref_db_name); } } } - - return port; -} - - -static rem_port* analyze_service(ClntAuthBlock& cBlock, - PathName& service_name, - bool uv_flag, - ClumpletReader& spb, - bool loopback) -{ -/************************************** - * - * a n a l y z e _ s e r v i c e - * - ************************************** - * - * Functional description - * Analyze a service specification and determine whether - * a remote server is required, and if so, what protocol - * to use. If the database can be accessed via the - * remote subsystem, return address of a port block - * with which to communicate with the server. - * Otherwise, return NULL. - * - **************************************/ - // 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(NULL, service_name, uv_flag, cBlock.getConfig()); - } - - if (ISC_analyze_protocol(PROTOCOL_WNET, service_name, node_name) || - ISC_analyze_pclan(service_name, node_name)) - { - if (node_name.isEmpty()) - { - node_name = WNET_LOCALHOST; - } - return WNET_analyze(NULL, service_name, node_name.c_str(), uv_flag, cBlock.getConfig()); - } -#endif - - if (ISC_analyze_protocol(PROTOCOL_INET, service_name, node_name) || - ISC_analyze_tcp(service_name, node_name)) - { - if (node_name.isEmpty()) - { - node_name = INET_LOCALHOST; - } - return INET_analyze(NULL, service_name, node_name.c_str(), uv_flag, spb, cBlock.getConfig()); - } - - rem_port* port = NULL; - - if (loopback) - { - - // We have a local connection string. If we're a pure client, - // attempt connect to a localhost. - - if (node_name.isEmpty()) - { -#if defined(WIN_NT) - if (!port) - { - port = XNET_analyze(NULL, service_name, uv_flag, cBlock.getConfig()); - } - - if (!port) - { - port = WNET_analyze(NULL, service_name, WNET_LOCALHOST, uv_flag, cBlock.getConfig()); - } -#endif - if (!port) - { - port = INET_analyze(NULL, service_name, INET_LOCALHOST, uv_flag, spb, cBlock.getConfig()); - } - } - } - if (!port) { Arg::Gds(isc_unavailable).raise(); } - return port; + secureAuthentication(cBlock, port); + } + catch (const status_exception& ex) + { + const ISC_STATUS* s = ex.value(); + if (s[1] != isc_unavailable) + { + (Arg::Gds(isc_unavailable) << Arg::StatusVector(s)).raise(); + } + else + { + throw; + } + } + + return port; } + static void clear_stmt_que(rem_port* port, Rsr* statement) { /************************************** @@ -6095,8 +6077,7 @@ static void info(IStatus* status, USHORT recv_item_length, const UCHAR* recv_items, ULONG buffer_length, - UCHAR* buffer, - ClntAuthBlock* cBlock) + UCHAR* buffer) { /************************************** * @@ -6136,18 +6117,7 @@ static void info(IStatus* status, try { - if (operation == op_service_info) - { - // Probably communicate with services auth - fb_assert(cBlock); - HANDSHAKE_DEBUG(fprintf(stderr, "Cli: info() calls authReceiveResponse\n")); - authReceiveResponse(*cBlock, rdb->rdb_port, rdb, status, packet, false); - } - else - { - // Just get response from server - receive_response(status, rdb, packet); - } + receive_response(status, rdb, packet); } catch (const Exception&) { @@ -6162,6 +6132,11 @@ static void info(IStatus* status, static void authFillParametersBlock(ClntAuthBlock& cBlock, ClumpletWriter& dpb, const ParametersSet* tags, rem_port* port) { + if (cBlock.authComplete) + { + return; // Already authenticated + } + LocalStatus s; cBlock.resetDataFromPlugin(); @@ -6231,15 +6206,23 @@ static void REMOTE_free_string(CSTRING* tmp) } } -static void authReceiveResponse(ClntAuthBlock& cBlock, rem_port* port, Rdb* rdb, - IStatus* status, PACKET* packet, bool checkKeys) +static void authReceiveResponse(bool havePacket, ClntAuthBlock& cBlock, rem_port* port, + Rdb* rdb, IStatus* status, PACKET* packet, bool checkKeys) { LocalStatus s; for (;;) { // Get response - receive_packet(port, packet); + if (!havePacket) + { + receive_packet(port, packet); + } + else + { + fb_assert(packet->p_operation == op_cond_accept); + } + havePacket = false; // havePacket means first packet is already received // Check response cstring* n = NULL; @@ -6256,7 +6239,16 @@ static void authReceiveResponse(ClntAuthBlock& cBlock, rem_port* port, Rdb* rdb, d = &packet->p_auth_cont.p_data; n = &packet->p_auth_cont.p_name; port->addServerKeys(&packet->p_auth_cont.p_keys); - HANDSHAKE_DEBUG(fprintf(stderr, "Cli: authReceiveResponse: ont_auth d=%d n=%d '%.*s' 0x%x\n", + HANDSHAKE_DEBUG(fprintf(stderr, "Cli: authReceiveResponse: cont_auth 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; + + case op_cond_accept: + d = &packet->p_acpd.p_acpt_data; + n = &packet->p_acpd.p_acpt_plugin; + port->addServerKeys(&packet->p_acpd.p_acpt_keys); + HANDSHAKE_DEBUG(fprintf(stderr, "Cli: authReceiveResponse: cond_accept 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; @@ -6285,6 +6277,7 @@ static void authReceiveResponse(ClntAuthBlock& cBlock, rem_port* port, Rdb* rdb, REMOTE_check_response(status, rdb, packet, checkKeys); // successfully attached HANDSHAKE_DEBUG(fprintf(stderr, "Cli: authReceiveResponse: OK!\n")); + cBlock.authComplete = true; rdb->rdb_id = packet->p_resp.p_resp_object; // try to start crypt @@ -6392,7 +6385,7 @@ static void init(IStatus* status, ClntAuthBlock& cBlock, rem_port* port, P_OP op send_packet(port, packet); - authReceiveResponse(cBlock, port, rdb, status, packet, true); + authReceiveResponse(false, cBlock, port, rdb, status, packet, true); } catch (const Exception&) { @@ -6681,67 +6674,71 @@ static void receive_packet_noqueue(rem_port* port, PACKET* packet) // Receive responses for all deferred packets that were already sent Rdb* rdb = port->port_context; - while (port->port_deferred_packets->getCount()) + + if (port->port_deferred_packets) { - rem_que_packet* const p = port->port_deferred_packets->begin(); - if (!p->sent) - break; - - OBJCT stmt_id = 0; - bool bCheckResponse = false, bFreeStmt = false; - - if (p->packet.p_operation == op_execute) + while (port->port_deferred_packets->getCount()) { - stmt_id = p->packet.p_sqldata.p_sqldata_statement; - bCheckResponse = true; - } - else if (p->packet.p_operation == op_free_statement) - { - stmt_id = p->packet.p_sqlfree.p_sqlfree_statement; - bFreeStmt = (p->packet.p_sqlfree.p_sqlfree_option == DSQL_drop); - } + rem_que_packet* const p = port->port_deferred_packets->begin(); + if (!p->sent) + break; - receive_packet_with_callback(port, &p->packet); + OBJCT stmt_id = 0; + bool bCheckResponse = false, bFreeStmt = false; - Rsr* statement = NULL; - if (bCheckResponse || bFreeStmt) - { - statement = port->port_objects[stmt_id]; - } - - if (bCheckResponse) - { - bool bAssign = true; - try + if (p->packet.p_operation == op_execute) { - LocalStatus status; - REMOTE_check_response(&status, rdb, &p->packet); - statement->saveException(status.get(), false); + stmt_id = p->packet.p_sqldata.p_sqldata_statement; + bCheckResponse = true; } - catch (const Exception& ex) + else if (p->packet.p_operation == op_free_statement) { - // save error within the corresponding statement - statement->saveException(ex, false); - bAssign = false; + stmt_id = p->packet.p_sqlfree.p_sqlfree_statement; + bFreeStmt = (p->packet.p_sqlfree.p_sqlfree_option == DSQL_drop); } - if (bAssign) + receive_packet_with_callback(port, &p->packet); + + Rsr* statement = NULL; + if (bCheckResponse || bFreeStmt) { - // assign statement to transaction - const OBJCT tran_id = p->packet.p_sqldata.p_sqldata_transaction; - Rtr* transaction = port->port_objects[tran_id]; - statement->rsr_rtr = transaction; + statement = port->port_objects[stmt_id]; } - } - if (bFreeStmt && p->packet.p_resp.p_resp_object == INVALID_OBJECT) - { - release_sql_request(statement); - } + if (bCheckResponse) + { + bool bAssign = true; + try + { + LocalStatus status; + REMOTE_check_response(&status, rdb, &p->packet); + statement->saveException(status.get(), false); + } + catch (const Exception& ex) + { + // save error within the corresponding statement + statement->saveException(ex, false); + bAssign = false; + } - // free only part of packet we worked with - REMOTE_free_packet(port, &p->packet, true); - port->port_deferred_packets->remove(p); + if (bAssign) + { + // assign statement to transaction + const OBJCT tran_id = p->packet.p_sqldata.p_sqldata_transaction; + Rtr* transaction = port->port_objects[tran_id]; + statement->rsr_rtr = transaction; + } + } + + if (bFreeStmt && p->packet.p_resp.p_resp_object == INVALID_OBJECT) + { + release_sql_request(statement); + } + + // free only part of packet we worked with + REMOTE_free_packet(port, &p->packet, true); + port->port_deferred_packets->remove(p); + } } receive_packet_with_callback(port, packet); @@ -7197,16 +7194,19 @@ static void send_packet(rem_port* port, PACKET* packet) // Send packets that were deferred - for (rem_que_packet* p = port->port_deferred_packets->begin(); - p < port->port_deferred_packets->end(); p++) + if (port->port_deferred_packets) { - if (!p->sent) + for (rem_que_packet* p = port->port_deferred_packets->begin(); + p < port->port_deferred_packets->end(); p++) { - if (!port->send_partial(&p->packet)) + if (!p->sent) { - Arg::Gds(isc_net_write_err).raise(); + if (!port->send_partial(&p->packet)) + { + Arg::Gds(isc_net_write_err).raise(); + } + p->sent = true; } - p->sent = true; } } @@ -7310,21 +7310,12 @@ static void svcstart(IStatus* status, * **************************************/ - // Get ready for multi-hop auth - ClntAuthBlock cBlock(NULL, NULL, NULL); - cBlock.loadServiceDataFrom(rdb->rdb_port); - ClumpletWriter send(ClumpletReader::SpbStart, MAX_DPB_SIZE, items, item_length); if (rdb->rdb_port->port_protocol < PROTOCOL_VERSION13) { // This is FB < 3.0. Lets convert the UTF8 strings to the OS codepage. IntlSpbStart().fromUtf8(send, 0); } - else - { - HANDSHAKE_DEBUG(fprintf(stderr, "Cli: svcstart calls authFillParametersBlock\n")); - authFillParametersBlock(cBlock, send, &spbStartParam, rdb->rdb_port); - } // Build the primary packet to get the operation started. PACKET* packet = &rdb->rdb_packet; @@ -7344,7 +7335,7 @@ static void svcstart(IStatus* status, try { - authReceiveResponse(cBlock, rdb->rdb_port, rdb, status, packet, true); + receive_response(status, rdb, packet); } catch (const Exception&) { @@ -7473,8 +7464,8 @@ ClntAuthBlock::ClntAuthBlock(const Firebird::PathName* fileName, Firebird::Clump : pluginList(getPool()), serverPluginList(getPool()), userName(getPool()), password(getPool()), dataForPlugin(getPool()), dataFromPlugin(getPool()), - cryptKeys(getPool()), dpbConfig(getPool()), hasCryptKey(false), - plugins(PluginType::AuthClient, FB_AUTH_CLIENT_VERSION, upInfo), + cryptKeys(getPool()), dpbConfig(getPool()), + hasCryptKey(false), plugins(PluginType::AuthClient, FB_AUTH_CLIENT_VERSION, upInfo), authComplete(false), firstTime(true) { if (dpb && tags && dpb->find(tags->config_text)) @@ -7664,18 +7655,6 @@ bool ClntAuthBlock::checkPluginName(Firebird::PathName& nameToCheck) return false; } -void ClntAuthBlock::saveServiceDataTo(rem_port* port) -{ - port->port_login = userName; - port->port_password = password; -} - -void ClntAuthBlock::loadServiceDataFrom(rem_port* port) -{ - userName = port->port_login; - password = port->port_password; -} - void ClntAuthBlock::putKey(IStatus* status, FbCryptKey* cryptKey) { status->init(); diff --git a/src/remote/inet.cpp b/src/remote/inet.cpp index f65c0027c0..c3b6803a44 100644 --- a/src/remote/inet.cpp +++ b/src/remote/inet.cpp @@ -448,7 +448,8 @@ static rem_port* inet_try_connect( PACKET*, const PathName&, const TEXT*, ClumpletReader&, - RefPtr*); + RefPtr*, + const PathName*); static bool_t inet_write(XDR*); //, int); #ifdef DEBUG @@ -523,7 +524,8 @@ rem_port* INET_analyze(ClntAuthBlock* cBlock, const TEXT* node_name, bool uv_flag, ClumpletReader &dpb, - RefPtr* config) + RefPtr* config, + const PathName* ref_db_name) { /************************************** * @@ -608,12 +610,13 @@ rem_port* INET_analyze(ClntAuthBlock* cBlock, // Try connection using first set of protocols - rem_port* port = inet_try_connect(packet, rdb, file_name, node_name, dpb, config); + rem_port* port = inet_try_connect(packet, rdb, file_name, node_name, dpb, config, ref_db_name); P_ACPT* accept = NULL; switch (packet->p_operation) { case op_accept_data: + case op_cond_accept: accept = &packet->p_acpd; if (cBlock) { @@ -2680,7 +2683,8 @@ static rem_port* inet_try_connect(PACKET* packet, const PathName& file_name, const TEXT* node_name, ClumpletReader& dpb, - RefPtr* config) + RefPtr* config, + const PathName* ref_db_name) { /************************************** * @@ -2701,8 +2705,10 @@ static rem_port* inet_try_connect(PACKET* packet, cnct->p_cnct_operation = op_attach; cnct->p_cnct_cversion = CONNECT_VERSION3; cnct->p_cnct_client = ARCHITECTURE; - cnct->p_cnct_file.cstr_length = (ULONG) file_name.length(); - cnct->p_cnct_file.cstr_address = reinterpret_cast(file_name.c_str()); + + const PathName& cnct_file(ref_db_name ? (*ref_db_name) : file_name); + cnct->p_cnct_file.cstr_length = (ULONG) cnct_file.length(); + cnct->p_cnct_file.cstr_address = reinterpret_cast(cnct_file.c_str()); // If we can't talk to a server, punt. Let somebody else generate // an error. status_vector will have the network error info. diff --git a/src/remote/inet_proto.h b/src/remote/inet_proto.h index ebd5dcd7b0..af00e8fa5d 100644 --- a/src/remote/inet_proto.h +++ b/src/remote/inet_proto.h @@ -34,7 +34,7 @@ namespace Firebird } rem_port* INET_analyze(ClntAuthBlock*, const Firebird::PathName&, const TEXT*, - bool, Firebird::ClumpletReader&, Firebird::RefPtr*); + bool, Firebird::ClumpletReader&, Firebird::RefPtr*, const Firebird::PathName*); rem_port* INET_connect(const TEXT*, struct packet*, USHORT, Firebird::ClumpletReader*, Firebird::RefPtr*); rem_port* INET_reconnect(SOCKET); diff --git a/src/remote/os/win32/wnet.cpp b/src/remote/os/win32/wnet.cpp index c48c085bd9..0f53569266 100644 --- a/src/remote/os/win32/wnet.cpp +++ b/src/remote/os/win32/wnet.cpp @@ -105,7 +105,8 @@ rem_port* WNET_analyze(ClntAuthBlock* cBlock, const PathName& file_name, const TEXT* node_name, bool uv_flag, - RefPtr* config) + RefPtr* config, + const Firebird::PathName* ref_db_name) { /************************************** * @@ -157,8 +158,10 @@ rem_port* WNET_analyze(ClntAuthBlock* cBlock, cnct->p_cnct_operation = op_attach; cnct->p_cnct_cversion = CONNECT_VERSION3; cnct->p_cnct_client = ARCHITECTURE; - cnct->p_cnct_file.cstr_length = (ULONG) file_name.length(); - cnct->p_cnct_file.cstr_address = reinterpret_cast(file_name.c_str()); + + const PathName& cnct_file(ref_db_name ? (*ref_db_name) : file_name); + cnct->p_cnct_file.cstr_length = (ULONG) cnct_file.length(); + cnct->p_cnct_file.cstr_address = reinterpret_cast(cnct_file.c_str()); // If we want user verification, we can't speak anything less than version 7 @@ -202,6 +205,7 @@ rem_port* WNET_analyze(ClntAuthBlock* cBlock, switch (packet->p_operation) { case op_accept_data: + case op_cond_accept: accept = &packet->p_acpd; if (cBlock) { diff --git a/src/remote/os/win32/wnet_proto.h b/src/remote/os/win32/wnet_proto.h index 3c52eebcba..c2fc22288a 100644 --- a/src/remote/os/win32/wnet_proto.h +++ b/src/remote/os/win32/wnet_proto.h @@ -31,7 +31,8 @@ extern "C" { #endif -rem_port* WNET_analyze(ClntAuthBlock*, const Firebird::PathName&, const TEXT*, bool, Firebird::RefPtr*); +rem_port* WNET_analyze(ClntAuthBlock*, const Firebird::PathName&, const TEXT*, bool, + Firebird::RefPtr*, const Firebird::PathName*); rem_port* WNET_connect(const TEXT*, struct packet*, USHORT, Firebird::RefPtr*); rem_port* WNET_reconnect(HANDLE); diff --git a/src/remote/os/win32/xnet.cpp b/src/remote/os/win32/xnet.cpp index 115b41aef7..999c6f42e1 100644 --- a/src/remote/os/win32/xnet.cpp +++ b/src/remote/os/win32/xnet.cpp @@ -192,7 +192,8 @@ static void xnet_log_error(const char* err_msg) rem_port* XNET_analyze(ClntAuthBlock* cBlock, const PathName& file_name, bool uv_flag, - RefPtr* config) + RefPtr* config, + const Firebird::PathName* ref_db_name) { /************************************** * @@ -244,8 +245,10 @@ rem_port* XNET_analyze(ClntAuthBlock* cBlock, cnct->p_cnct_operation = op_attach; cnct->p_cnct_cversion = CONNECT_VERSION3; cnct->p_cnct_client = ARCHITECTURE; - cnct->p_cnct_file.cstr_length = (ULONG) file_name.length(); - cnct->p_cnct_file.cstr_address = reinterpret_cast(file_name.c_str()); + + const PathName& cnct_file(ref_db_name ? (*ref_db_name) : file_name); + cnct->p_cnct_file.cstr_length = (ULONG) cnct_file.length(); + cnct->p_cnct_file.cstr_address = reinterpret_cast(cnct_file.c_str()); cnct->p_cnct_user_id.cstr_length = (ULONG) user_id.getBufferLength(); cnct->p_cnct_user_id.cstr_address = user_id.getBuffer(); @@ -287,6 +290,7 @@ rem_port* XNET_analyze(ClntAuthBlock* cBlock, switch (packet->p_operation) { case op_accept_data: + case op_cond_accept: accept = &packet->p_acpd; if (cBlock) { diff --git a/src/remote/os/win32/xnet_proto.h b/src/remote/os/win32/xnet_proto.h index d746819e13..cfd617ca18 100644 --- a/src/remote/os/win32/xnet_proto.h +++ b/src/remote/os/win32/xnet_proto.h @@ -32,7 +32,8 @@ #define rem_port void #endif -rem_port* XNET_analyze(ClntAuthBlock*, const Firebird::PathName&, bool, Firebird::RefPtr*); +rem_port* XNET_analyze(ClntAuthBlock*, const Firebird::PathName&, bool, Firebird::RefPtr*, + const Firebird::PathName*); rem_port* XNET_connect(struct packet*, USHORT, Firebird::RefPtr*); rem_port* XNET_reconnect(ULONG); diff --git a/src/remote/protocol.cpp b/src/remote/protocol.cpp index f1684047ad..dc692f4727 100644 --- a/src/remote/protocol.cpp +++ b/src/remote/protocol.cpp @@ -339,6 +339,7 @@ bool_t xdr_protocol(XDR* xdrs, PACKET* p) return P_TRUE(xdrs, p); case op_accept_data: + case op_cond_accept: 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)); diff --git a/src/remote/protocol.h b/src/remote/protocol.h index 5247e6a179..89f4bddaf6 100644 --- a/src/remote/protocol.h +++ b/src/remote/protocol.h @@ -279,6 +279,8 @@ enum P_OP op_crypt = 96, op_crypt_key_callback = 97, + op_cond_accept = 98, // Server accepts connection, returns some data to client + // and asks client to continue authentication before attach call op_max }; @@ -375,6 +377,7 @@ const UCHAR CNCT_specific_data = 7; // Some data, needed for user verification 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 +const UCHAR CNCT_client_crypt = 11; // Client encyption level (DISABLED/ENABLED/REQUIRED) // Accept Block (Server response to connect block) diff --git a/src/remote/remot_proto.h b/src/remote/remot_proto.h index cf5c7d8856..d5da786e5b 100644 --- a/src/remote/remot_proto.h +++ b/src/remote/remot_proto.h @@ -59,11 +59,13 @@ 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, - const Firebird::string* dpb_config); + const Firebird::string* dpb_config = NULL); void REMOTE_parseList(Remote::ParsedList&, Firebird::PathName); void REMOTE_makeList(Firebird::PathName& list, const Remote::ParsedList& parsed); void REMOTE_check_response(Firebird::IStatus* warning, Rdb* rdb, PACKET* packet, bool checkKeys = false); +extern signed char wcCompatible[3][3]; + #define HANDSHAKE_DEBUG(A) #define WIRECRYPT_DEBUG(A) diff --git a/src/remote/remote.cpp b/src/remote/remote.cpp index e7a02c60be..a03393c9a5 100644 --- a/src/remote/remote.cpp +++ b/src/remote/remote.cpp @@ -91,47 +91,25 @@ const ParametersSet spbParam = isc_spb_config }; -const ParametersSet spbStartParam = +const ParametersSet connectParam = { 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 - 0, - 0, - 0, - 0, - 0 -}; - -const ParametersSet spbInfoParam = -{ - 0, - 0, - isc_info_svc_auth_block, - 0, - 0, - 0, - 0, + CNCT_login, 0, 0, 0, 0, + CNCT_plugin_name, + CNCT_plugin_list, + CNCT_specific_data, 0, 0, 0, 0, 0, 0, + CNCT_host, + CNCT_user, 0 }; @@ -1016,6 +994,32 @@ Firebird::PathName ClntAuthBlock::getPluginName() return plugins.hasData() ? plugins.name() : ""; } +template +static void addMutliPartConnectParameter(const T& dataToAdd, + Firebird::ClumpletWriter& user_id, UCHAR param) +{ + size_t remaining = dataToAdd.getCount(); + fb_assert(remaining <= 254u * 256u); // paranoid check => 65024 + UCHAR part = 0; + UCHAR buffer[255]; + typename T::const_pointer ptr = dataToAdd.begin(); + while (remaining > 0) + { + size_t 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(param, buffer, step + 1); + if (!part) // we completed 256 loops, almost impossible but check anyway. + break; + } +} + void ClntAuthBlock::extractDataFromPluginTo(Firebird::ClumpletWriter& user_id) { // Add user login name @@ -1043,26 +1047,10 @@ void ClntAuthBlock::extractDataFromPluginTo(Firebird::ClumpletWriter& user_id) // and we have no ways to override this limit cause it can be sent 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. - size_t remaining = dataFromPlugin.getCount(); - fb_assert(remaining <= 254u * 256u); // paranoid check => 65024 - UCHAR part = 0; - UCHAR buffer[255]; - const UCHAR* ptr = dataFromPlugin.begin(); - while (remaining > 0) - { - size_t step = remaining; - if (step > 254) - step = 254; - remaining -= step; - buffer[0] = part++; - fb_assert(part || remaining == 0); - memcpy(&buffer[1], ptr, step); - ptr += step; + addMutliPartConnectParameter(dataFromPlugin, user_id, CNCT_specific_data); - user_id.insertBytes(CNCT_specific_data, buffer, step + 1); - if (!part) // we completed 256 loops, almost impossible but check anyway. - break; - } + // Client's wirecrypt requested level + user_id.insertInt(CNCT_client_crypt, config->getWireCrypt(WC_CLIENT)); } void ClntAuthBlock::resetClnt(const Firebird::PathName* fileName, const CSTRING* listStr) @@ -1086,8 +1074,8 @@ void ClntAuthBlock::resetClnt(const Firebird::PathName* fileName, const CSTRING* dataForPlugin.clear(); dataFromPlugin.clear(); - authComplete = false; firstTime = true; + config = REMOTE_get_config(fileName, &dpbConfig); pluginList = config->getPlugins(Firebird::PluginType::AuthClient); @@ -1144,17 +1132,21 @@ void ClntAuthBlock::storeDataForPlugin(unsigned int length, const unsigned char* Firebird::RefPtr REMOTE_get_config(const Firebird::PathName* dbName, const Firebird::string* dpb_config) { - Firebird::RefPtr rc = Config::getDefaultConfig(); + Firebird::RefPtr config; - if (dbName) + if (dbName && dbName->hasData()) { Firebird::PathName dummy; - expandDatabaseName(*dbName, dummy, &rc); + expandDatabaseName(*dbName, dummy, &config); + } + else + { + config = Config::getDefaultConfig(); } - Config::merge(rc, dpb_config); + Config::merge(config, dpb_config); - return rc; + return config; } void REMOTE_parseList(Remote::ParsedList& parsed, Firebird::PathName list) @@ -1402,3 +1394,10 @@ bool rem_port::tryKeyType(const KnownServerKey& srvKey, InternalCryptKey* cryptK return false; } + +signed char wcCompatible[3][3] = { +/* DISABLED ENABLED REQUIRED */ +/* DISABLED */ {0, 0, -1}, +/* ENABLED */ {0, 1, 1}, +/* REQUIRED */ {-1, 1, 1} +}; diff --git a/src/remote/remote.h b/src/remote/remote.h index 1f107ba654..398081c55f 100644 --- a/src/remote/remote.h +++ b/src/remote/remote.h @@ -116,22 +116,14 @@ struct ParametersSet host_name, os_user, config_text; }; -extern const ParametersSet dpbParam, spbParam, spbStartParam, spbInfoParam; +extern const ParametersSet dpbParam, spbParam, connectParam; struct Svc : public Firebird::GlobalStorage { ServService svc_iface; // service interface - Firebird::ClumpletWriter* svc_cached_spb; // Saved auth tags from attachService() call - Firebird::Array svc_wide_auth; // Server-wide (default) authentication block - enum { - SVCAUTH_NONE, // Service is not authenticated - SVCAUTH_TEMP, // Service is authenticated for single task - SVCAUTH_PERM // Service is authenticated permanently - } svc_auth; // Authentication state of service Svc() : - svc_iface(NULL), svc_cached_spb(NULL), - svc_wide_auth(getPool()), svc_auth(SVCAUTH_NONE) + svc_iface(NULL) { } }; @@ -580,8 +572,14 @@ typedef Firebird::Array PacketQueue; class ServerAuthBase { public: + enum AuthenticateFlags { + NO_FLAGS = 0x0, + CONT_AUTH = 0x1, + USE_COND_ACCEPT = 0x2 + }; + virtual ~ServerAuthBase(); - virtual bool authenticate(PACKET* send, bool cont = false) = 0; + virtual bool authenticate(PACKET* send, AuthenticateFlags flags = NO_FLAGS) = 0; }; class ServerCallbackBase @@ -668,8 +666,6 @@ public: void extractDataFromPluginTo(Firebird::ClumpletWriter& user_id); void resetClnt(const Firebird::PathName* fileName, const CSTRING* listStr = NULL); bool checkPluginName(Firebird::PathName& nameToCheck); - void saveServiceDataTo(rem_port*); - void loadServiceDataFrom(rem_port*); Firebird::PathName getPluginName(); void tryNewKeys(rem_port*); void releaseKeys(unsigned from); @@ -696,7 +692,6 @@ private: Firebird::PathName pluginName, pluginList; // These two may be legacy encrypted password, trusted auth data and so on Firebird::UCharBuffer dataForPlugin, dataFromPlugin; - Firebird::PathName dbPath; Firebird::ClumpletWriter lastExtractedKeys; Firebird::ObjectsArray newKeys; bool flComplete, firstTime; @@ -709,7 +704,6 @@ public: : port(p_port), userName(getPool()), pluginName(getPool()), pluginList(getPool()), dataForPlugin(getPool()), dataFromPlugin(getPool()), - dbPath(getPool()), lastExtractedKeys(getPool(), Firebird::ClumpletReader::UnTagged, MAX_DPB_SIZE), newKeys(getPool()), flComplete(false), firstTime(true), @@ -723,10 +717,9 @@ public: void extractDataFromPluginTo(cstring* to); void extractDataFromPluginTo(P_AUTH_CONT* to); + void extractDataFromPluginTo(P_ACPD* to); bool authCompleted(bool flag = false); - void setPath(const Firebird::PathName* dbPath); void setLogin(const Firebird::string& user); - const char* getPath(); void load(Firebird::ClumpletReader& userId); const char* getPluginName(); void setPluginList(const Firebird::string& name); @@ -864,7 +857,7 @@ struct rem_port : public Firebird::GlobalStorage, public Firebird::RefCounted rem_str* port_host; // Our name rem_str* port_connection; // Name of connection Firebird::string port_login; - Firebird::string port_password; + Firebird::PathName port_security_db; Firebird::string port_user_name; Firebird::string port_peer_name; Firebird::string port_protocol_id; // String containing protocol name for this port @@ -888,6 +881,7 @@ struct rem_port : public Firebird::GlobalStorage, public Firebird::RefCounted // requires wire crypt active before attachDatabase() bool port_crypt_complete; // wire crypt init is complete one way or another, // up to being turned off in firebird.conf + bool port_required_encryption; // encryption is required on port Firebird::ObjectsArray port_known_server_keys; // Server sends to client // keys known by it, they are stored here Firebird::IWireCryptPlugin* port_crypt_plugin; // plugin used by port, when not NULL - crypts wire data @@ -916,7 +910,7 @@ public: port_packet_vector(0), #endif port_objects(getPool()), port_version(0), port_host(0), - port_connection(0), port_login(getPool()), port_password(getPool()), + port_connection(0), port_login(getPool()), port_security_db(getPool()), port_user_name(getPool()), port_peer_name(getPool()), port_protocol_id(getPool()), port_address(getPool()), port_rpr(0), port_statement(0), port_receive_rmtque(0), @@ -924,6 +918,7 @@ public: port_queue(getPool()), port_qoffset(0), port_srv_auth(NULL), port_srv_auth_block(NULL), port_crypt_keys(getPool()), port_need_disk_crypt(false), port_crypt_complete(false), + port_required_encryption(true), // safe default port_known_server_keys(getPool()), port_crypt_plugin(NULL), port_client_crypt_callback(NULL), port_server_crypt_callback(NULL), port_buffer(FB_NEW(getPool()) UCHAR[rpt]) @@ -1101,7 +1096,7 @@ public: { return send_response(p, obj, length, status->get(), defer_flag); } - ISC_STATUS service_attach(const char*, Firebird::ClumpletWriter*, PACKET*, bool); + ISC_STATUS service_attach(const char*, Firebird::ClumpletWriter*, PACKET*); ISC_STATUS service_end(P_RLSE*, PACKET*); void service_start(P_INFO*, PACKET*); ISC_STATUS set_cursor(P_SQLCUR*, PACKET*); diff --git a/src/remote/server/server.cpp b/src/remote/server/server.cpp index 803a0abd73..d614faa5a6 100644 --- a/src/remote/server/server.cpp +++ b/src/remote/server/server.cpp @@ -224,20 +224,63 @@ GlobalPtr remoteFailedLogins; MakeUpgradeInfo<> upInfo; +template +static void getMultiPartConnectParameter(T& putTo, Firebird::ClumpletReader& id, UCHAR param) +{ + // This array is needed only to make sure that all parts of specific data are present + UCHAR checkBytes[256]; + memset(checkBytes, 0, sizeof(checkBytes)); + UCHAR top = 0; + + for (id.rewind(); !id.isEof(); id.moveNext()) + { + if (id.getClumpTag() == param) + { + 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; + putTo.grow(offset + len); + memcpy(&putTo[offset], specData, len); + } + } + } + + for (UCHAR segment = 0; segment < top; ++segment) + { + if (!checkBytes[segment]) + { + (Arg::Gds(isc_multi_segment) << Arg::Num(segment)).raise(); + } + } + + HANDSHAKE_DEBUG(fprintf(stderr, "Srv: getMultiPartConnectParameter: loaded tag %d length %" SIZEFORMAT "\n", + param, putTo.getCount())); +} + + // delayed authentication block for auth callback class ServerAuth : public GlobalStorage, public ServerAuthBase { public: virtual void accept(PACKET* send, Auth::WriterImplementation* authBlock) = 0; - ServerAuth(const PathName* aDbName, ClumpletWriter* aPb, const ParametersSet& aTags, rem_port* port) + ServerAuth(ClumpletReader* aPb, const ParametersSet& aTags, + rem_port* port, bool multiPartData = false) : authItr(NULL), userName(getPool()), authServer(NULL), tags(&aTags), hopsCount(0), - authPort(port), - dbName(getPool()) + authPort(port) { if (!authPort->port_srv_auth_block) { @@ -253,25 +296,12 @@ public: if (authPort->port_srv_auth_block->getLogin() && userName != authPort->port_srv_auth_block->getLogin()) { - (Arg::Gds(isc_login) << Arg::Gds(isc_random) << "Client error - login does not match").raise(); + (Arg::Gds(isc_login) << Arg::Gds(isc_login_changed)).raise(); } authPort->port_srv_auth_block->setLogin(userName); HANDSHAKE_DEBUG(fprintf(stderr, "Srv: ServerAuth(): user name=%s\n", userName.c_str())); } - const char* oldPath = authPort->port_srv_auth_block->getPath(); - if (aDbName) - { - dbName = *aDbName; - if (oldPath && dbName != oldPath) - { - HANDSHAKE_DEBUG(fprintf(stderr, "Srv: ServerAuth(): 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(); - } - authPort->port_srv_auth_block->setPath(aDbName); - HANDSHAKE_DEBUG(fprintf(stderr, "Srv: ServerAuth(): db name=%s\n", dbName.c_str())); - } - UCharBuffer u; if (port->port_protocol >= PROTOCOL_VERSION13) { @@ -288,9 +318,12 @@ public: authPort->port_srv_auth_block->setPluginList(x); HANDSHAKE_DEBUG(fprintf(stderr, "Srv: ServerAuth(): plugin list=%s\n", x.c_str())); } - if (aPb->find(tags->specific_data)) + if (tags->specific_data && aPb->find(tags->specific_data)) { - aPb->getData(u); + if (multiPartData) + getMultiPartConnectParameter(u, *aPb, tags->specific_data); + else + aPb->getData(u); authPort->port_srv_auth_block->setDataForPlugin(u); HANDSHAKE_DEBUG(fprintf(stderr, "Srv: ServerAuth(): plugin data is %" SIZEFORMAT " len\n", u.getCount())); } @@ -331,7 +364,7 @@ public: ~ServerAuth() { } - bool authenticate(PACKET* send, bool contAuth = false) + bool authenticate(PACKET* send, AuthenticateFlags flags) { #ifdef DEV_BUILD if (++hopsCount > 10) @@ -339,8 +372,7 @@ public: if (++hopsCount > 100) #endif { - (Arg::Gds(isc_login) << - Arg::Gds(isc_random) << "Exceeded authentication exchange limit").raise(); + (Arg::Gds(isc_login) << Arg::Gds(isc_auth_handshake_limit)).raise(); } if (authPort->port_srv_auth_block->authCompleted()) @@ -375,7 +407,7 @@ public: } // if we asked for more data but received nothing switch to next plugin - bool forceNext = contAuth && (!authPort->port_srv_auth_block->hasDataForPlugin()); + bool forceNext = (flags & CONT_AUTH) && (!authPort->port_srv_auth_block->hasDataForPlugin()); HANDSHAKE_DEBUG(fprintf(stderr, "Srv: authenticate: ServerAuth calls plug %s\n", forceNext ? "forced-NEXT" : authItr->name())); int authResult = forceNext ? Auth::AUTH_CONTINUE : @@ -413,9 +445,20 @@ public: if (authPort->port_protocol >= PROTOCOL_VERSION13) { - send->p_operation = op_cont_auth; - authPort->port_srv_auth_block->extractDataFromPluginTo(&send->p_auth_cont); - authPort->port_srv_auth_block->extractNewKeys(&send->p_auth_cont.p_keys); + if (flags & USE_COND_ACCEPT) + { + HANDSHAKE_DEBUG(fprintf(stderr, "Srv: authenticate: send op_cond_accept\n")); + send->p_operation = op_cond_accept; + authPort->port_srv_auth_block->extractDataFromPluginTo(&send->p_acpd); + authPort->port_srv_auth_block->extractNewKeys(&send->p_acpd.p_acpt_keys); + } + else + { + HANDSHAKE_DEBUG(fprintf(stderr, "Srv: authenticate: send op_cont_auth\n")); + send->p_operation = op_cont_auth; + authPort->port_srv_auth_block->extractDataFromPluginTo(&send->p_auth_cont); + authPort->port_srv_auth_block->extractNewKeys(&send->p_auth_cont.p_keys); + } } else { @@ -479,24 +522,25 @@ private: protected: rem_port* authPort; - PathName dbName; }; class DatabaseAuth : public ServerAuth { public: - DatabaseAuth(rem_port* port, const PathName& adbName, ClumpletWriter* dpb, P_OP op) - : ServerAuth(&adbName, dpb, dpbParam, port), - operation(op), - pb(dpb) + DatabaseAuth(rem_port* port, const PathName& db, ClumpletWriter* dpb, P_OP op) + : ServerAuth(dpb, dpbParam, port), + dbName(getPool(), db), + pb(dpb), + operation(op) { } void accept(PACKET* send, Auth::WriterImplementation* authBlock); private: - P_OP operation; + PathName dbName; AutoPtr pb; + P_OP operation; }; @@ -504,7 +548,7 @@ class ServiceAttachAuth : public ServerAuth { public: ServiceAttachAuth(rem_port* port, const PathName& pmanagerName, ClumpletWriter* spb) - : ServerAuth(NULL, spb, spbParam, port), + : ServerAuth(spb, spbParam, port), managerName(getPool(), pmanagerName), pb(spb) { } @@ -1494,6 +1538,47 @@ void SRVR_multi_thread( rem_port* main_port, USHORT flags) } +bool requiredEncryption(rem_port* port, ClumpletReader& id) +{ + int clientCrypt = id.find(CNCT_client_crypt) ? id.getInt() : WIRE_CRYPT_ENABLED; + int serverCrypt = port->getPortConfig()->getWireCrypt(WC_SERVER); + if (wcCompatible[clientCrypt][serverCrypt] < 0) + { + Arg::Gds(isc_wirecrypt_incompatible).raise(); + } + + port->port_required_encryption = wcCompatible[clientCrypt][serverCrypt] > 0; + return port->port_required_encryption; +} + + +class ConnectAuth : public ServerAuth +{ +public: + ConnectAuth(rem_port* port, ClumpletReader& connect) + : ServerAuth(&connect, connectParam, port, true), useResponse(false) + { + HANDSHAKE_DEBUG(fprintf(stderr, "Srv: ConnectAuth::ConnectAuth()\n")); + } + + void accept(PACKET* send, Auth::WriterImplementation* authBlock); + + bool useResponse; +}; + + +static void setErrorStatus(IStatus* status) +{ + 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()); +} + static bool accept_connection(rem_port* port, P_CNCT* connect, PACKET* send) { /************************************** @@ -1555,19 +1640,75 @@ static bool accept_connection(rem_port* port, P_CNCT* connect, PACKET* send) HANDSHAKE_DEBUG(fprintf(stderr, "Srv: accept_connection: protoaccept a=%d (v>=13)=%d %d %d\n", accepted, version >= PROTOCOL_VERSION13, version, PROTOCOL_VERSION13)); + send->p_acpd.p_acpt_version = port->port_protocol = version; + send->p_acpd.p_acpt_architecture = architecture; + send->p_acpd.p_acpt_type = type; + send->p_acpd.p_acpt_authenticated = 0; + + send->p_acpt.p_acpt_version = port->port_protocol = version; + send->p_acpt.p_acpt_architecture = architecture; + send->p_acpt.p_acpt_type = type; + + // modify the version string to reflect the chosen protocol + string buffer; + buffer.printf("%s/P%d", port->port_version->str_data, port->port_protocol & FB_PROTOCOL_MASK); + delete port->port_version; + port->port_version = REMOTE_make_string(buffer.c_str()); + + if (architecture == ARCHITECTURE) + port->port_flags |= PORT_symmetric; + if (type != ptype_out_of_band) + port->port_flags |= PORT_no_oob; + if (type == ptype_lazy_send) + port->port_flags |= PORT_lazy; + + + Firebird::ClumpletReader id(Firebird::ClumpletReader::UnTagged, + connect->p_cnct_user_id.cstr_address, + connect->p_cnct_user_id.cstr_length); + + if (accepted) + { + // Setup correct configuration for port + PathName dbName(connect->p_cnct_file.cstr_address, connect->p_cnct_file.cstr_length); + RefPtr dbConfig = REMOTE_get_config(&dbName); + port->port_security_db = dbConfig->getSecurityDatabase(); + port->port_config = REMOTE_get_config(port->port_security_db.hasData() ? + &port->port_security_db : NULL); + + // Clear accept data + send->p_acpd.p_acpt_plugin.cstr_length = 0; + send->p_acpd.p_acpt_data.cstr_length = 0; + send->p_acpd.p_acpt_authenticated = 0; + } + + if (accepted && requiredEncryption(port, id)) + { + if (version >= PROTOCOL_VERSION13) + { + ConnectAuth* cnctAuth = new ConnectAuth(port, id); + port->port_srv_auth = cnctAuth; + if (port->port_srv_auth->authenticate(send, ServerAuth::USE_COND_ACCEPT)) + { + delete port->port_srv_auth; + port->port_srv_auth = NULL; + } + + cnctAuth->useResponse = true; + return true; + } + + accepted = false; + } + // We are going to try authentication handshake LocalStatus status; bool returnData = false; - //bool returnPlugList = false; if (accepted && version >= PROTOCOL_VERSION13) { HANDSHAKE_DEBUG(fprintf(stderr, "Srv: accept_connection: creates port_srv_auth_block\n")); port->port_srv_auth_block = new SrvAuthBlock(port); - 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, "Srv: accept_connection: is going to load data to port_srv_auth_block\n")); port->port_srv_auth_block->load(id); @@ -1581,35 +1722,53 @@ static bool accept_connection(rem_port* port, P_CNCT* connect, PACKET* send) if (port->port_srv_auth_block->getPluginName()) { - 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, "Srv: 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, - "Srv: accept_connection: call plugin %s\n", port->port_srv_auth_block->getPluginName())); - AuthServerPlugins* const plugins = port->port_srv_auth_block->plugins; - for (; plugins->hasData(); plugins->next()) + NoCaseString clientPluginName(port->port_srv_auth_block->getPluginName()); + // If there is plugin matching client's one it will be + HANDSHAKE_DEBUG(fprintf(stderr, "Srv: accept_connection: client plugin='%s' server='%s'\n", + clientPluginName.c_str(), plugins->name())); + + if (clientPluginName != plugins->name()) + { + // createPluginsItr() when possible makes current client's plugin first in the list + // i.e. if names don't match this means we can't use it and client should continue + // with the next one from us + port->port_srv_auth_block->setPluginName(plugins->name()); + port->port_srv_auth_block->extractPluginName(&send->p_acpd.p_acpt_plugin); + returnData = true; + } + else { port->port_srv_auth_block->authBlockWriter.setMethod(plugins->name()); - switch (plugins->plugin()->authenticate( - &status, port->port_srv_auth_block, - &port->port_srv_auth_block->authBlockWriter)) + switch (plugins->plugin()->authenticate(&status, port->port_srv_auth_block, + &port->port_srv_auth_block->authBlockWriter)) { case Auth::AUTH_SUCCESS: + usernameFailedLogins->loginSuccess(port->port_login); + remoteFailedLogins->loginSuccess(port->getRemoteId()); port->port_srv_auth_block->authCompleted(true); send->p_acpd.p_acpt_authenticated = 1; + returnData = true; break; case Auth::AUTH_CONTINUE: // try next plugin - continue; - // First try failed - extractNewKeys will send to client list of known plugins - //returnPlugList = true; - //break; + plugins->next(); + if (!plugins->hasData()) + { + // failed + setErrorStatus(&status); + accepted = false; + break; + } + port->port_srv_auth_block->setPluginName(plugins->name()); + port->port_srv_auth_block->extractPluginName(&send->p_acpd.p_acpt_plugin); + break; case Auth::AUTH_MORE_DATA: port->port_srv_auth_block->extractPluginName(&send->p_acpd.p_acpt_plugin); @@ -1618,20 +1777,10 @@ static bool accept_connection(rem_port* port, P_CNCT* connect, PACKET* send) 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()); - } + setErrorStatus(&status); accepted = false; break; } - break; } } } @@ -1653,36 +1802,40 @@ static bool accept_connection(rem_port* port, P_CNCT* connect, PACKET* send) { returnData = true; } + + HANDSHAKE_DEBUG(fprintf(stderr, "Srv: accept_connection: accepted ud=%d protocol=%x\n", returnData, port->port_protocol)); + send->p_operation = returnData ? op_accept_data : op_accept; - P_ACPT* accept = returnData ? &send->p_acpd : &send->p_acpt; - accept->p_acpt_version = port->port_protocol = version; - accept->p_acpt_architecture = architecture; - accept->p_acpt_type = type; - - HANDSHAKE_DEBUG(fprintf(stderr, "Srv: accept_connection: accepted ud=%d v=%x\n", returnData, version)); - - // and modify the version string to reflect the chosen protocol - - string buffer; - buffer.printf("%s/P%d", port->port_version->str_data, port->port_protocol & FB_PROTOCOL_MASK); - delete port->port_version; - port->port_version = REMOTE_make_string(buffer.c_str()); - - if (architecture == ARCHITECTURE) - port->port_flags |= PORT_symmetric; - - if (type != ptype_out_of_band) - port->port_flags |= PORT_no_oob; - - if (type == ptype_lazy_send) - port->port_flags |= PORT_lazy; - port->send(send); return true; } +void ConnectAuth::accept(PACKET* send, Auth::WriterImplementation*) +{ + HANDSHAKE_DEBUG(fprintf(stderr, "Srv: ConnectAuth::accept: accepted protocol=%x op=%s\n", + authPort->port_protocol, useResponse ? "response" : "accept")); + fb_assert(authPort->port_protocol >= PROTOCOL_VERSION13); + + if (useResponse) + { + CSTRING* const s = &send->p_resp.p_resp_data; + authPort->port_srv_auth_block->extractNewKeys(s); + ISC_STATUS sv[] = {1, 0, 0}; + authPort->send_response(send, 0, s->cstr_length, sv, false); + } + else + { + send->p_operation = op_accept_data; + CSTRING* const s = &send->p_acpd.p_acpt_keys; + authPort->port_srv_auth_block->extractNewKeys(s); + send->p_acpd.p_acpt_authenticated = 1; + authPort->send(send); + } +} + + void Rsr::checkIface() { if (!rsr_iface) @@ -1907,6 +2060,12 @@ static void attach_database(rem_port* port, P_OP operation, P_ATCH* attach, PACK * Process an attach or create packet. * **************************************/ + WIRECRYPT_DEBUG(fprintf(stderr, "Line encryption %sabled on attach\n", port->port_crypt_complete ? "en" : "dis")); + if (port->port_required_encryption && !port->port_crypt_complete) + { + Arg::Gds(isc_miss_wirecrypt).raise(); + } + ClumpletWriter* wrt = FB_NEW(*getDefaultMemoryPool()) ClumpletWriter(*getDefaultMemoryPool(), ClumpletReader::dpbList, MAX_DPB_SIZE, attach->p_atch_dpb.cstr_address, attach->p_atch_dpb.cstr_length); @@ -1928,7 +2087,7 @@ void DatabaseAuth::accept(PACKET* send, Auth::WriterImplementation* authBlock) authBlock->store(pb, isc_dpb_auth_block); - send->p_operation = op_accept; + send->p_operation = op_accept; for (pb->rewind(); !pb->isEof();) { @@ -3464,75 +3623,6 @@ ISC_STATUS rem_port::get_slice(P_SLC * stuff, PACKET* sendL) } -class ServiceQueryAuth : public ServerAuth -{ -public: - 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, spbInfoParam, port), - pb(spb), - sendItems(getPool(), ClumpletReader::SpbSendItems, MAX_DPB_SIZE, pSendItems, pSendLength, 0), - receiveItems(getPool()), - bufferLength(pBufferLength), - infoObject(pinfoObject) - { - receiveItems.add(pReceiveItems, pReceiveLength); - } - - void accept(PACKET* sendL, Auth::WriterImplementation* authBlock) - { - LocalStatus status_vector; - Rdb* rdb = authPort->port_context; - - if (bad_service(&status_vector, rdb)) - { - authPort->send_response(sendL, 0, 0, &status_vector, false); - return; - } - - authBlock->store(&sendItems, isc_info_svc_auth_block); - Array buf; - UCHAR* const buffer = buf.getBuffer(bufferLength); - memset(buffer, 0, bufferLength); - - rdb->rdb_svc->svc_iface->query(&status_vector, - (ULONG) sendItems.getBufferLength(), sendItems.getBuffer(), - (ULONG) receiveItems.getCount(), receiveItems.begin(), - bufferLength, buffer); - - SSHORT skip_len = 0; - if (buffer && *buffer == isc_info_length) - { - skip_len = gds__vax_integer(buffer + 1, 2); - const SLONG val = gds__vax_integer(buffer + 3, skip_len); - fb_assert(val >= 0); - skip_len += 3; - if (val && ULONG(val) < ULONG(bufferLength)) - bufferLength = val; - } - - // resp_data is in use - use separate packet in case of query - sendL->p_operation = op_crypt; - if (authPort->port_srv_auth_block->extractNewKeys(&sendL->p_crypt.p_key)) - { - authPort->send(sendL); - } - - sendL->p_resp.p_resp_data.cstr_address = buffer + skip_len; - authPort->send_response(sendL, infoObject, bufferLength, &status_vector, false); - } - -private: - AutoPtr pb; - ClumpletWriter sendItems; - HalfStaticArray receiveItems; - unsigned int bufferLength; - OBJCT infoObject; -}; - - void rem_port::info(P_OP op, P_INFO* stuff, PACKET* sendL) { /************************************** @@ -3590,7 +3680,6 @@ void rem_port::info(P_OP op, P_INFO* stuff, PACKET* sendL) Svc* service; ULONG info_db_len = 0; - bool needRunningService = false; switch (op) { @@ -3636,49 +3725,9 @@ void rem_port::info(P_OP op, P_INFO* stuff, PACKET* sendL) case op_service_info: service = rdb->rdb_svc; - needRunningService = fb_utils::isRunningCheck(info_buffer, info_len); - - if (service->svc_auth == Svc::SVCAUTH_PERM || - (service->svc_auth == Svc::SVCAUTH_TEMP && needRunningService)) - { - service->svc_iface->query(&status_vector, - stuff->p_info_items.cstr_length, stuff->p_info_items.cstr_address, - info_len, info_buffer, stuff->p_info_buffer_length, buffer); - } - else if (needRunningService) - { - // Want to check state of running service, but appropriate auth is missing - (Arg::Gds(isc_login) << Arg::Gds(isc_random) << "No auth of running service").raise(); - } - else - { - ClumpletWriter* wrt = service->svc_cached_spb ? - FB_NEW(*getDefaultMemoryPool()) ClumpletWriter(*getDefaultMemoryPool(), - *service->svc_cached_spb) : - FB_NEW(*getDefaultMemoryPool()) ClumpletWriter(*getDefaultMemoryPool(), - ClumpletReader::WideUnTagged, MAX_DPB_SIZE); - - 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_login.hasData()) - { - port_srv_auth_block->setLogin(port_login); - } - - if (port_srv_auth->authenticate(sendL)) - { - delete port_srv_auth; - port_srv_auth = NULL; - } - return; - } + service->svc_iface->query(&status_vector, + stuff->p_info_items.cstr_length, stuff->p_info_items.cstr_address, + info_len, info_buffer, stuff->p_info_buffer_length, buffer); break; case op_info_sql: @@ -4336,7 +4385,7 @@ static void trusted_auth(rem_port* port, const P_TRAU* p_trau, PACKET* send) HANDSHAKE_DEBUG(fprintf(stderr, "Srv: trusted_auth\n")); port->port_srv_auth_block->setDataForPlugin(p_trau->p_trau_data); - if (sa->authenticate(send, true)) + if (sa->authenticate(send, ServerAuth::CONT_AUTH)) { delete sa; port->port_srv_auth = NULL; @@ -4369,7 +4418,7 @@ static void continue_authentication(rem_port* port, const p_auth_continue* p_aut HANDSHAKE_DEBUG(fprintf(stderr, "Srv: continue_authentication\n")); port->port_srv_auth_block->setDataForPlugin(p_auth_c); - if (sa->authenticate(send, true)) + if (sa->authenticate(send, ServerAuth::CONT_AUTH)) { delete sa; port->port_srv_auth = NULL; @@ -5087,34 +5136,23 @@ static void send_error(rem_port* port, PACKET* apacket, const Firebird::Arg::Sta static void attach_service(rem_port* port, P_ATCH* attach, PACKET* sendL) { + WIRECRYPT_DEBUG(fprintf(stderr, "Line encryption %sabled on attach svc\n", port->port_crypt_complete ? "en" : "dis")); + if (port->port_required_encryption && !port->port_crypt_complete) + { + Arg::Gds(isc_miss_wirecrypt).raise(); + } + PathName manager(attach->p_atch_file.cstr_address, attach->p_atch_file.cstr_length); - if (port->port_protocol < PROTOCOL_VERSION13 || // old protocol requires immediate auth - ISC_check_if_remote(manager, false)) // service redirect - { - // 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_srv_auth = new ServiceAttachAuth(port, PathName(attach->p_atch_file.cstr_address, - attach->p_atch_file.cstr_length), wrt); + 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_srv_auth = new ServiceAttachAuth(port, + PathName(attach->p_atch_file.cstr_address, attach->p_atch_file.cstr_length), wrt); - if (port->port_srv_auth->authenticate(sendL)) - { - delete port->port_srv_auth; - port->port_srv_auth = NULL; - } - } - else + if (port->port_srv_auth->authenticate(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_login); - port->port_login.upper(); - } - port->service_attach(manager.c_str(), &spb, sendL, false); + delete port->port_srv_auth; + port->port_srv_auth = NULL; } } @@ -5124,14 +5162,13 @@ void ServiceAttachAuth::accept(PACKET* sendL, Auth::WriterImplementation* authBl authBlock->store(pb, isc_spb_auth_block); authPort->port_srv_auth_block->extractNewKeys(&sendL->p_resp.p_resp_data); - authPort->service_attach(managerName.c_str(), pb, sendL, true); + authPort->service_attach(managerName.c_str(), pb, sendL); } ISC_STATUS rem_port::service_attach(const char* service_name, ClumpletWriter* spb, - PACKET* sendL, - bool authenticated) + PACKET* sendL) { /************************************** * @@ -5148,14 +5185,6 @@ ISC_STATUS rem_port::service_attach(const char* service_name, // Now insert additional clumplets into spb addClumplets(spb, spbParam, this); - // Get ready to cache old-style auth parameters - ClumpletWriter* cache = NULL; - if (!authenticated) - { - cache = FB_NEW(*getDefaultMemoryPool()) ClumpletWriter(*getDefaultMemoryPool(), - ClumpletReader::WideUnTagged, MAX_DPB_SIZE); - } - for (spb->rewind(); !spb->isEof();) { switch (spb->getClumpTag()) @@ -5164,11 +5193,6 @@ ISC_STATUS rem_port::service_attach(const char* service_name, case isc_spb_user_name: case isc_spb_password: case isc_spb_password_enc: - if (!authenticated) - { - cache->insertClumplet(spb->getClumplet()); - } - // fall down... // remove trusted auth & trusted role if present (security measure) case isc_spb_trusted_role: @@ -5189,12 +5213,6 @@ ISC_STATUS rem_port::service_attach(const char* service_name, // they will be stuffed in the SPB if so. REMOTE_get_timeout_params(this, spb); - if (!authenticated) - { - // add fake auth block to have additional guarantee that this service never reachs database - spb->insertTag(isc_spb_auth_block); - } - if (!port_server_crypt_callback) { port_server_crypt_callback = new ServerCallback(this); @@ -5206,6 +5224,7 @@ ISC_STATUS rem_port::service_attach(const char* service_name, provider->setDbCryptCallback(&status_vector, port_server_crypt_callback->getInterface()); if (status_vector.isSuccess()) { + dumpAuthBlock("rem_port::service_attach()", spb, isc_spb_auth_block); ServService iface(provider->attachServiceManager(&status_vector, service_name, (ULONG) spb->getBufferLength(), spb->getBuffer())); @@ -5219,15 +5238,12 @@ ISC_STATUS rem_port::service_attach(const char* service_name, #endif rdb->rdb_port = this; Svc* svc = rdb->rdb_svc = new Svc; - svc->svc_auth = authenticated ? Svc::SVCAUTH_PERM : Svc::SVCAUTH_NONE; - svc->svc_cached_spb = cache; svc->svc_iface = iface; } } - return this->send_response(sendL, 0, - (authenticated ? sendL->p_resp.p_resp_data.cstr_length : 0), - &status_vector, false); + return this->send_response(sendL, 0, sendL->p_resp.p_resp_data.cstr_length, &status_vector, + false); } @@ -5261,43 +5277,6 @@ ISC_STATUS rem_port::service_end(P_RLSE* /*release*/, PACKET* sendL) } -class ServiceStartAuth : public ServerAuth -{ -public: - ServiceStartAuth(rem_port* port, PathName& dbName, ClumpletWriter* authSpb, ClumpletWriter* origSpb) - : ServerAuth(dbName.hasData() ? &dbName : NULL, authSpb, spbStartParam, port), - authPb(authSpb), startPb(origSpb) - { } - - void accept(PACKET* send, Auth::WriterImplementation* authBlock) - { - LocalStatus status_vector; - Rdb* rdb = authPort->port_context; - - if (bad_service(&status_vector, rdb)) - { - authPort->send_response(send, 0, 0, &status_vector, false); - return; - } - - while (startPb->find(isc_spb_trusted_auth)) - { - startPb->deleteClumplet(); - } - authBlock->store(startPb, isc_spb_auth_block); - - rdb->rdb_svc->svc_iface->start(&status_vector, (ULONG) startPb->getBufferLength(), startPb->getBuffer()); - rdb->rdb_svc->svc_auth = Svc::SVCAUTH_TEMP; - - authPort->port_srv_auth_block->extractNewKeys(&send->p_resp.p_resp_data); - authPort->send_response(send, 0, send->p_resp.p_resp_data.cstr_length, &status_vector, false); - } - -private: - AutoPtr authPb, startPb; -}; - - void rem_port::service_start(P_INFO * stuff, PACKET* sendL) { /************************************** @@ -5320,65 +5299,9 @@ void rem_port::service_start(P_INFO * stuff, PACKET* sendL) } Svc* svc = rdb->rdb_svc; - if (svc->svc_auth == Svc::SVCAUTH_PERM) - { - svc->svc_iface->start(&status_vector, stuff->p_info_items.cstr_length, - stuff->p_info_items.cstr_address); - this->send_response(sendL, 0, 0, &status_vector, false); - } - else - { - // Check credentials in default way right now - ClumpletWriter* authParams = svc->svc_cached_spb ? - FB_NEW(*getDefaultMemoryPool()) ClumpletWriter(*getDefaultMemoryPool(), *svc->svc_cached_spb) : - FB_NEW(*getDefaultMemoryPool()) ClumpletWriter(*getDefaultMemoryPool(), - ClumpletReader::WideUnTagged, MAX_DPB_SIZE); - - ClumpletWriter* spb = FB_NEW(*getDefaultMemoryPool()) ClumpletWriter(*getDefaultMemoryPool(), - ClumpletReader::SpbStart, MAX_DPB_SIZE, stuff->p_info_items.cstr_address, - stuff->p_info_items.cstr_length); - - PathName dbName; - for (spb->rewind(); !spb->isEof(); ) - { - bool fDel = false; - switch(spb->getClumpTag()) - { - case isc_spb_dbname: - spb->getPath(dbName); - break; - 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; - } - if (!fDel) - { - spb->moveNext(); - } - } - - if (port_srv_auth_block) - { - port_srv_auth_block->reset(); - } - delete port_srv_auth; - ///port_srv_auth = NULL; - port_srv_auth = new ServiceStartAuth(this, dbName, authParams, spb); - if (port_login.hasData()) - { - port_srv_auth_block->setLogin(port_login); - } - - if (port_srv_auth->authenticate(sendL)) - { - delete port_srv_auth; - port_srv_auth = NULL; - } - } + svc->svc_iface->start(&status_vector, stuff->p_info_items.cstr_length, + stuff->p_info_items.cstr_address); + this->send_response(sendL, 0, 0, &status_vector, false); } @@ -5439,8 +5362,7 @@ void rem_port::start_crypt(P_CRYPT * crypt, PACKET* sendL) if (! key) { - (Arg::Gds(isc_random) << "Unknown key from client").raise(); - // good idea to add keyName later + (Arg::Gds(isc_wirecrypt_key) << keyName).raise(); } PathName plugName(crypt->p_plugin.cstr_address, crypt->p_plugin.cstr_length); @@ -5458,16 +5380,14 @@ void rem_port::start_crypt(P_CRYPT * crypt, PACKET* sendL) } if (!found) { - (Arg::Gds(isc_random) << "Unknown plugin from client").raise(); - // good idea to add plugName later + (Arg::Gds(isc_wirecrypt_plugin) << plugName).raise(); } GetPlugins cp(PluginType::WireCrypt, FB_WIRECRYPT_PLUGIN_VERSION, upInfo, plugName.c_str()); if (!cp.hasData()) { - (Arg::Gds(isc_random) << "Bad plugin from client").raise(); - // good idea to add plugName later + (Arg::Gds(isc_wirecrypt_plugin) << plugName).raise(); } // Initialize crypt key @@ -6226,81 +6146,39 @@ bool SrvAuthBlock::authCompleted(bool flag) return flComplete; } -void SrvAuthBlock::setPath(const Firebird::PathName* aDbPath) -{ - if (aDbPath) - dbPath = *aDbPath; - else - dbPath = ""; -} - void SrvAuthBlock::setLogin(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 are present - UCHAR checkBytes[256]; - memset(checkBytes, 0, sizeof(checkBytes)); - UCHAR top = 0; - - for (id.rewind(); !id.isEof(); id.moveNext()) + if (id.find(CNCT_login)) { - switch (id.getClumpTag()) - { - case CNCT_login: - id.getString(userName); - userName.upper(); - HANDSHAKE_DEBUG(fprintf(stderr, "Srv: AuthBlock: login %s\n", userName.c_str())); - break; - case CNCT_plugin_name: - id.getPath(pluginName); - firstTime = false; - HANDSHAKE_DEBUG(fprintf(stderr, "Srv: AuthBlock: plugin %s\n", pluginName.c_str())); - break; - case CNCT_plugin_list: - id.getPath(pluginList); - HANDSHAKE_DEBUG(fprintf(stderr, "Srv: AuthBlock: 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; - } + id.getString(userName); + userName.upper(); + HANDSHAKE_DEBUG(fprintf(stderr, "Srv: AuthBlock: login %s\n", userName.c_str())); } - for (UCHAR segment = 0; segment < top; ++segment) + if (id.find(CNCT_plugin_name)) { - if (!checkBytes[segment]) - { - (Arg::Gds(isc_random) << "Missing segment in specific data").raise(); - } + id.getPath(pluginName); + firstTime = false; + HANDSHAKE_DEBUG(fprintf(stderr, "Srv: AuthBlock: plugin %s\n", pluginName.c_str())); } - HANDSHAKE_DEBUG(fprintf(stderr, "Srv: AuthBlock: data %" SIZEFORMAT "\n", dataForPlugin.getCount())); + if (id.find(CNCT_plugin_list)) + { + id.getPath(pluginList); + HANDSHAKE_DEBUG(fprintf(stderr, "Srv: AuthBlock: plugin list %s\n", pluginList.c_str())); + } + + dataForPlugin.clear(); + getMultiPartConnectParameter(dataForPlugin, id, CNCT_specific_data); + if (dataForPlugin.hasData()) + { + HANDSHAKE_DEBUG(fprintf(stderr, "Srv: AuthBlock: data %" SIZEFORMAT "\n", dataForPlugin.getCount())); + } } const char* SrvAuthBlock::getPluginName() @@ -6361,6 +6239,12 @@ void SrvAuthBlock::extractDataFromPluginTo(P_AUTH_CONT* to) extractPluginName(&to->p_name); } +void SrvAuthBlock::extractDataFromPluginTo(P_ACPD* to) +{ + extractDataFromPluginTo(&to->p_acpt_data); + extractPluginName(&to->p_acpt_plugin); +} + void SrvAuthBlock::extractPluginName(cstring* to) { to->cstr_length = (ULONG) pluginName.length(); @@ -6439,9 +6323,8 @@ void SrvAuthBlock::createPluginsItr() Remote::ParsedList fromClient; REMOTE_parseList(fromClient, pluginList); - RefPtr myConfig = REMOTE_get_config(dbPath.hasData() ? &dbPath : NULL, NULL); Remote::ParsedList onServer; - REMOTE_parseList(onServer, myConfig->getPlugins(PluginType::AuthServer)); + REMOTE_parseList(onServer, port->getPortConfig()->getPlugins(PluginType::AuthServer)); Remote::ParsedList final; for (unsigned s = 0; s < onServer.getCount(); ++s) @@ -6495,8 +6378,9 @@ void SrvAuthBlock::createPluginsItr() REMOTE_makeList(pluginList, final); + RefPtr portConf(port->getPortConfig()); plugins = new AuthServerPlugins(PluginType::AuthServer, FB_AUTH_SERVER_VERSION, upInfo, - myConfig, pluginList.c_str()); + portConf, pluginList.c_str()); } void SrvAuthBlock::reset() diff --git a/src/utilities/fbsvcmgr/fbsvcmgr.cpp b/src/utilities/fbsvcmgr/fbsvcmgr.cpp index d74df7f2a4..a116a2c5b0 100644 --- a/src/utilities/fbsvcmgr/fbsvcmgr.cpp +++ b/src/utilities/fbsvcmgr/fbsvcmgr.cpp @@ -312,6 +312,7 @@ const SvcSwitches attSwitch[] = {"password", putStringArgument, 0, isc_spb_password, 0}, {"fetch_password", putFileArgument, 0, isc_spb_password, 0}, {"trusted_auth", putSingleTag, 0, isc_spb_trusted_auth, 0}, + {"expected_db", putStringArgument, 0, isc_spb_expected_db, 0}, {0, 0, 0, 0, 0} }; diff --git a/src/utilities/gsec/gsec.cpp b/src/utilities/gsec/gsec.cpp index 75950afd5b..889c7faa85 100644 --- a/src/utilities/gsec/gsec.cpp +++ b/src/utilities/gsec/gsec.cpp @@ -300,7 +300,7 @@ int gsec(Firebird::UtilSvc* uSvc) manager = getPlugin.plugin(); if (!manager) { - GSEC_error_redirect((Firebird::Arg::Gds(isc_random) << "Missing management plugin").value(), GsecMsg15); + GSEC_error_redirect((Firebird::Arg::Gds(isc_random) << "Management plugin is missing or failed to load").value(), GsecMsg15); } GsecInfo info(user_data->trustedUser.get(), user_data->role.get(), diff --git a/src/yvalve/YObjects.h b/src/yvalve/YObjects.h index 104011be28..47f42a3dbf 100644 --- a/src/yvalve/YObjects.h +++ b/src/yvalve/YObjects.h @@ -450,22 +450,15 @@ public: Firebird::StatusHolder savedStatus; // Do not use raise() method of this class in yValve. }; -class YService : public Firebird::StdPlugin, public YObject, public EnterCount +class YService : public YHelper, public EnterCount { public: static const ISC_STATUS ERROR_CODE = isc_bad_svc_handle; - static const int SERV_START = 1; - static const int SERV_QUERY = 2; - static const int SERV_DETACH = 3; - - // Regular case YService(Firebird::IProvider* aProvider, Firebird::IService* aNext, bool utf8); - // Used when next handle creation is delayed till service start - YService(const char* svcName, unsigned int spbLength, const unsigned char* spb, - Firebird::ICryptKeyCallback* callback, bool utf8); ~YService(); + void shutdown(); void destroy(); FB_API_HANDLE& getHandle(); @@ -479,54 +472,13 @@ public: unsigned int spbLength, const unsigned char* spb); public: - class ServiceType - { - public: - Firebird::IProvider* provider; - Firebird::RefPtr next; - - public: - ServiceType(Firebird::IService* n, Firebird::IProvider* p) - : provider(p), next(n) - { } - - ServiceType() - : provider(NULL) - { } - - ~ServiceType(); - - void shutdown(); - void detach(Firebird::IStatus* status); - }; - - ServiceType regular, started, queryCache; - - Firebird::PathName attachName; - Firebird::AutoPtr attachSpb; - - int FB_CARG release(); - typedef IService NextInterface; typedef YService YRef; - void shutdown() - { - regular.shutdown(); - started.shutdown(); - queryCache.shutdown(); - } - - Firebird::IService* getNextService(int mode, Firebird::IStatus* status); - private: - unsigned int checkSpbLen; - const unsigned char* checkSpbPresent; - Firebird::HalfStaticArray authBlock; + Firebird::IProvider* provider; Firebird::ICryptKeyCallback* cryptCallback; bool utf8Connection; // Client talks to us using UTF8, else - system default charset - - void populateSpb(Firebird::ClumpletWriter& spb, UCHAR tag); }; class Dispatcher : public Firebird::StdPlugin diff --git a/src/yvalve/utl.cpp b/src/yvalve/utl.cpp index 3bfad5b0fb..96268257c0 100644 --- a/src/yvalve/utl.cpp +++ b/src/yvalve/utl.cpp @@ -2145,10 +2145,10 @@ static int load(ISC_QUAD* blob_id, FB_API_HANDLE database, FB_API_HANDLE transac // new utl -static inline void setTag(Firebird::ClumpletWriter& dpb, UCHAR tag, - Firebird::string& value, bool utf8) +static void setTag(Firebird::ClumpletWriter& dpb, UCHAR tag, const char* env, bool utf8) { - if (! dpb.find(tag)) + Firebird::string value; + if (fb_utils::readenv(env, value) && (!dpb.find(tag))) { if (utf8) { @@ -2169,16 +2169,15 @@ void setLogin(Firebird::ClumpletWriter& dpb, bool spbFlag) if (!(dpb.find(trusted_auth) || dpb.find(address_path) || dpb.find(auth_block))) { bool utf8 = dpb.find(utf8Tag); - Firebird::string username; - if (fb_utils::readenv(ISC_USER, username)) - { - setTag(dpb, isc_dpb_user_name, username, utf8); - } - Firebird::string password; - if (fb_utils::readenv(ISC_PASSWORD, password) && !dpb.find(isc_dpb_password)) + setTag(dpb, isc_dpb_user_name, ISC_USER, utf8); + if (!dpb.find(isc_dpb_password_enc)) { - setTag(dpb, isc_dpb_password, password, utf8); + setTag(dpb, isc_dpb_password, ISC_PASSWORD, utf8); + } + if (spbFlag) + { + setTag(dpb, isc_spb_expected_db, "FB_EXPECTED_DB", utf8); } } } diff --git a/src/yvalve/why.cpp b/src/yvalve/why.cpp index 171ed69d87..f2d50bc61d 100644 --- a/src/yvalve/why.cpp +++ b/src/yvalve/why.cpp @@ -782,13 +782,13 @@ namespace Why } template <> - YEntry::YEntry(IStatus* aStatus, YService* aService, int mode) + YEntry::YEntry(IStatus* aStatus, YService* aService, int checkService) : ref(aService), nextRef(NULL) { aStatus->init(); - init(aService->getNextService(mode, aStatus)); + init(aService->next); - if ((mode != YService::SERV_DETACH) && !(nextRef.hasData())) + if (checkService && !(nextRef.hasData())) { fini(); Arg::Gds(YService::ERROR_CODE).raise(); @@ -5007,195 +5007,54 @@ void YAttachment::getNextTransaction(IStatus* status, ITransaction* tra, NextTra //------------------------------------- -static IService* getServiceManagerByName(IProvider** provider, IStatus* status, - const char* serviceName, unsigned int spbLength, const unsigned char* spb, - Firebird::ICryptKeyCallback* cryptCallback) -{ - RefPtr config(Config::getDefaultConfig()); - - ClumpletReader readSpb(ClumpletReader::spbList, spb, spbLength); - if (readSpb.find(isc_spb_config)) - { - string spb_config; - readSpb.getString(spb_config); - Config::merge(config, &spb_config); - } - - for (GetPlugins providerIterator(PluginType::Provider, - FB_PROVIDER_VERSION, upInfo, config); - providerIterator.hasData(); - providerIterator.next()) - { - IProvider* p = providerIterator.plugin(); - - if (cryptCallback) - { - p->setDbCryptCallback(status, cryptCallback); - if (!status->isSuccess()) - continue; - } - - IService* service = p->attachServiceManager(status, serviceName, spbLength, spb); - if (status->isSuccess()) - { - if (provider) - { - p->addRef(); - *provider = p; - } - - return service; - } - - } - - if (status->isSuccess()) - { - (Arg::Gds(isc_service_att_err) << - Arg::Gds(isc_no_providers)).copyTo(status); - } - - return NULL; -} - -YService::ServiceType::~ServiceType() -{ - if (provider) - { - PluginManagerInterfacePtr()->releasePlugin(provider); - provider = NULL; - } -} - -void YService::ServiceType::shutdown() -{ - if (provider) - { - next = NULL; - PluginManagerInterfacePtr()->releasePlugin(provider); - provider = NULL; - } -} - -void YService::ServiceType::detach(IStatus* status) -{ - if (next.hasData()) - { - next->detach(status); - if (!status->isSuccess()) - { - status_exception::raise(status->get()); - } - } -} - YService::YService(IProvider* aProvider, IService* aNext, bool utf8) - : regular(aNext, aProvider), - attachName(getPool()), - checkSpbLen(0), - checkSpbPresent(NULL), - authBlock(getPool()), + : YHelper(aNext), + provider(aProvider), cryptCallback(NULL), utf8Connection(utf8) { - this->addRef(); // from YHelper + provider->addRef(); makeHandle(&services, this, handle); } -YService::YService(const char* svcName, unsigned int spbLength, const unsigned char* spb, - ICryptKeyCallback* callback, bool utf8) - : attachName(getPool()), - attachSpb(FB_NEW(getPool()) ClumpletWriter(getPool(), ClumpletReader::spbList, - MAX_DPB_SIZE, spb, spbLength)), - checkSpbLen(0), - checkSpbPresent(NULL), - authBlock(getPool()), - cryptCallback(callback), - utf8Connection(utf8) -{ - attachName.assign(svcName); - this->addRef(); // from YHelper - makeHandle(&services, this, handle); - - if (attachSpb->find(isc_spb_auth_block)) - authBlock.add(attachSpb->getBytes(), attachSpb->getClumpLength()); -} - FB_API_HANDLE& YService::getHandle() { fb_assert(handle); return handle; } -IService* YService::getNextService(int mode, IStatus* status) -{ - if (regular.next) - return regular.next; - - switch (mode) - { - case SERV_START: - if (started.provider) - started.shutdown(); - - started.next = getServiceManagerByName(&started.provider, status, attachName.c_str(), - (attachSpb ? attachSpb->getBufferLength() : 0), - (attachSpb ? attachSpb->getBuffer() : NULL), cryptCallback); - - if (!status->isSuccess()) - status_exception::raise(status->get()); - - return started.next; - - case SERV_QUERY: - if (fb_utils::isRunningCheck(checkSpbPresent, checkSpbLen)) - return started.next; - - if (queryCache.next) - return queryCache.next; - - queryCache.next = getServiceManagerByName(&queryCache.provider, status, attachName.c_str(), - (attachSpb ? attachSpb->getBufferLength() : 0), - (attachSpb ? attachSpb->getBuffer() : NULL), cryptCallback); - - if (!status->isSuccess()) - status_exception::raise(status->get()); - - return queryCache.next; - - case SERV_DETACH: - break; - - default: - fb_assert(false); - } - - return NULL; -} - YService::~YService() { + if (provider) + PluginManagerInterfacePtr()->releasePlugin(provider); } void YService::destroy() { removeHandle(&services, handle); - regular.next = NULL; - started.next = NULL; - queryCache.next = NULL; + next = NULL; release(); } +void YService::shutdown() +{ + if (provider) + { + destroy(); + PluginManagerInterfacePtr()->releasePlugin(provider); + provider = NULL; + } +} + void YService::detach(IStatus* status) { try { - YEntry entry(status, this, SERV_DETACH); + YEntry entry(status, this, 1); - regular.detach(status); - started.detach(status); - queryCache.detach(status); + if (entry.next()) + entry.next()->detach(status); destroy(); } @@ -5205,44 +5064,18 @@ void YService::detach(IStatus* status) } } -void YService::populateSpb(ClumpletWriter& spb, UCHAR tag) -{ - if (attachSpb) - attachSpb->deleteWithTag(isc_spb_auth_block); - else - attachSpb = FB_NEW(getPool()) ClumpletWriter(getPool(), ClumpletReader::spbList, MAX_DPB_SIZE); - - if (spb.find(tag)) - { - attachSpb->insertBytes(isc_spb_auth_block, spb.getBytes(), spb.getClumpLength()); - spb.deleteClumplet(); - } - else - attachSpb->insertTag(isc_spb_auth_block); -} - void YService::query(IStatus* status, unsigned int sendLength, const unsigned char* sendItems, unsigned int receiveLength, const unsigned char* receiveItems, unsigned int bufferLength, unsigned char* buffer) { try { - ClumpletWriter spb(ClumpletReader::SpbSendItems, MAX_DPB_SIZE, sendItems, sendLength); - if (!regular.next) - populateSpb(spb, isc_info_svc_auth_block); - - checkSpbLen = receiveLength; - checkSpbPresent = receiveItems; - YEntry entry(status, this, SERV_QUERY); - entry.next()->query(status, spb.getBufferLength(), spb.getBuffer(), + YEntry entry(status, this); + entry.next()->query(status, sendLength, sendItems, receiveLength, receiveItems, bufferLength, buffer); - checkSpbLen = 0; - checkSpbPresent = NULL; } catch (const Exception& e) { - checkSpbLen = 0; - checkSpbPresent = NULL; e.stuffException(status); } } @@ -5252,16 +5085,12 @@ void YService::start(IStatus* status, unsigned int spbLength, const unsigned cha try { ClumpletWriter spb(ClumpletReader::SpbStart, MAX_DPB_SIZE, spbItems, spbLength); - if (!regular.next) - { - populateSpb(spb, isc_spb_auth_block); - } if (!utf8Connection) { IntlSpbStart().toUtf8(spb, 0); } - YEntry entry(status, this, SERV_START); + YEntry entry(status, this); entry.next()->start(status, spb.getBufferLength(), spb.getBuffer()); } catch (const Exception& e) @@ -5270,25 +5099,6 @@ void YService::start(IStatus* status, unsigned int spbLength, const unsigned cha } } -int YService::release() -{ - if (--this->refCounter == 0) - { - if (regular.next || started.next || queryCache.next) - { - ++this->refCounter; // to be decremented in destroy() - ++this->refCounter; // to avoid recursion - this->destroy(); // destroy() must call release() - --this->refCounter; - } - - delete this; // call correct destructor ! - return 0; - } - - return 1; -} - //------------------------------------- @@ -5424,7 +5234,6 @@ YService* Dispatcher::attachServiceManager(IStatus* status, const char* serviceN unsigned int spbLength, const unsigned char* spb) { IService* service = NULL; - try { DispatcherEntry entry(status); @@ -5448,20 +5257,41 @@ YService* Dispatcher::attachServiceManager(IStatus* status, const char* serviceN IntlSpb().toUtf8(spbWriter, isc_spb_utf8_filename); } - if ((spbWriter.find(isc_spb_auth_block) && spbWriter.getClumpLength() > 0) || - ISC_check_if_remote(svcName, false)) + // Build correct config + RefPtr config(Config::getDefaultConfig()); + if (spbWriter.find(isc_spb_config)) { - IProvider* provider = NULL; - service = getServiceManagerByName(&provider, status, svcName.c_str(), - spbWriter.getBufferLength(), spbWriter.getBuffer(), cryptCallback); - - if (service) - return new YService(provider, service, utfData); + string spb_config; + spbWriter.getString(spb_config); + Config::merge(config, &spb_config); } - else + + for (GetPlugins providerIterator(PluginType::Provider, + FB_PROVIDER_VERSION, upInfo, config); + providerIterator.hasData(); + providerIterator.next()) { - return new YService(svcName.c_str(), spbWriter.getBufferLength(), - spbWriter.getBuffer(), cryptCallback, utfData); + IProvider* p = providerIterator.plugin(); + + if (cryptCallback) + { + p->setDbCryptCallback(status, cryptCallback); + if (!status->isSuccess()) + continue; + } + + service = p->attachServiceManager(status, svcName.c_str(), + spbWriter.getBufferLength(), spbWriter.getBuffer()); + if (status->isSuccess()) + { + return new YService(p, service, utfData); + } + } + + if (status->isSuccess()) + { + (Arg::Gds(isc_service_att_err) << + Arg::Gds(isc_no_providers)).copyTo(status); } } catch (const Exception& e)