8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-31 11:23:02 +01:00
firebird-mirror/src/jrd/pwd.cpp

474 lines
10 KiB
C++
Raw Normal View History

2001-05-23 15:26:42 +02:00
/*
* PROGRAM: JRD Access Method
2003-12-31 06:36:12 +01:00
* MODULE: pwd.cpp
2001-05-23 15:26:42 +02:00
* 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
2001-05-23 15:26:42 +02:00
*/
#include "firebird.h"
2001-05-23 15:26:42 +02:00
#include <string.h>
#include <stdlib.h>
#include <time.h>
2004-03-22 12:38:23 +01:00
#include "../jrd/common.h"
2003-11-08 17:40:17 +01:00
#include "../jrd/ibase.h"
2001-05-23 15:26:42 +02:00
#include "../jrd/jrd.h"
#include "../jrd/jrd_pwd.h"
2001-05-23 15:26:42 +02:00
#include "../jrd/enc_proto.h"
#include "../jrd/err_proto.h"
#include "../jrd/gds_proto.h"
#include "../jrd/isc_proto.h"
#include "../jrd/thread_proto.h"
2004-05-03 01:06:37 +02:00
#include "../jrd/jrd_proto.h"
2008-01-16 10:41:31 +01:00
#include "../jrd/scl.h"
#include "../common/config/config.h"
#include "../common/classes/objects_array.h"
#include "../common/classes/init.h"
2001-05-23 15:26:42 +02:00
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)
{
2010-01-24 17:52:24 +01:00
while (active == 1)
{
2010-01-24 17:52:24 +01:00
{ // 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;
gds__thread_start(threadTimer, 0, 0, 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;
}
2003-10-05 12:27:08 +02:00
// BLR to search database for user name record
2001-05-23 15:26:42 +02:00
const UCHAR PWD_REQUEST[] =
2008-12-22 10:00:05 +01:00
{
2001-05-23 15:26:42 +02:00
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),
2001-05-23 15:26:42 +02:00
blr_message, 0, 1, 0,
blr_cstring, 129, 0,
blr_receive, 0,
blr_begin,
blr_for,
blr_rse, 1,
blr_relation, 9, 'R', 'D', 'B', '$', 'U', 'S', 'E', 'R', 'S', 0,
2001-05-23 15:26:42 +02:00
blr_first,
blr_literal, blr_short, 0, 1, 0,
blr_boolean,
blr_eql,
2005-10-24 14:30:57 +02:00
blr_field, 0, 13, 'R', 'D', 'B', '$', 'U', 'S', 'E', 'R', '_', 'N', 'A', 'M', 'E',
2001-05-23 15:26:42 +02:00
blr_parameter, 0, 0, 0,
blr_end,
blr_send, 1,
blr_begin,
blr_assignment,
2005-10-24 14:30:57 +02:00
blr_field, 0, 7, 'R', 'D', 'B', '$', 'G', 'I', 'D',
2001-05-23 15:26:42 +02:00
blr_parameter, 1, 0, 0,
blr_assignment,
2005-10-24 14:30:57 +02:00
blr_field, 0, 7, 'R', 'D', 'B', '$', 'U', 'I', 'D',
2001-05-23 15:26:42 +02:00
blr_parameter, 1, 1, 0,
blr_assignment,
blr_literal, blr_short, 0, 1, 0,
blr_parameter, 1, 2, 0,
blr_assignment,
2005-10-24 14:30:57 +02:00
blr_field, 0, 10, 'R', 'D', 'B', '$', 'P', 'A', 'S', 'S', 'W', 'D',
2001-05-23 15:26:42 +02:00
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];
};
2003-10-05 12:27:08 +02:00
// Transaction parameter buffer
2001-05-23 15:26:42 +02:00
const UCHAR TPB[4] =
2008-12-22 10:00:05 +01:00
{
2001-05-23 15:26:42 +02:00
isc_tpb_version1,
isc_tpb_read,
2001-05-23 15:26:42 +02:00
isc_tpb_concurrency,
isc_tpb_wait
};
} // anonymous
namespace Auth {
// Static instance of the database
SecurityDatabase SecurityDatabase::instance;
2001-05-23 15:26:42 +02:00
/******************************************************************************
2001-05-23 15:26:42 +02:00
*
* Private interface
*/
2001-05-23 15:26:42 +02:00
void SecurityDatabase::fini()
{
MutexLockGuard guard(mutex);
if (lookup_req)
2001-05-23 15:26:42 +02:00
{
isc_release_request(status, &lookup_req);
checkStatus("isc_release_request");
}
if (lookup_db)
{
isc_detach_database(status, &lookup_db);
checkStatus("isc_detach_database");
2001-05-23 15:26:42 +02:00
}
}
void SecurityDatabase::init()
{
if (! timer)
{
timer = fb_alloc_timer();
getPath(user_info_name);
}
}
2001-05-23 15:26:42 +02:00
bool SecurityDatabase::lookup_user(const char* user_name, char* pwd)
2001-05-23 15:26:42 +02:00
{
2003-10-03 14:28:54 +02:00
bool found = false; // user found flag
char uname[129]; // user name buffer
user_record user; // user record
2001-05-23 15:26:42 +02:00
2003-10-05 12:27:08 +02:00
// Start by clearing the output data
2001-05-23 15:26:42 +02:00
if (pwd)
*pwd = '\0';
strncpy(uname, user_name, sizeof uname);
uname[sizeof uname - 1] = 0;
2001-05-23 15:26:42 +02:00
MutexLockGuard guard(mutex);
2005-04-01 12:26:22 +02:00
2003-10-05 12:27:08 +02:00
// Attach database and compile request
2001-05-23 15:26:42 +02:00
prepare();
2001-05-23 15:26:42 +02:00
2003-10-05 12:27:08 +02:00
// Lookup
isc_tr_handle lookup_trans = 0;
2001-05-23 15:26:42 +02:00
isc_start_transaction(status, &lookup_trans, 1, &lookup_db, sizeof(TPB), TPB);
checkStatus("isc_start_transaction", isc_psw_start_trans);
2001-05-23 15:26:42 +02:00
isc_start_and_send(status, &lookup_req, &lookup_trans, 0, sizeof(uname), uname, 0);
checkStatus("isc_start_and_send");
2008-12-05 02:20:14 +01:00
while (true)
2001-05-23 15:26:42 +02:00
{
isc_receive(status, &lookup_req, 1, sizeof(user), &user, 0);
checkStatus("isc_receive");
if (!user.flag || status[1])
break;
found = true;
2008-12-05 02:20:14 +01:00
if (pwd)
2001-05-23 15:26:42 +02:00
{
strncpy(pwd, user.password, MAX_PASSWORD_LENGTH);
pwd[MAX_PASSWORD_LENGTH] = 0;
2001-05-23 15:26:42 +02:00
}
}
isc_rollback_transaction(status, &lookup_trans);
checkStatus("isc_rollback_transaction");
2001-05-23 15:26:42 +02:00
fb_thread_timer(timer, 10000, Shutdown, 0);
2003-10-03 14:28:54 +02:00
return found;
2001-05-23 15:26:42 +02:00
}
void SecurityDatabase::prepare()
2001-05-23 15:26:42 +02:00
{
2003-10-03 14:28:54 +02:00
if (lookup_db)
{
return;
}
init();
2001-05-23 15:26:42 +02:00
lookup_db = lookup_req = 0;
2001-05-23 15:26:42 +02:00
2003-10-05 12:27:08 +02:00
// Perhaps build up a dpb
ClumpletWriter dpb(ClumpletReader::Tagged, MAX_DPB_SIZE, isc_dpb_version1);
2001-05-23 15:26:42 +02:00
// Attachment is for the security database
dpb.insertByte(isc_dpb_sec_attach, TRUE);
2001-05-23 15:26:42 +02:00
2008-01-16 10:41:31 +01:00
// Attach as SYSDBA
dpb.insertString(isc_dpb_trusted_auth, SYSDBA_USER_NAME, strlen(SYSDBA_USER_NAME));
2001-05-23 15:26:42 +02:00
isc_db_handle tempHandle = 0;
isc_attach_database(status, 0, user_info_name, &tempHandle,
dpb.getBufferLength(), reinterpret_cast<const char*>(dpb.getBuffer()));
checkStatus("isc_attach_database", isc_psw_attach);
lookup_db = tempHandle;
2005-05-24 05:54:09 +02:00
isc_compile_request(status, &lookup_db, &lookup_req, sizeof(PWD_REQUEST),
reinterpret_cast<const char*>(PWD_REQUEST));
2001-05-23 15:26:42 +02:00
if (status[1])
{
ISC_STATUS_ARRAY localStatus;
// ignore status returned in order to keep first error
isc_detach_database(localStatus, &lookup_db);
2001-05-23 15:26:42 +02:00
}
checkStatus("isc_compile_request", isc_psw_attach);
2001-05-23 15:26:42 +02:00
}
/******************************************************************************
*
* Public interface
*/
2010-01-24 16:18:43 +01:00
Result SecurityDatabase::verify(WriterInterface* authBlock,
ClumpletReader& originalDpb)
{
static AmCache useNative = AM_UNKNOWN;
if (useNative == AM_UNKNOWN)
{
2008-07-08 08:45:14 +02:00
// We use PathName for string comparison using platform filename comparison
2008-07-06 20:08:23 +02:00
// rules (case-sensitive or case-insensitive).
2008-07-06 12:59:03 +02:00
const PathName authMethod(Config::getAuthMethod());
2008-12-22 10:00:05 +01:00
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];
const bool found = instance.lookup_user(login.c_str(), pw1);
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", instance.user_info_name);
return AUTH_SUCCESS;
}
2003-10-05 12:27:08 +02:00
return AUTH_CONTINUE;
}
void SecurityDatabase::checkStatus(const char* callName, ISC_STATUS userError)
{
if (status[1] == 0)
{
return;
}
string message;
2008-09-06 20:42:55 +02:00
message.printf("Error in %s() API call when working with security database", callName);
iscLogStatus(message.c_str(), status);
#ifdef DEV_BUILD
// throw original status error
status_exception::raise(status);
#else
// 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
}
void SecurityDatabase::Shutdown(void*)
{
instance.fini();
}
ServerInstance* SecurityDatabaseServer::instance()
{
return interfaceAlloc<SecurityDatabaseServerInstance>();
}
void SecurityDatabaseServer::getName(unsigned char** data, unsigned short* dataSize)
{
const char* name = "LEGACY_AUTH";
2010-01-24 17:52:24 +01:00
*data = (unsigned char*) name;
*dataSize = strlen(name);
}
void SecurityDatabaseServer::release()
2008-01-16 10:41:31 +01:00
{
gds__free(this);
2008-01-16 10:41:31 +01:00
}
2010-01-24 16:18:43 +01:00
Result SecurityDatabaseServerInstance::startAuthentication(bool isService, const char*,
const unsigned char* dpb, unsigned int dpbSize,
WriterInterface* writerInterface)
2008-01-16 10:41:31 +01:00
{
ClumpletReader rdr(isService ? ClumpletReader::spbList : ClumpletReader::dpbList, dpb, dpbSize);
return SecurityDatabase::verify(writerInterface, rdr);
}
2008-01-16 10:41:31 +01:00
2010-01-24 16:18:43 +01:00
Result SecurityDatabaseServerInstance::contAuthentication(WriterInterface* writerInterface,
const unsigned char* data, unsigned int size)
{
return AUTH_FAILED;
2008-01-16 10:41:31 +01:00
}
void SecurityDatabaseServerInstance::getData(unsigned char** data, unsigned short* dataSize)
2008-01-16 10:41:31 +01:00
{
*data = NULL;
*dataSize = 0;
2008-01-16 10:41:31 +01:00
}
void SecurityDatabaseServerInstance::release()
{
gds__free(this);
}
} // namespace Auth