|
From: <ale...@us...> - 2011-03-02 13:43:08
|
Revision: 52492
http://firebird.svn.sourceforge.net/firebird/?rev=52492&view=rev
Author: alexpeshkoff
Date: 2011-03-02 13:42:56 +0000 (Wed, 02 Mar 2011)
Log Message:
-----------
Fixed CORE-3369: first step to make it possible to specify non-default security database for specific database
Modified Paths:
--------------
firebird/trunk/builds/posix/make.shared.variables
firebird/trunk/src/auth/AuthInterface.h
firebird/trunk/src/auth/SecurityDatabase/LegacyManagement.epp
firebird/trunk/src/auth/SecurityDatabase/LegacyManagement.h
firebird/trunk/src/common/call_service.cpp
firebird/trunk/src/common/call_service.h
firebird/trunk/src/common/classes/ImplementHelper.h
firebird/trunk/src/common/classes/PublicHandle.cpp
firebird/trunk/src/common/classes/PublicHandle.h
firebird/trunk/src/common/classes/alloc.cpp
firebird/trunk/src/common/classes/alloc.h
firebird/trunk/src/common/classes/init.h
firebird/trunk/src/common/config/config.cpp
firebird/trunk/src/common/config/config.h
firebird/trunk/src/common/config/config_file.cpp
firebird/trunk/src/common/config/config_file.h
firebird/trunk/src/common/fb_exception.cpp
firebird/trunk/src/common/isc_sync.cpp
firebird/trunk/src/dbs/security.sql
firebird/trunk/src/include/FirebirdPluginApi.h
firebird/trunk/src/include/Interface.h
firebird/trunk/src/jrd/ExtEngineManager.cpp
firebird/trunk/src/jrd/UserManagement.cpp
firebird/trunk/src/jrd/UserManagement.h
firebird/trunk/src/jrd/dyn.epp
firebird/trunk/src/jrd/jrd.cpp
firebird/trunk/src/jrd/svc.cpp
firebird/trunk/src/jrd/trace/TraceManager.cpp
firebird/trunk/src/remote/client/interface.cpp
firebird/trunk/src/remote/server/os/posix/inet_server.cpp
firebird/trunk/src/remote/server/os/win32/srvr_w32.cpp
firebird/trunk/src/remote/server/server.cpp
firebird/trunk/src/utilities/gsec/gsec.cpp
firebird/trunk/src/utilities/gsec/gsec.h
firebird/trunk/src/utilities/gsec/gsecswi.h
firebird/trunk/src/utilities/gstat/dba.epp
firebird/trunk/src/yvalve/MasterImplementation.cpp
firebird/trunk/src/yvalve/PluginManager.cpp
firebird/trunk/src/yvalve/PluginManager.h
firebird/trunk/src/yvalve/alt.cpp
firebird/trunk/src/yvalve/why.cpp
Added Paths:
-----------
firebird/trunk/src/auth/SecurityDatabase/LegacyServer.cpp
firebird/trunk/src/auth/SecurityDatabase/LegacyServer.h
firebird/trunk/src/common/classes/GetPlugins.h
firebird/trunk/src/common/security.cpp
firebird/trunk/src/common/security.h
Removed Paths:
-------------
firebird/trunk/src/auth/SecurityDatabase/jrd_pwd.h
firebird/trunk/src/auth/SecurityDatabase/pwd.cpp
firebird/trunk/src/utilities/gsec/secur_proto.h
firebird/trunk/src/utilities/gsec/security.cpp
Modified: firebird/trunk/builds/posix/make.shared.variables
===================================================================
--- firebird/trunk/builds/posix/make.shared.variables 2011-03-02 12:25:11 UTC (rev 52491)
+++ firebird/trunk/builds/posix/make.shared.variables 2011-03-02 13:42:56 UTC (rev 52492)
@@ -36,7 +36,8 @@
GPRE_Common_Objects:= $(call dirObjects,gpre) $(call makeObjects,gpre/languages,@GPRE_LANGUAGE_MODULES@)
GPRE_std:= $(call dirObjects,gpre/std)
GPRE_Objects:= $(GPRE_Common_Objects) $(GPRE_std)
-GPRE_boot:= $(call dirObjects,gpre/boot) $(call makeObjects,yvalve,gds.cpp)
+GPRE_boot:= $(call dirObjects,gpre/boot) $(call makeObjects,yvalve,gds.cpp) \
+ $(call makeObjects,yvalve,MasterImplementation.cpp) $(call makeObjects,yvalve,PluginManager.cpp)
GPRE_Boot_Objects:= $(GPRE_Common_Objects) $(GPRE_boot)
AllObjects += $(GPRE_Common_Objects) $(GPRE_std) $(GPRE_boot)
@@ -50,8 +51,8 @@
# Remote
Remote_Common:= $(call dirObjects,remote)
-Remote_Server:= $(call dirObjects,remote/server) $(call makeObjects,auth/SecurityDatabase,pwd.cpp)
- # legacy security database pwd.cpp should become SA plugin
+Remote_Server:= $(call dirObjects,remote/server) $(call makeObjects,auth/SecurityDatabase,LegacyServer.cpp)
+ # legacy security database LegacyServer.cpp should become SA plugin
Remote_Client:= $(call dirObjects,remote/client) $(call makeObjects,auth/SecurityDatabase,LegacyClient.cpp)
Remote_Server_Objects:= $(Remote_Common) $(Remote_Server)
Remote_Client_Objects:= $(Remote_Common) $(Remote_Client)
Modified: firebird/trunk/src/auth/AuthInterface.h
===================================================================
--- firebird/trunk/src/auth/AuthInterface.h 2011-03-02 12:25:11 UTC (rev 52491)
+++ firebird/trunk/src/auth/AuthInterface.h 2011-03-02 13:42:56 UTC (rev 52492)
@@ -31,10 +31,6 @@
#include "FirebirdPluginApi.h"
-// This is temporal measure - see later
-struct internal_user_data;
-#include "../utilities/gsec/secur_proto.h"
-
namespace Firebird {
class Status;
}
@@ -81,16 +77,79 @@
};
#define FB_AUTH_CLIENT_VERSION (FB_PLUGIN_VERSION + 3)
+class UserField : public Firebird::Interface
+{
+public:
+ virtual int FB_CARG entered() = 0;
+ virtual int FB_CARG specified() = 0;
+ virtual void FB_CARG setEntered(int newValue) = 0;
+};
+#define FB_USER_FIELD_VERSION (FB_INTERFACE_VERSION + 3)
+
+class CharUserField : public UserField
+{
+public:
+ virtual const char* FB_CARG get() = 0;
+ virtual void FB_CARG set(const char* newValue) = 0;
+};
+#define FB_AUTH_CHAR_USER_VERSION (FB_USER_FIELD_VERSION + 2)
+
+class IntUserField : public UserField
+{
+public:
+ virtual int FB_CARG get() = 0;
+ virtual void FB_CARG set(int newValue) = 0;
+};
+#define FB_AUTH_INT_USER_VERSION (FB_USER_FIELD_VERSION + 2)
+
+class User : public Firebird::Interface
+{
+public:
+ virtual int FB_CARG operation() = 0;
+
+ virtual CharUserField* FB_CARG userName() = 0;
+ virtual CharUserField* FB_CARG password() = 0;
+
+ virtual CharUserField* FB_CARG firstName() = 0;
+ virtual CharUserField* FB_CARG lastName() = 0;
+ virtual CharUserField* FB_CARG middleName() = 0;
+ virtual CharUserField* FB_CARG groupName() = 0;
+
+ virtual IntUserField* FB_CARG uid() = 0;
+ virtual IntUserField* FB_CARG gid() = 0;
+ virtual IntUserField* FB_CARG admin() = 0;
+
+ virtual void FB_CARG clear() = 0;
+};
+#define FB_AUTH_USER_VERSION (FB_INTERFACE_VERSION + 11)
+
+class ListUsers : public Firebird::Interface
+{
+public:
+ virtual void FB_CARG list(User* user) = 0;
+};
+#define FB_AUTH_LIST_USERS_VERSION (FB_INTERFACE_VERSION + 1)
+
+class LogonInfo : public Firebird::Interface
+{
+public:
+ virtual const char* FB_CARG name() = 0;
+ virtual const char* FB_CARG role() = 0;
+ virtual int FB_CARG forceAdmin() = 0;
+ virtual const char* FB_CARG networkProtocol() = 0;
+ virtual const char* FB_CARG remoteAddress() = 0;
+};
+#define FB_AUTH_LOGON_INFO_VERSION (FB_INTERFACE_VERSION + 5)
+
class Management : public Firebird::Plugin
{
public:
- // work in progress - we must avoid both internal_user_data and callback function
- virtual int FB_CARG execLine(ISC_STATUS* isc_status, const char *realUser,
- FB_API_HANDLE db, FB_API_HANDLE trans,
- internal_user_data* io_user_data,
- FPTR_SECURITY_CALLBACK display_func, void* callback_arg) = 0;
+ virtual void FB_CARG start(Firebird::Status* status, LogonInfo* logonInfo) = 0;
+ virtual int FB_CARG execute(Firebird::Status* status, User* user, ListUsers* callback) = 0;
+ virtual void FB_CARG commit(Firebird::Status* status) = 0;
+ virtual void FB_CARG rollback(Firebird::Status* status) = 0;
};
-#define FB_AUTH_MANAGE_VERSION (FB_PLUGIN_VERSION + 1)
+#define FB_AUTH_MANAGE_VERSION (FB_PLUGIN_VERSION + 4)
} // namespace Auth
Modified: firebird/trunk/src/auth/SecurityDatabase/LegacyManagement.epp
===================================================================
--- firebird/trunk/src/auth/SecurityDatabase/LegacyManagement.epp 2011-03-02 12:25:11 UTC (rev 52491)
+++ firebird/trunk/src/auth/SecurityDatabase/LegacyManagement.epp 2011-03-02 13:42:56 UTC (rev 52492)
@@ -29,7 +29,7 @@
#include <ctype.h>
#include "../common/common.h"
#include "../jrd/ibase.h"
-#include "../auth/SecurityDatabase/jrd_pwd.h"
+#include "../auth/SecurityDatabase/LegacyServer.h"
#include "../common/enc_proto.h"
#include "../yvalve/gds_proto.h"
#include "../common/isc_proto.h"
@@ -42,34 +42,34 @@
#include "FirebirdPluginApi.h"
// Here we use version-independent symbolic link (or copy) of actual database
-DATABASE DB = STATIC FILENAME "security.fdb";
+DATABASE database = STATIC FILENAME "security.fdb";
static Firebird::GlobalPtr<Firebird::Mutex> execLineMutex; // protects various gpre generated structures
-static bool grantRevokeAdmin(ISC_STATUS* isc_status, FB_API_HANDLE DB, FB_API_HANDLE trans,
- const internal_user_data* io_user_data)
+static bool grantRevokeAdmin(ISC_STATUS* isc_status, FB_API_HANDLE database, FB_API_HANDLE trans,
+ Auth::User* user)
{
- if (!io_user_data->admin_entered)
+ if (!user->admin()->entered())
{
return true;
}
Firebird::string sql;
- sql.printf((io_user_data->admin ? "GRANT %s TO \"%s\"" : "REVOKE %s FROM \"%s\""),
- "RDB$ADMIN", io_user_data->user_name);
- isc_dsql_execute_immediate(isc_status, &DB, &trans, sql.length(), sql.c_str(), SQL_DIALECT_V6, NULL);
+ sql.printf((user->admin()->get() ? "GRANT %s TO \"%s\"" : "REVOKE %s FROM \"%s\""),
+ "RDB$ADMIN", user->userName()->get());
+ isc_dsql_execute_immediate(isc_status, &database, &trans, sql.length(), sql.c_str(), SQL_DIALECT_V6, NULL);
- if (isc_status[1] && io_user_data->admin == 0)
+ if (isc_status[1] && user->admin()->get() == 0)
{
isc_req_handle request = 0;
FOR (TRANSACTION_HANDLE trans REQUEST_HANDLE request) R IN RDB$USER_PRIVILEGES
- WITH R.RDB$USER EQ io_user_data->user_name
+ WITH R.RDB$USER EQ user->userName()->get()
AND R.RDB$RELATION_NAME EQ 'RDB$ADMIN'
AND R.RDB$PRIVILEGE EQ 'M'
sql.printf("REVOKE RDB$ADMIN FROM \"%s\" GRANTED BY \"%s\"",
- io_user_data->user_name, R.RDB$GRANTOR);
+ user->userName()->get(), R.RDB$GRANTOR);
END_FOR
if (request)
@@ -81,20 +81,20 @@
}
}
- isc_dsql_execute_immediate(isc_status, &DB, &trans, sql.length(), sql.c_str(), SQL_DIALECT_V6, NULL);
+ isc_dsql_execute_immediate(isc_status, &database, &trans, sql.length(), sql.c_str(), SQL_DIALECT_V6, NULL);
}
return isc_status[1] == 0;
}
/*
-static bool storePasswd(ISC_STATUS* isc_status, FB_API_HANDLE DB, FB_API_HANDLE trans,
+static bool storePasswd(ISC_STATUS* isc_status, FB_API_HANDLE database, FB_API_HANDLE trans,
ISC_QUAD& blobId, const Firebird::string& passwd)
{
UserBlob blob(isc_status);
const UCHAR blob_desc[] = {isc_bpb_version1, isc_bpb_type, 1, isc_blob_untyped};
- if (!blob.create(DB, trans, blobId, sizeof(blob_desc), blob_desc))
+ if (!blob.create(database, trans, blobId, sizeof(blob_desc), blob_desc))
{
return false;
}
@@ -108,15 +108,143 @@
}
*/
-// work in progress - we must avoid both internal_user_data and callback function
-int Auth::SecurityDatabaseManagement::execLine(ISC_STATUS* isc_status,
- const char* realUser,
- FB_API_HANDLE DB,
- FB_API_HANDLE trans,
- internal_user_data* io_user_data,
- FPTR_SECURITY_CALLBACK display_func,
- void* callback_arg)
+const static unsigned int INIT_KEY = ((~0) - 1);
+static unsigned int secDbKey = INIT_KEY;
+
+namespace Auth {
+
+SecurityDatabaseManagement::SecurityDatabaseManagement(Firebird::IFactoryParameter* par)
+ : config(par->getFirebirdConf()), database(0), transaction(0), userName(getPool())
{
+ config->release();
+}
+
+void FB_CARG SecurityDatabaseManagement::start(Firebird::Status* st, LogonInfo* logonInfo)
+{
+ try
+ {
+ st->init();
+
+ if (secDbKey == INIT_KEY)
+ {
+ secDbKey = config->getKey("SecurityDatabase");
+ }
+ const char* secDbName = config->asString(secDbKey);
+
+ if (!(secDbName && secDbName[0]))
+ {
+ (Firebird::Arg::Gds(isc_random) << "Error getting security database name").raise();
+ }
+
+ Firebird::ClumpletWriter dpb(Firebird::ClumpletReader::dpbList, MAX_DPB_SIZE);
+ dpb.insertByte(isc_dpb_gsec_attach, TRUE);
+
+ const char* str = logonInfo->name();
+ if (str && str[0])
+ {
+ dpb.insertString(isc_dpb_trusted_auth, str, strlen(str));
+ userName = str;
+ }
+ else
+ {
+ userName = "<Unknown>";
+ }
+
+ str = logonInfo->role();
+ if (str && str[0])
+ dpb.insertString(isc_dpb_sql_role_name, str, strlen(str));
+ else if (logonInfo->forceAdmin())
+ dpb.insertString(isc_dpb_trusted_role, ADMIN_ROLE, strlen(ADMIN_ROLE));
+
+ // Work in progress - should put remote data to DPB !!!
+
+ ISC_STATUS_ARRAY status;
+ if (isc_attach_database(status, 0, secDbName, &database,
+ dpb.getBufferLength(), reinterpret_cast<const char*>(dpb.getBuffer())))
+ {
+ Firebird::status_exception::raise(status);
+ }
+
+ if (isc_start_transaction(status, &transaction, 1, &database, 0, NULL))
+ {
+ Firebird::status_exception::raise(status);
+ }
+ }
+ catch (const Firebird::Exception& ex)
+ {
+ ex.stuffException(st);
+ }
+}
+
+void FB_CARG SecurityDatabaseManagement::commit(Firebird::Status* st)
+{
+ try
+ {
+ st->init();
+
+ ISC_STATUS_ARRAY status;
+ if (transaction)
+ {
+ if (isc_commit_transaction(status, &transaction))
+ {
+ Firebird::status_exception::raise(status);
+ }
+ }
+ }
+ catch (const Firebird::Exception& ex)
+ {
+ ex.stuffException(st);
+ }
+}
+
+void FB_CARG SecurityDatabaseManagement::rollback(Firebird::Status* st)
+{
+ try
+ {
+ st->init();
+
+ ISC_STATUS_ARRAY status;
+ if (transaction)
+ {
+ if (isc_rollback_transaction(status, &transaction))
+ {
+ Firebird::status_exception::raise(status);
+ }
+ }
+ }
+ catch (const Firebird::Exception& ex)
+ {
+ ex.stuffException(st);
+ }
+}
+
+int FB_CARG SecurityDatabaseManagement::release()
+{
+ if (--refCounter == 0)
+ {
+ ISC_STATUS_ARRAY status;
+ if (transaction)
+ isc_rollback_transaction(status, &transaction);
+ if (database)
+ isc_detach_database(status, &database);
+
+ delete this;
+ return 0;
+ }
+
+ return 1;
+}
+
+#define STR_STORE(to, from) strStore(to, from, sizeof(to))
+
+static inline void strStore(char* to, const char* from, size_t len)
+{
+ to[--len] = '\0';
+ strncpy(to, from, len);
+}
+
+int FB_CARG SecurityDatabaseManagement::execute(Firebird::Status* st, User* user, ListUsers* callback)
+{
/*************************************
*
* S E C U R I T Y _ e x e c _ l i n e
@@ -177,380 +305,346 @@
* -lname <lastname>
*
**************************************/
- Firebird::MutexLockGuard guard(execLineMutex);
-
- SCHAR encrypted1[Auth::MAX_PASSWORD_LENGTH + 2];
- Firebird::string encrypted2;
- bool found;
int ret = 0;
- // check for non-printable characters in user name
- for (const TEXT* p = io_user_data->user_name; *p; p++)
+ try
{
- if (! isprint(*p)) {
- return GsecMsg75; // Add special error message for this case ?
- }
- }
+ ISC_STATUS_ARRAY isc_status;
+ fb_utils::init_status(isc_status);
+ st->init();
- isc_req_handle request = 0;
- isc_req_handle request2 = 0;
+ Firebird::MutexLockGuard guard(execLineMutex);
- switch (io_user_data->operation)
- {
- case MAP_DROP_OPER:
- case MAP_SET_OPER:
- if (realUser)
+ SCHAR encrypted1[MAX_PASSWORD_LENGTH + 2];
+ Firebird::string encrypted2;
+ bool found;
+
+ // check for non-printable characters in user name
+ for (const TEXT* p = user->userName()->get(); *p; p++)
{
- ret = GsecMsg104;
- break;
- }
- {
- Firebird::string sql;
- sql.printf("ALTER ROLE RDB$ADMIN %s AUTO ADMIN MAPPING",
- io_user_data->operation == MAP_SET_OPER ? "SET" : "DROP");
- isc_dsql_execute_immediate(isc_status, &DB, &trans, sql.length(), sql.c_str(), 1, NULL);
- if (isc_status[1] != 0)
+ if (!isprint(*p))
{
- ret = GsecMsg97;
+ return GsecMsg75; // Add special error message for this case ?
}
}
- break;
- case ADD_OPER:
- if (realUser)
+
+ isc_req_handle request = 0;
+ isc_req_handle request2 = 0;
+
+ switch (user->operation())
{
- ret = GsecMsg104;
- break;
- }
- // this checks the "entered" flags for each parameter (except the name)
- // and makes all non-entered parameters null valued
-
- STORE (TRANSACTION_HANDLE trans REQUEST_HANDLE request) U IN PLG$USERS USING
- strcpy(U.PLG$USER_NAME, io_user_data->user_name);
- if (io_user_data->uid_entered)
+ case MAP_DROP_OPER:
+ case MAP_SET_OPER:
{
- U.PLG$UID = io_user_data->uid;
- U.PLG$UID.NULL = ISC_FALSE;
+ Firebird::string sql;
+ sql.printf("ALTER ROLE RDB$ADMIN %s AUTO ADMIN MAPPING",
+ user->operation() == MAP_SET_OPER ? "SET" : "DROP");
+ isc_dsql_execute_immediate(isc_status, &database, &transaction, sql.length(), sql.c_str(), 1, NULL);
+ if (isc_status[1] != 0)
+ {
+ ret = GsecMsg97;
+ }
}
- else
- U.PLG$UID.NULL = ISC_TRUE;
- if (io_user_data->gid_entered)
- {
- U.PLG$GID = io_user_data->gid;
- U.PLG$GID.NULL = ISC_FALSE;
- }
- else
- U.PLG$GID.NULL = ISC_TRUE;
- if (io_user_data->group_name_entered)
- {
- strcpy(U.PLG$GROUP_NAME, io_user_data->group_name);
- U.PLG$GROUP_NAME.NULL = ISC_FALSE;
- }
- else
- U.PLG$GROUP_NAME.NULL = ISC_TRUE;
- if (io_user_data->password_entered)
- {
- ENC_crypt(encrypted1, sizeof encrypted1, io_user_data->password, Auth::PASSWORD_SALT);
- Auth::SecurityDatabase::hash(encrypted2, io_user_data->user_name, &encrypted1[2]);
- strcpy(U.PLG$PASSWD, encrypted2.c_str());
- U.PLG$PASSWD.NULL = ISC_FALSE;
- }
- else
- U.PLG$PASSWD.NULL = ISC_TRUE;
- if (io_user_data->first_name_entered)
- {
- strcpy(U.PLG$FIRST_NAME, io_user_data->first_name);
- U.PLG$FIRST_NAME.NULL = ISC_FALSE;
- }
- else
- U.PLG$FIRST_NAME.NULL = ISC_TRUE;
- if (io_user_data->middle_name_entered)
- {
- strcpy(U.PLG$MIDDLE_NAME, io_user_data->middle_name);
- U.PLG$MIDDLE_NAME.NULL = ISC_FALSE;
- }
- else
- U.PLG$MIDDLE_NAME.NULL = ISC_TRUE;
- if (io_user_data->last_name_entered)
- {
- strcpy(U.PLG$LAST_NAME, io_user_data->last_name);
- U.PLG$LAST_NAME.NULL = ISC_FALSE;
- }
- else
- U.PLG$LAST_NAME.NULL = ISC_TRUE;
- END_STORE
- ON_ERROR
- ret = GsecMsg19; // gsec - add record error
- END_ERROR;
- if (ret == 0 && !grantRevokeAdmin(isc_status, DB, trans, io_user_data))
- {
- ret = GsecMsg19; // gsec - add record error
- }
- break;
-
- case MOD_OPER:
- if (realUser && strcmp(realUser, io_user_data->user_name))
- {
- ret = GsecMsg104;
break;
- }
- // this updates an existing record, replacing all fields that are
- // entered, and for those that were specified but not entered, it
- // changes the current value to the null value
+ case ADD_OPER:
+ // this checks the "entered" flags for each parameter (except the name)
+ // and makes all non-entered parameters null valued
- found = false;
- FOR (TRANSACTION_HANDLE trans REQUEST_HANDLE request) U IN PLG$USERS
- WITH U.PLG$USER_NAME EQ io_user_data->user_name
- found = true;
- MODIFY U USING
- if (io_user_data->uid_entered)
+ STORE (TRANSACTION_HANDLE transaction REQUEST_HANDLE request) U IN PLG$VIEW_USERS USING
+ STR_STORE(U.PLG$USER_NAME, user->userName()->get());
+ if (user->uid()->entered())
{
- U.PLG$UID = io_user_data->uid;
+ U.PLG$UID = user->uid()->get();
U.PLG$UID.NULL = ISC_FALSE;
}
- else if (io_user_data->uid_specified)
+ else
U.PLG$UID.NULL = ISC_TRUE;
- if (io_user_data->gid_entered)
+ if (user->gid()->entered())
{
- U.PLG$GID = io_user_data->gid;
+ U.PLG$GID = user->gid()->get();
U.PLG$GID.NULL = ISC_FALSE;
}
- else if (io_user_data->gid_specified)
+ else
U.PLG$GID.NULL = ISC_TRUE;
- if (io_user_data->group_name_entered)
+ if (user->groupName()->entered())
{
- strcpy(U.PLG$GROUP_NAME, io_user_data->group_name);
+ STR_STORE(U.PLG$GROUP_NAME, user->groupName()->get());
U.PLG$GROUP_NAME.NULL = ISC_FALSE;
}
- else if (io_user_data->group_name_specified)
+ else
U.PLG$GROUP_NAME.NULL = ISC_TRUE;
- if (io_user_data->password_entered)
+ if (user->password()->entered())
{
- ENC_crypt(encrypted1, sizeof encrypted1, io_user_data->password, Auth::PASSWORD_SALT);
- Auth::SecurityDatabase::hash(encrypted2, io_user_data->user_name, &encrypted1[2]);
- strcpy(U.PLG$PASSWD, encrypted2.c_str());
+ ENC_crypt(encrypted1, sizeof encrypted1, user->password()->get(), PASSWORD_SALT);
+ SecurityDatabase::hash(encrypted2, user->userName()->get(), &encrypted1[2]);
+ STR_STORE(U.PLG$PASSWD, encrypted2.c_str());
U.PLG$PASSWD.NULL = ISC_FALSE;
}
- else if (io_user_data->password_specified)
+ else
U.PLG$PASSWD.NULL = ISC_TRUE;
- if (io_user_data->first_name_entered)
+ if (user->firstName()->entered())
{
- strcpy(U.PLG$FIRST_NAME, io_user_data->first_name);
+ STR_STORE(U.PLG$FIRST_NAME, user->firstName()->get());
U.PLG$FIRST_NAME.NULL = ISC_FALSE;
}
- else if (io_user_data->first_name_specified)
+ else
U.PLG$FIRST_NAME.NULL = ISC_TRUE;
- if (io_user_data->middle_name_entered)
+ if (user->middleName()->entered())
{
- strcpy(U.PLG$MIDDLE_NAME, io_user_data->middle_name);
+ STR_STORE(U.PLG$MIDDLE_NAME, user->middleName()->get());
U.PLG$MIDDLE_NAME.NULL = ISC_FALSE;
}
- else if (io_user_data->middle_name_specified)
+ else
U.PLG$MIDDLE_NAME.NULL = ISC_TRUE;
- if (io_user_data->last_name_entered)
+ if (user->lastName()->entered())
{
- strcpy(U.PLG$LAST_NAME, io_user_data->last_name);
+ STR_STORE(U.PLG$LAST_NAME, user->lastName()->get());
U.PLG$LAST_NAME.NULL = ISC_FALSE;
}
- else if (io_user_data->last_name_specified)
+ else
U.PLG$LAST_NAME.NULL = ISC_TRUE;
- END_MODIFY
+ END_STORE
ON_ERROR
- ret = GsecMsg20;
+ ret = GsecMsg19; // gsec - add record error
END_ERROR;
- END_FOR
- ON_ERROR
- ret = GsecMsg21;
- END_ERROR;
- if (!ret && !found)
- ret = GsecMsg22;
- if (ret == 0 && !grantRevokeAdmin(isc_status, DB, trans, io_user_data))
- {
- ret = GsecMsg21;
- }
- break;
-
- case DEL_OPER:
- if (realUser)
- {
- ret = GsecMsg104;
+ if (ret == 0 && !grantRevokeAdmin(isc_status, database, transaction, user))
+ {
+ ret = GsecMsg19; // gsec - add record error
+ }
break;
- }
- // looks up the specified user record and deletes it
+ case MOD_OPER:
+ // this updates an existing record, replacing all fields that are
+ // entered, and for those that were specified but not entered, it
+ // changes the current value to the null value
- found = false;
- // Do not allow SYSDBA user to be deleted
- if (!fb_utils::stricmp(io_user_data->user_name, SYSDBA_USER_NAME))
- ret = GsecMsg23;
- else
- {
- FOR (TRANSACTION_HANDLE trans REQUEST_HANDLE request) U IN PLG$USERS
- WITH U.PLG$USER_NAME EQ io_user_data->user_name
+ found = false;
+ FOR (TRANSACTION_HANDLE transaction REQUEST_HANDLE request) U IN PLG$VIEW_USERS
+ WITH U.PLG$USER_NAME EQ user->userName()->get()
found = true;
- ERASE U
+ MODIFY U USING
+ if (user->uid()->entered())
+ {
+ U.PLG$UID = user->uid()->get();
+ U.PLG$UID.NULL = ISC_FALSE;
+ }
+ else if (user->uid()->specified())
+ U.PLG$UID.NULL = ISC_TRUE;
+ if (user->gid()->entered())
+ {
+ U.PLG$GID = user->gid()->get();
+ U.PLG$GID.NULL = ISC_FALSE;
+ }
+ else if (user->gid()->specified())
+ U.PLG$GID.NULL = ISC_TRUE;
+ if (user->groupName()->entered())
+ {
+ STR_STORE(U.PLG$GROUP_NAME, user->groupName()->get());
+ U.PLG$GROUP_NAME.NULL = ISC_FALSE;
+ }
+ else if (user->groupName()->specified())
+ U.PLG$GROUP_NAME.NULL = ISC_TRUE;
+ if (user->password()->entered())
+ {
+ ENC_crypt(encrypted1, sizeof encrypted1, user->password()->get(), PASSWORD_SALT);
+ SecurityDatabase::hash(encrypted2, user->userName()->get(), &encrypted1[2]);
+ STR_STORE(U.PLG$PASSWD, encrypted2.c_str());
+ U.PLG$PASSWD.NULL = ISC_FALSE;
+ }
+ else if (user->password()->specified())
+ U.PLG$PASSWD.NULL = ISC_TRUE;
+ if (user->firstName()->entered())
+ {
+ STR_STORE(U.PLG$FIRST_NAME, user->firstName()->get());
+ U.PLG$FIRST_NAME.NULL = ISC_FALSE;
+ }
+ else if (user->firstName()->specified())
+ U.PLG$FIRST_NAME.NULL = ISC_TRUE;
+ if (user->middleName()->entered())
+ {
+ STR_STORE(U.PLG$MIDDLE_NAME, user->middleName()->get());
+ U.PLG$MIDDLE_NAME.NULL = ISC_FALSE;
+ }
+ else if (user->middleName()->specified())
+ U.PLG$MIDDLE_NAME.NULL = ISC_TRUE;
+ if (user->lastName()->entered())
+ {
+ STR_STORE(U.PLG$LAST_NAME, user->lastName()->get());
+ U.PLG$LAST_NAME.NULL = ISC_FALSE;
+ }
+ else if (user->lastName()->specified())
+ U.PLG$LAST_NAME.NULL = ISC_TRUE;
+ END_MODIFY
ON_ERROR
- ret = GsecMsg23; // gsec - delete record error
+ ret = GsecMsg20;
END_ERROR;
END_FOR
ON_ERROR
- ret = GsecMsg24; // gsec - find/delete record error
+ ret = GsecMsg21;
END_ERROR;
- }
+ if (!ret && !found)
+ ret = GsecMsg22;
+ if (ret == 0 && !grantRevokeAdmin(isc_status, database, transaction, user))
+ {
+ ret = GsecMsg21;
+ }
+ break;
- if (!ret && !found)
- ret = GsecMsg22; // gsec - record not found for user:
+ case DEL_OPER:
+ // looks up the specified user record and deletes it
- io_user_data->admin = 0;
- io_user_data->admin_entered = true;
- if (ret == 0 && ! grantRevokeAdmin(isc_status, DB, trans, io_user_data))
- {
- ret = GsecMsg24;
- }
- break;
-
- case DIS_OPER:
- case OLD_DIS_OPER:
- if (realUser)
- {
- if (io_user_data->user_name_entered && strcmp(realUser, io_user_data->user_name))
+ found = false;
+ // Do not allow SYSDBA user to be deleted
+ if (!fb_utils::stricmp(user->userName()->get(), SYSDBA_USER_NAME))
+ ret = GsecMsg23;
+ else
{
- ret = GsecMsg104;
- break;
+ FOR (TRANSACTION_HANDLE transaction REQUEST_HANDLE request) U IN PLG$VIEW_USERS
+ WITH U.PLG$USER_NAME EQ user->userName()->get()
+ found = true;
+ ERASE U
+ ON_ERROR
+ ret = GsecMsg23; // gsec - delete record error
+ END_ERROR;
+ END_FOR
+ ON_ERROR
+ ret = GsecMsg24; // gsec - find/delete record error
+ END_ERROR;
}
- if (!io_user_data->user_name_entered)
+
+ if (!ret && !found)
+ ret = GsecMsg22; // gsec - record not found for user:
+
+ user->admin()->set(0);
+ user->admin()->setEntered(1);
+ if (ret == 0 && !grantRevokeAdmin(isc_status, database, transaction, user))
{
- if (strlen(realUser) > sizeof(io_user_data->user_name) - 1)
- {
- ret = GsecMsg104;
- break;
- }
- strcpy(io_user_data->user_name, realUser);
- io_user_data->user_name_entered = true;
+ ret = GsecMsg24;
}
- }
+ break;
- // gets either the desired record, or all records, and displays them
+ case DIS_OPER:
+ case OLD_DIS_OPER:
+ // gets either the desired record, or all records, and displays them
- found = false;
- if (!io_user_data->user_name_entered)
- {
- FOR (TRANSACTION_HANDLE trans REQUEST_HANDLE request) U IN PLG$USERS
- io_user_data->uid = U.PLG$UID;
- io_user_data->uid_entered = !U.PLG$UID.NULL;
- io_user_data->gid = U.PLG$GID;
- io_user_data->gid_entered = !U.PLG$GID.NULL;
- strcpy(io_user_data->user_name, U.PLG$USER_NAME);
- io_user_data->user_name_entered = !U.PLG$USER_NAME.NULL;
- strcpy(io_user_data->group_name, U.PLG$GROUP_NAME);
- io_user_data->group_name_entered = !U.PLG$GROUP_NAME.NULL;
- io_user_data->password[0] = 0;
- io_user_data->password_entered = false;
- strcpy(io_user_data->first_name, U.PLG$FIRST_NAME);
- io_user_data->first_name_entered = !U.PLG$FIRST_NAME.NULL;
- strcpy(io_user_data->middle_name, U.PLG$MIDDLE_NAME);
- io_user_data->middle_name_entered = !U.PLG$MIDDLE_NAME.NULL;
- strcpy(io_user_data->last_name, U.PLG$LAST_NAME);
- io_user_data->last_name_entered = !U.PLG$LAST_NAME.NULL;
+ found = false;
+ if (!user->userName()->entered())
+ {
+ FOR (TRANSACTION_HANDLE transaction REQUEST_HANDLE request) U IN PLG$VIEW_USERS
+ user->uid()->set(U.PLG$UID);
+ user->uid()->setEntered(U.PLG$UID.NULL ? 0 : 1);
+ user->gid()->set(U.PLG$GID);
+ user->gid()->setEntered(U.PLG$GID.NULL ? 0 : 1);
+ user->userName()->set(U.PLG$USER_NAME);
+ user->userName()->setEntered(U.PLG$USER_NAME.NULL ? 0 : 1);
+ user->groupName()->set(U.PLG$GROUP_NAME);
+ user->groupName()->setEntered(U.PLG$GROUP_NAME.NULL ? 0 : 1);
+ user->password()->set("");
+ user->password()->setEntered(0);
+ user->firstName()->set(U.PLG$FIRST_NAME);
+ user->firstName()->setEntered(U.PLG$FIRST_NAME.NULL ? 0 : 1);
+ user->middleName()->set(U.PLG$MIDDLE_NAME);
+ user->middleName()->setEntered(U.PLG$MIDDLE_NAME.NULL ? 0 : 1);
+ user->lastName()->set(U.PLG$LAST_NAME);
+ user->lastName()->setEntered(U.PLG$LAST_NAME.NULL ? 0 : 1);
- io_user_data->admin = 0;
- FOR (TRANSACTION_HANDLE trans REQUEST_HANDLE request2) P IN RDB$USER_PRIVILEGES
- WITH P.RDB$USER EQ U.PLG$USER_NAME
- AND P.RDB$RELATION_NAME EQ 'RDB$ADMIN'
- AND P.RDB$PRIVILEGE EQ 'M'
- io_user_data->admin = 1;
+ user->admin()->set(0);
+ FOR (TRANSACTION_HANDLE transaction REQUEST_HANDLE request2) P IN RDB$USER_PRIVILEGES
+ WITH P.RDB$USER EQ U.PLG$USER_NAME
+ AND P.RDB$RELATION_NAME EQ 'RDB$ADMIN'
+ AND P.RDB$PRIVILEGE EQ 'M'
+ user->admin()->set(1);
+ END_FOR
+
+ callback->list(user);
+
+ found = true;
END_FOR
+ ON_ERROR
+ ret = GsecMsg28; // gsec - find/display record error
+ END_ERROR;
+ }
+ else
+ {
+ FOR (TRANSACTION_HANDLE transaction REQUEST_HANDLE request) U IN PLG$VIEW_USERS
+ WITH U.PLG$USER_NAME EQ user->userName()->get()
+ user->uid()->set(U.PLG$UID);
+ user->uid()->setEntered(U.PLG$UID.NULL ? 0 : 1);
+ user->gid()->set(U.PLG$GID);
+ user->gid()->setEntered(U.PLG$GID.NULL ? 0 : 1);
+ user->userName()->set(U.PLG$USER_NAME);
+ user->userName()->setEntered(U.PLG$USER_NAME.NULL ? 0 : 1);
+ user->groupName()->set(U.PLG$GROUP_NAME);
+ user->groupName()->setEntered(U.PLG$GROUP_NAME.NULL ? 0 : 1);
+ user->password()->set("");
+ user->password()->setEntered(0);
+ user->firstName()->set(U.PLG$FIRST_NAME);
+ user->firstName()->setEntered(U.PLG$FIRST_NAME.NULL ? 0 : 1);
+ user->middleName()->set(U.PLG$MIDDLE_NAME);
+ user->middleName()->setEntered(U.PLG$MIDDLE_NAME.NULL ? 0 : 1);
+ user->lastName()->set(U.PLG$LAST_NAME);
+ user->lastName()->setEntered(U.PLG$LAST_NAME.NULL ? 0 : 1);
- display_func(callback_arg, io_user_data, !found);
+ user->admin()->set(0);
+ FOR (TRANSACTION_HANDLE transaction REQUEST_HANDLE request2) P IN RDB$USER_PRIVILEGES
+ WITH P.RDB$USER EQ U.PLG$USER_NAME
+ AND P.RDB$RELATION_NAME EQ 'RDB$ADMIN'
+ AND P.RDB$PRIVILEGE EQ 'M'
+ user->admin()->set(1);
+ END_FOR
- found = true;
- END_FOR
- ON_ERROR
- ret = GsecMsg28; // gsec - find/display record error
- END_ERROR;
- }
- else
- {
- FOR (TRANSACTION_HANDLE trans REQUEST_HANDLE request) U IN PLG$USERS
- WITH U.PLG$USER_NAME EQ io_user_data->user_name
- io_user_data->uid = U.PLG$UID;
- io_user_data->uid_entered = !U.PLG$UID.NULL;
- io_user_data->gid = U.PLG$GID;
- io_user_data->gid_entered = !U.PLG$GID.NULL;
- strcpy(io_user_data->user_name, U.PLG$USER_NAME);
- io_user_data->user_name_entered = !U.PLG$USER_NAME.NULL;
- strcpy(io_user_data->group_name, U.PLG$GROUP_NAME);
- io_user_data->group_name_entered = !U.PLG$GROUP_NAME.NULL;
- io_user_data->password[0] = 0;
- io_user_data->password_entered = false;
- strcpy(io_user_data->first_name, U.PLG$FIRST_NAME);
- io_user_data->first_name_entered = !U.PLG$FIRST_NAME.NULL;
- strcpy(io_user_data->middle_name, U.PLG$MIDDLE_NAME);
- io_user_data->middle_name_entered = !U.PLG$MIDDLE_NAME.NULL;
- strcpy(io_user_data->last_name, U.PLG$LAST_NAME);
- io_user_data->last_name_entered = !U.PLG$LAST_NAME.NULL;
+ callback->list(user);
- io_user_data->admin = 0;
- FOR (TRANSACTION_HANDLE trans REQUEST_HANDLE request2) P IN RDB$USER_PRIVILEGES
- WITH P.RDB$USER EQ U.PLG$USER_NAME
- AND P.RDB$RELATION_NAME EQ 'RDB$ADMIN'
- AND P.RDB$PRIVILEGE EQ 'M'
- io_user_data->admin = 1;
+ found = true;
END_FOR
+ ON_ERROR
+ ret = GsecMsg28; // gsec - find/display record error
+ END_ERROR;
+ }
+ break;
- display_func(callback_arg, io_user_data, !found);
-
- found = true;
- END_FOR
- ON_ERROR
- ret = GsecMsg28; // gsec - find/display record error
- END_ERROR;
+ default:
+ ret = GsecMsg16; // gsec - error in switch specifications
+ break;
}
- break;
- default:
- ret = GsecMsg16; // gsec - error in switch specifications
- break;
- }
-
- if (request)
- {
- ISC_STATUS_ARRAY s;
- if (isc_release_request(s, &request) != FB_SUCCESS)
+ if (request)
{
- if (! ret)
+ ISC_STATUS_ARRAY s;
+ if (isc_release_request(s, &request) != FB_SUCCESS)
{
- ret = GsecMsg94; // error releasing request in security database
+ if (! ret)
+ {
+ ret = GsecMsg94; // error releasing request in security database
+ }
}
}
- }
- if (request2)
- {
- ISC_STATUS_ARRAY s;
- if (isc_release_request(s, &request2) != FB_SUCCESS)
+ if (request2)
{
- if (! ret)
+ ISC_STATUS_ARRAY s;
+ if (isc_release_request(s, &request2) != FB_SUCCESS)
{
- ret = GsecMsg94; // error releasing request in security database
+ if (! ret)
+ {
+ ret = GsecMsg94; // error releasing request in security database
+ }
}
}
+
+ st->set(isc_status);
}
+ catch (const Firebird::Exception& ex)
+ {
+ ex.stuffException(st);
+ }
return ret;
}
-int Auth::SecurityDatabaseManagement::release()
-{
- if (--refCounter == 0)
- {
- delete this;
- return 0;
- }
+} // namespace Auth
- return 1;
-}
-
// register plugin
static Firebird::SimpleFactory<Auth::SecurityDatabaseManagement> factory;
Modified: firebird/trunk/src/auth/SecurityDatabase/LegacyManagement.h
===================================================================
--- firebird/trunk/src/auth/SecurityDatabase/LegacyManagement.h 2011-03-02 12:25:11 UTC (rev 52491)
+++ firebird/trunk/src/auth/SecurityDatabase/LegacyManagement.h 2011-03-02 13:42:56 UTC (rev 52492)
@@ -36,17 +36,19 @@
class SecurityDatabaseManagement : public Firebird::StdPlugin<Management, FB_AUTH_MANAGE_VERSION>
{
public:
- explicit SecurityDatabaseManagement(Firebird::IFactoryParameter*)
- {
- }
+ explicit SecurityDatabaseManagement(Firebird::IFactoryParameter* par);
- // work in progress - we must avoid both internal_user_data and callback function
- int FB_CARG execLine(ISC_STATUS* isc_status, const char* realUser,
- FB_API_HANDLE db, FB_API_HANDLE trans,
- internal_user_data* io_user_data,
- FPTR_SECURITY_CALLBACK display_func, void* callback_arg);
+ void FB_CARG start(Firebird::Status* status, LogonInfo* logonInfo);
+ int FB_CARG execute(Firebird::Status* status, User* user, ListUsers* callback);
+ void FB_CARG commit(Firebird::Status* status);
+ void FB_CARG rollback(Firebird::Status* status);
int FB_CARG release();
+
+private:
+ Firebird::RefPtr<Firebird::IFirebirdConf> config;
+ FB_API_HANDLE database, transaction;
+ Firebird::string userName;
};
} // namespace Auth
Copied: firebird/trunk/src/auth/SecurityDatabase/LegacyServer.cpp (from rev 52312, firebird/trunk/src/auth/SecurityDatabase/pwd.cpp)
===================================================================
--- firebird/trunk/src/auth/SecurityDatabase/LegacyServer.cpp (rev 0)
+++ firebird/trunk/src/auth/SecurityDatabase/LegacyServer.cpp 2011-03-02 13:42:56 UTC (rev 52492)
@@ -0,0 +1,577 @@
+/*
+ * PROGRAM: JRD Access Method
+ * MODULE: pwd.cpp
+ * DESCRIPTION: User information database access
+ *
+ * The contents of this file are subject to the Interbase Public
+ * License Version 1.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy
+ * of the License at http://www.Inprise.com/IPL.html
+ *
+ * Software distributed under the License is distributed on an
+ * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
+ * or implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code was created by Inprise Corporation
+ * and its predecessors. Portions created by Inprise Corporation are
+ * Copyright (C) Inprise Corporation.
+ *
+ * All Rights Reserved.
+ * Contributor(s): ______________________________________.
+ *
+ * 2003.02.02 Dmitry Yemanov: Implemented cached security database connection
+ */
+
+#include "firebird.h"
+#include <string.h>
+#include <stdlib.h>
+#include <time.h>
+#include "../common/common.h"
+#include "../jrd/ibase.h"
+#include "../jrd/jrd.h"
+#include "../auth/SecurityDatabase/LegacyServer.h"
+#include "../common/enc_proto.h"
+#include "../jrd/err_proto.h"
+#include "../yvalve/gds_proto.h"
+#include "../common/isc_proto.h"
+#include "../jrd/thread_proto.h"
+#include "../jrd/jrd_proto.h"
+#include "../jrd/scl.h"
+#include "../common/config/config.h"
+#include "../common/classes/objects_array.h"
+#include "../common/classes/init.h"
+#include "../common/classes/ImplementHelper.h"
+
+using namespace Firebird;
+
+namespace {
+
+// temporal implementation of timer
+
+GlobalPtr<Mutex> timerMutex;
+FPTR_VOID_PTR toRun = 0;
+unsigned int cnt = 0;
+
+int active = 0;
+
+int stopTimer(const int, const int mask, void*)
+{
+ switch(mask)
+ {
+ case fb_shut_preproviders:
+ active = 2;
+ break;
+ case fb_shut_finish:
+ while (active == 2)
+ {
+ THREAD_SLEEP(10);
+ }
+ break;
+ }
+
+ return 0;
+}
+
+THREAD_ENTRY_DECLARE threadTimer(THREAD_ENTRY_PARAM)
+{
+ while (active == 1)
+ {
+ { // scope
+ MutexLockGuard g(timerMutex);
+ if (cnt == 0)
+ {
+ if (toRun)
+ {
+ toRun(0);
+ toRun = 0;
+ }
+ }
+ else
+ {
+ --cnt;
+ }
+ }
+
+ THREAD_SLEEP(100);
+ }
+
+ active = 3;
+ return 0;
+}
+
+int fb_alloc_timer()
+{
+ if (! active)
+ {
+ active = 1;
+ Thread::start(threadTimer, 0, 0);
+ fb_shutdown_callback(0, stopTimer, fb_shut_preproviders | fb_shut_finish, 0);
+ }
+
+ return 1;
+}
+
+void fb_thread_timer(int, int delay, FPTR_VOID_PTR function, void*)
+{
+ MutexLockGuard g(timerMutex);
+
+ cnt = delay / 100;
+ if (! cnt)
+ {
+ cnt = 1;
+ }
+
+ toRun = function;
+}
+
+// BLR to search database for user name record
+
+const UCHAR PWD_REQUEST[] =
+{
+ blr_version5,
+ blr_begin,
+ blr_message, 1, 4, 0,
+ blr_long, 0,
+ blr_long, 0,
+ blr_short, 0,
+ blr_text, BLR_WORD(Auth::MAX_PASSWORD_LENGTH + 2),
+ blr_message, 0, 1, 0,
+ blr_cstring, 129, 0,
+ blr_receive, 0,
+ blr_begin,
+ blr_for,
+ blr_rse, 1,
+ blr_relation, 9, 'P', 'L', 'G', '$', 'U', 'S', 'E', 'R', 'S', 0,
+ blr_first,
+ blr_literal, blr_short, 0, 1, 0,
+ blr_boolean,
+ blr_eql,
+ blr_field, 0, 13, 'P', 'L', 'G', '$', 'U', 'S', 'E', 'R', '_', 'N', 'A', 'M', 'E',
+ blr_parameter, 0, 0, 0,
+ blr_end,
+ blr_send, 1,
+ blr_begin,
+ blr_assignment,
+ blr_field, 0, 7, 'P', 'L', 'G', '$', 'G', 'I', 'D',
+ blr_parameter, 1, 0, 0,
+ blr_assignment,
+ blr_field, 0, 7, 'P', 'L', 'G', '$', 'U', 'I', 'D',
+ blr_parameter, 1, 1, 0,
+ blr_assignment,
+ blr_literal, blr_short, 0, 1, 0,
+ blr_parameter, 1, 2, 0,
+ blr_assignment,
+ blr_field, 0, 10, 'P', 'L', 'G', '$', 'P', 'A', 'S', 'S', 'W', 'D',
+ blr_parameter, 1, 3, 0,
+ blr_end,
+ blr_send, 1,
+ blr_assignment,
+ blr_literal, blr_short, 0, 0, 0,
+ blr_parameter, 1, 2, 0,
+ blr_end,
+ blr_end,
+ blr_eoc
+};
+
+// Returns data in the following format
+
+struct user_record
+{
+ SLONG gid;
+ SLONG uid;
+ SSHORT flag;
+ SCHAR password[Auth::MAX_PASSWORD_LENGTH + 2];
+};
+
+// Transaction parameter buffer
+
+const UCHAR TPB[4] =
+{
+ isc_tpb_version1,
+ isc_tpb_read,
+ isc_tpb_concurrency,
+ isc_tpb_wait
+};
+
+int timer = 0;
+
+} // anonymous
+
+namespace Auth {
+
+/******************************************************************************
+ *
+ * Private interface
+ */
+
+void SecurityDatabase::fini()
+{
+ isc_db_handle tmp = 0;
+
+ { // scope
+ MutexLockGuard guard(mutex);
+
+ if (lookup_req)
+ {
+ isc_release_request(status, &lookup_req);
+ checkStatus("isc_release_request");
+ }
+
+ tmp = lookup_db;
+ lookup_db = 0;
+ }
+
+ if (tmp)
+ {
+ isc_detach_database(status, &tmp);
+ checkStatus("isc_detach_database");
+ }
+}
+
+void SecurityDatabase::init()
+{
+ if (! timer)
+ {
+ timer = fb_alloc_timer();
+ }
+}
+
+bool SecurityDatabase::lookup_user(const char* user_name, char* pwd)
+{
+ bool found = false; // user found flag
+ char uname[129]; // user name buffer
+ user_record user; // user record
+
+ // Start by clearing the output data
+
+ if (pwd)
+ *pwd = '\0';
+
+ strncpy(uname, user_name, sizeof uname);
+ uname[sizeof uname - 1] = 0;
+
+ MutexLockGuard guard(mutex);
+
+ // Attach database and compile request
+
+ prepare();
+
+ // Lookup
+
+ isc_tr_handle lookup_trans = 0;
+
+ isc_start_transaction(status, &lookup_trans, 1, &lookup_db, sizeof(TPB), TPB);
+ checkStatus("isc_start_transaction", isc_psw_start_trans);
+
+ isc_start_and_send(status, &lookup_req, &lookup_trans, 0, sizeof(uname), uname, 0);
+ checkStatus("isc_start_and_send");
+
+ while (true)
+ {
+ isc_receive(status, &lookup_req, 1, sizeof(user), &user, 0);
+ checkStatus("isc_receive");
+
+ if (!user.flag || status[1])
+ break;
+
+ found = true;
+
+ if (pwd)
+ {
+ strncpy(pwd, user.password, MAX_PASSWORD_LENGTH);
+ pwd[MAX_PASSWORD_LENGTH] = 0;
+ }
+ }
+
+ isc_rollback_transaction(status, &lookup_trans);
+ checkStatus("isc_rollback_transaction");
+
+ return found;
+}
+
+void SecurityDatabase::prepare()
+{
+ if (lookup_db)
+ {
+ return;
+ }
+
+ init();
+
+ lookup_db = lookup_req = 0;
+
+ // Perhaps build up a dpb
+ ClumpletWriter dpb(ClumpletReader::Tagged, MAX_DPB_SIZE, isc_dpb_version1);
+
+ // Attachment is for the security database
+ dpb.insertByte(isc_dpb_sec_attach, TRUE);
+
+ // Attach as SYSDBA
+ dpb.insertString(isc_dpb_trusted_auth, SYSDBA_USER_NAME, strlen(SYSDBA_USER_NAME));
+
+ isc_db_handle tempHandle = 0;
+ isc_attach_database(status, 0, secureDbName, &tempHandle,
+ dpb.getBufferLength(), reinterpret_cast<const char*>(dpb.getBuffer()));
+ checkStatus("isc_attach_database", isc_psw_attach);
+ lookup_db = tempHandle;
+
+ isc_compile_request(status, &lookup_db, &lookup_req, sizeof(PWD_REQUEST),
+ reinterpret_cast<const char*>(PWD_REQUEST));
+ if (status[1])
+ {
+ ISC_STATUS_ARRAY localStatus;
+ // ignore status returned in order to keep first error
+ isc_detach_database(localStatus, &lookup_db);
+ }
+
+ checkStatus("isc_compile_request", isc_psw_attach);
+}
+
+/******************************************************************************
+ *
+ * Public interface
+ */
+
+Result SecurityDatabase::verify(WriterInterface* authBlock,
+ ClumpletReader& originalDpb)
+{
+ static AmCache useNative = AM_UNKNOWN;
+
+ if (useNative == AM_UNKNOWN)
+ {
+ // We use PathName for string comparison using platform filename comparison
+ // rules (case-sensitive or case-insensitive).
+ const PathName authMethod(Config::getAuthMethod());
+ useNative = (authMethod == AmNative || authMethod == AmMixed) ? AM_ENABLED : AM_DISABLED;
+ }
+
+ if (useNative == AM_DISABLED)
+ {
+ return AUTH_CONTINUE;
+ }
+
+ string login, password, passwordEnc;
+
+ for (originalDpb.rewind(); !originalDpb.isEof(); originalDpb.moveNext())
+ {
+ switch (originalDpb.getClumpTag())
+ {
+ case isc_dpb_user_name:
+ originalDpb.getString(login);
+ break;
+ case isc_dpb_password:
+ originalDpb.getString(password);
+ break;
+ case isc_dpb_password_enc:
+ originalDpb.getString(passwordEnc);
+ break;
+ }
+ }
+
+ if (login.hasData() && (password.hasData() || passwordEnc.hasData()))
+ {
+ login.upper();
+
+ // Look up the user name in the userinfo database and use the parameters
+ // found there. This means that another database must be accessed, and
+ // that means the current context must be saved and restored.
+
+ char pw1[MAX_PASSWORD_LENGTH + 1];
+ if (!lookup_user(login.c_str(), pw1))
+ {
+ return AUTH_FAILED;
+ }
+ pw1[MAX_PASSWORD_LENGTH] = 0;
+ string storedHash(pw1, MAX_PASSWORD_LENGTH);
+ storedHash.rtrim();
+
+ if (!passwordEnc.hasData())
+ {
+ char pwt[MAX_PASSWORD_LENGTH + 2];
+ ENC_crypt(pwt, sizeof pwt, password.c_str(), PASSWORD_SALT);
+ passwordEnc.assign(&pwt[2]);
+ }
+
+ string newHash;
+ hash(newHash, login, passwordEnc, storedHash);
+ if (newHash != storedHash)
+ {
+ bool legacyHash = Config::getLegacyHash();
+ if (legacyHash)
+ {
+ newHash.resize(MAX_PASSWORD_LENGTH + 2);
+ ENC_crypt(newHash.begin(), newHash.length(), passwordEnc.c_str(), PASSWORD_SALT);
+ newHash.recalculate_length();
+ newHash.erase(0, 2);
+ legacyHash = newHash == storedHash;
+ }
+ if (!legacyHash)
+ {
+ return AUTH_FAILED;
+ }
+ }
+
+ authBlock->add(login.c_str(), "SecDB", secureDbName);
+ return AUTH_SUCCESS;
+ }
+
+ return AUTH_CONTINUE;
+}
+
+void SecurityDatabase::checkStatus(const char* callName, ISC_STATUS userError)
+{
+ if (status[1] == 0)
+ {
+ return;
+ }
+
+#ifdef DEV_BUILD
+ // throw original status error
+ status_exception::raise(status);
+#else
+ string message;
+ message.printf("Error in %s() API call when working with security database", callName);
+ iscLogStatus(message.c_str(), status);
+
+ // showing real problems with security database to users is not good idea
+ // from security POV - therefore some generic message is used
+ Arg::Gds(userError).raise();
+#endif
+}
+
+// TODO - avoid races between timer thread and auth thread
+// TODO - account for too old instances and cleanup them
+// WHEN - when timer interface is ready
+typedef HalfStaticArray<SecurityDatabase*, 4> InstancesArray;
+GlobalPtr<InstancesArray> instances;
+GlobalPtr<Mutex> instancesMutex;
+
+void SecurityDatabase::shutdown(void*)
+{
+ try
+ {
+ MutexLockGuard g(instancesMutex);
+ InstancesArray& curInstances(instances);
+ for (unsigned int i = 0; i < curInstances.getCount(); ++i)
+ {
+ if (curInstances[i])
+ {
+ curInstances[i]->fini();
+ delete curInstances[i];
+ curInstances[i] = NULL;
+ }
+ }
+ curInstances.clear();
+ }
+ catch (Exception &ex)
+ {
+ ISC_STATUS_ARRAY status;
+ ex.stuff_exception(status);
+ if (status[0] == 1 && status[1] != isc_att_shutdown)
+ {
+ iscLogStatus("Legacy security database shutdown", status);
+ }
+ }
+}
+
+const static unsigned int INIT_KEY = ((~0) - 1);
+static unsigned int secDbKey = INIT_KEY;
+
+Result SecurityDatabaseServer::startAuthentication(Firebird::Status* status,
+ bool isService, const char*,
+ const unsigned char* dpb, unsigned int dpbSize,
+ WriterInterface* writerInterface)
+{
+ status->init();
+
+ try
+ {
+ PathName secDbName;
+ { // config scope
+ RefPtr<IFirebirdConf> config(iParameter->getFirebirdConf());
+ config->release();
+
+ if (secDbKey == INIT_KEY)
+ {
+ secDbKey = config->getKey("SecurityDatabase");
+ }
+ const char* tmp = config->asString(secDbKey);
+ if (!tmp)
+ {
+ (Arg::Gds(isc_random) << "Error getting security database name").raise();
+ }
+
+ secDbName = tmp;
+ }
+
+ SecurityDatabase* instance = 0;
+
+ fb_thread_timer(timer, 10000, SecurityDatabase::shutdown, 0);
+ { // guard scope
+ MutexLockGuard g(instancesMutex);
+ InstancesArray& curInstances(instances);
+ for (unsigned int i = 0; i < curInstances.getCount(); ++i)
+ {
+ if (secDbName == curInstances[i]->secureDbName)
+ {
+ instance = curInstances[i];
+ break;
+ }
+ }
+
+ if (!instance)
+ {
+ instance = new SecurityDatabase;
+ secDbName.copyTo(instance->secureDbName, sizeof(instance->secureDbName));
+ curInstances.add(instance);
+ }
+ }
+
+ fb_assert(instance);
+
+ ClumpletReader rdr(isService ? ClumpletReader::spbList : ClumpletReader::dpbList, dpb, dpbSize);
+ return instance->verify(writerInterface, rdr);
+ }
+ catch (const Firebird::Exception& ex)
+ {
+ ex.stuffException(status);
+ return AUTH_FAILED;
+ }
+}
+
+Result SecurityDatabaseServer::contAuthentication(Firebird::Status*,
+ WriterInterface* /*writerInterface*/,
+ const unsigned char* /*data*/, unsigned int /*size*/)
+{
+ return AUTH_FAILED;
+}
+
+void SecurityDatabaseServer::getData(const unsigned char** data, unsigned short* dataSize)
+{
+ *data = NULL;
+ *dataSize = 0;
+}
+
+int SecurityDatabaseServer::release()
+{
+ if (--refCounter == 0)
+ {
+ delete this;
+ return 0;
+ }
+
+ return 1;
+}
+
+namespace {
+ Firebird::SimpleFactory<SecurityDatabaseServer> factory;
+}
+
+void registerLegacyServer(Firebird::IPlugin* iPlugin)
+{
+ factory->addRef();
+ iPlugin->registerPlugin(Firebird::PluginType::AuthServer, "Legacy_Auth", &factory);
+}
+
+} // namespace Auth
Property changes on: firebird/trunk/src/auth/SecurityDatabase/LegacyServer.cpp
___________________________________________________________________
Added: svn:mime-type
+ text/plain
Added: svn:eol-style
+ native
Copied: firebird/trunk/src/auth/SecurityDatabase/LegacyServer.h (from rev 52312, firebird/trunk/src/auth/SecurityDatabase/jrd_pwd.h)
===================================================================
--- firebird/trunk/src/auth/SecurityDatabase/LegacyServer.h (rev 0)
+++ firebird/trunk/src/auth/SecurityDatabase/LegacyServer.h 2011-03-02 13:42:56 UTC (rev 52492)
@@ -0,0 +1,130 @@
+/*
+ * PROGRAM: JRD Access Method
+ * MODULE: jrd_pwd.h
+ * DESCRIPTION: User information database name
+ *
+ * The contents of this file are subject to the Interbase Public
+ * License Version 1.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy
+ * of the License at http://www.Inprise.com/IPL.html
+ *
+ * Software distributed under the License is distributed on an
+ * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
+ * or implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code was created by Inprise Corporation
+ * and its predecessors. Portions created by Inprise Corporation are
+ * Copyright (C) Inprise Corporation.
+ *
+ * All Rights Reserved.
+ * Contributor(s): ______________________________________.
+ *
+ * 2002.10.29 Sean Leyne - Removed obsolete "Netware" port
+ * 2003.02.02 Dmitry Yemanov: Implemented cached security database connection
+ */
+
+#ifndef AUTH_LEGACY_SERVER_H
+#define AUTH_LEGACY_SERVER_H
+
+#include "../jrd/ibase.h"
+#include "../common/utils_proto.h"
+#include "../common/sha.h"
+#include "gen/iberror.h"
+#include "../common/classes/ClumpletWriter.h"
+#include "../common/classes/ImplementHelper.h"
+
+#include "../auth/AuthInterface.h"
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#include <time.h>
+
+namespace Auth {
+
+const size_t MAX_PASSWORD_LENGTH = 64; // used to store passwords internally
+static const char* const PASSWORD_SALT = "9z"; // for old ENC_crypt()
+const size_t SALT_LENGTH = 12; // measured after base64 coding
+
+class SecurityDatabase : public Firebird::GlobalStorage
+{
+public:
+ Result verify(WriterInterface* authBlock,
+ Firebird::ClumpletReader& originalDpb);
+
+ static void shutdown(void*);
+
+ static void hash(Firebird::string& h, const Firebird::string& userName, const TEXT* passwd)
+ {
+ Firebird::string salt;
+ Jrd::CryptSupport::random(salt, SALT_LENGTH);
+ hash(h, userName, passwd, salt);
+ }
+
+ static void hash(Firebird::string& h,
+ const Firebird::string& userName,
+ const Firebird::string& passwd,
+ const Firebird::string& oldHash)
+ {
+ Firebird::string salt(oldHash);
+ salt.resize(SALT_LENGTH, '=');
+ Firebird::string allData(salt);
+ allData += userName;
+ allData += passwd;
+ Jrd::CryptSupport::hash(h, allData);
+ h = salt + h;
+ }
+
+ char secureDbName[MAXPATHLEN];
+
+ SecurityDatabase()
+ : lookup_db(0), lookup_req(0)
+ {
+ }
+
+private:
+ Firebird::Mutex mutex;
+
+ ISC_STATUS_ARRAY status;
+
+ isc_db_handle lookup_db;
+ isc_req_handle lookup_req;
+
+ void init();
+ void fini();
+ bool lookup_user(const char*, char*);
+ void prepare();
+ void checkStatus(const char* callName, ISC_STATUS userError = isc_psw_db_error);
+};
+
+class SecurityDatabaseServerFactory : public Firebird::StdIface<Firebird::PluginsFactory, FB_PLUGINS_FACTORY_VERSION>
+{
+public:
+ Firebird::Plugin* FB_CARG createPlugin(const char* name, const char* configFile);
+};
+
+class SecurityDatabaseServer : public Firebird::StdPlugin<Server, FB_AUTH_SERVER_VERSION>
+{
+public:
+ explicit SecurityDatabaseServer(Firebird::IFactoryParameter* p)
+ : iParameter(p)
+ { }
+
+ Result FB_CARG startAuthentication(Firebird::Status* status, bool isService, const char* dbName,
+ const unsigned char* dpb, unsigned int dpbSize,
+ WriterInterface* writerInterface);
+ Result FB_CARG contAuthentication(Firebird::Status* status, WriterInterface* writerInterface,
+ const unsigned char* data, unsigned int size);
+ void FB_CARG getData(const unsigned char** data, unsigned short* dataSize);
+ int FB_CARG release();
+
+private:
+ Firebird::RefPtr<Firebird::IFactoryParameter> iParameter;
+};
+
+void registerLegacyServer(Firebird::IPlugin* iPlugin);
+
+} // namespace Auth
+
+#endif // AUTH_LEGACY_SERVER_H
Property changes on: firebird/trunk/src/auth/SecurityDatabase/LegacyServer.h
___________________________________________________________________
Added: svn:mime-type
+ text/plain
Added: svn:eol-style
+ native
Deleted: firebird/trunk/src/auth/SecurityDatabase/jrd_pwd.h
===================================================================
--- firebird/trunk/src/auth/SecurityDatabase/jrd_pwd.h 2011-03-02 12:25:11 UTC (rev 52491)
+++ firebird/trunk/src/auth/SecurityDatabase/jrd_pwd.h 2011-03-02 13:42:56 UTC (rev 52492)
@@ -1,138 +0,0 @@
-/*
- * PROGRAM: JRD Access Method
- * MODULE: jrd_pwd.h
- * DESCRIPTION: User information database name
- *
- * The contents of this file are subject to the Interbase Public
- * License Version 1.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy
- * of the License at http://www.Inprise.com/IPL.html
- *
- * Software distributed under the License is distributed on an
- * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
- * or implied. See the License for the specific language governing
- * rights and limitations under the License.
- *
- * The Original Code was created by Inprise Corporation
- * and its predecessors. Portions created by Inprise Corporation are
- * Copyright (C) Inprise Corporation.
- *
- * All Rights Reserved.
- * Contributor(s): ______________________________________.
- *
- * 2002.10.29 Sean Leyne - Removed obsolete "Netware" port
- * 2003.02.02 Dmitry Yemanov: Implemented cached security database connection
- */
-
-#ifndef JRD_PWD_H
-#define JRD_PWD_H
-
-#include "../jrd/ibase.h"
-#include "../common/utils_proto.h"
-#include "../common/sha.h"
-#include "gen/iberror.h"
-#include "../common/classes/ClumpletWriter.h"
-#include "../common/classes/ImplementHelper.h"
-
-#include "../auth/AuthInterface.h"
-
-#ifdef HAVE_STDLIB_H
-#include <stdlib.h>
-#endif
-#include <time.h>
-
-namespace Auth {
-
-const size_t MAX_PASSWORD_ENC_LENGTH = 12; // passed by remote protocol
-const size_t MAX_PASSWORD_LENGTH = 64; // used to store passwords internally
-static const char* const PASSWORD_SALT = "9z"; // for old ENC_crypt()
-const size_t SALT_LENGTH = 12; // measured after base64 coding
-
-class SecurityDatabase
-{
-public:
- static void getPath(char* path_buffer)
- {
- static const char* USER_INFO_NAME = "security3.fdb";
- Firebird::PathName name = fb_utils::getPrefix(fb_utils::FB_DIR_SECDB, USER_INFO_NAME);
- name.copyTo(path_buffer, MAXPATHLEN);
- }
-
- static Result verify(WriterInterface* authBlock,
- Firebird::ClumpletReader& originalDpb);
-
- static void shutdown(void*);
-
- static void hash(Firebird::string& h, const Firebird::string& userName, const TEXT* passwd)
- {
- Firebird::string salt;
- Jrd::CryptSupport::random(salt, SALT_LENGTH);
- hash(h, userName, passwd, salt);
- }
-
- static void hash(Firebird::string& h,
- const Firebird::string& userName,
- const Firebird::string& passwd,
- const Firebird::string& oldHash)
- {
- Firebird::string salt(oldHash);
- salt.resize(SALT_LENGTH, '=');
- Firebird::string allData(salt);
- allData += userName;
- allData += passwd;
- Jrd::CryptSupport::hash(h, allData);
- h = salt + h;
- }
-
-private:
- Firebird::Mutex mutex;
-
- ISC_STATUS_ARRAY status;
-
- isc_db_handle lookup_db;
- isc_req_handle lookup_req;
-
- int timer;
- char user_info_name[MAXPATHLEN];
-
- void init();
- void fini();
- bool lookup_user(const char*, char*);
- void prepare();
- void checkStatus(const char* callName, ISC_STATUS userError = isc_psw_db_error);
-
- static SecurityDatabase instance;
-
- SecurityDatabase()
- : lookup_db(0), lookup_req(0), timer(0)
- {
- }
-};
-
-class SecurityDatabaseServerFactory : public Firebird::StdIface<Firebird::PluginsFactory, FB_PLUGINS_FACTORY_VERSION>
-{
-public:
- Firebird::Plugin* FB_CARG createPlugin(const char* name, const char* configFile);
-};
-
-class SecurityDatabaseServer : public Firebird::StdPlugin<Server, FB_AUTH_SERVER_VERSION>
-{
-public:
- explicit SecurityDatabaseServer(Firebird::IFactoryParameter*)
- {
- }
-
- Result FB_CARG startAuthentication(Firebird::Status* status, bool isService, const char* dbName,
- const unsigned char* dpb, unsigned int dpbSize,
- WriterInterface* writerInterface);
- Result FB_CARG contAuthentication(Firebird::Status* status, WriterInterface* writerInterface,
- const unsigned char* data, unsigned int size);
- void FB_CARG getData(const unsigned char** data, unsigned short* dataSize);
- int FB_CARG release();
-};
-
-void registerLegacyServer(Firebird::IPlugin* iPlugin);
-
-} // namespace Auth
-
-#endif // JRD_PWD_H
Deleted: firebird/trunk/src/auth/SecurityDatabase/pwd.cpp
===================================================================
--- firebird/trunk/src/auth/SecurityDatabase/pwd.cpp 2011-03-02 12:25:11 UTC (rev 52491)
+++ firebird/trunk/src/auth/SecurityDatabase/pwd.cpp 2011-03-02 13:42:56 UTC (rev 52492)
@@ -1,516 +0,0 @@
-/*
- * PROGRAM: JRD Access Method
- * MODULE: pwd.cpp
- * DESCRIPTION: User information database access
- *
- * The contents of this file are subject to the Interbase Public
- * License Version 1.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy
- * of the License at http://www.Inprise.com/IPL.html
- *
- * Software distributed under the License is distributed on an
- * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
- * or implied. See the License for the specific language governing
- * rights and limitations under the License.
- *
- * The Original Code was created by Inprise Corporation
- * and its predecessors. Portions created by Inprise Corporation are
- * Copyright (C) Inprise Corporation.
- *
- * All Rights Reserved.
- * Contributor(s): ______________________________________.
- *
- * 2003.02.02 Dmitry Yemanov: Implemented cached security database connection
- */
-
-#include "firebird.h"
-#include <string.h>
-#include <stdlib.h>
-#include <time.h>
-#include "../common/common.h"
-#include "../jrd/ibase.h"
-#include "../jrd/jrd.h"
-#include "../auth/SecurityDatabase/jrd_pwd.h"
-#include "../common/enc_proto.h"
-#include "../jrd/err_proto.h"
-#include "../yvalve/gds_proto.h"
-#include "../common/isc_proto.h"
-#include "../jrd/thread_proto.h"
-#include "../jrd/jrd_proto.h"
-#include "../jrd/scl.h"
-#include "../common/config/config.h"
-#include "../common/classes/objects_array.h"
-#include "../common/classes/init.h"
-#include "../common/classes/ImplementHelper.h"
-
-using namespace Firebird;
-
-namespace {
-
-// temporal implementation of timer
-
-GlobalPtr<Mutex> timerMutex;
-FPTR_VOID_PTR toRun = 0;
-unsigned int cnt = 0;
-
-int active = 0;
-
-int stopTimer(const int, const int mask, void*)
-{
- switch(mask)
- {
- case fb_shut_preproviders:
- active = 2;
- break;
- case fb_shut_finish:
- while (active == 2)
- {
- THREAD_SLEEP(10);
- }
- break;
- }
-
...
[truncated message content] |