diff --git a/builds/posix/Makefile.in.examples b/builds/posix/Makefile.in.examples index 71cb426a5c..590e583914 100644 --- a/builds/posix/Makefile.in.examples +++ b/builds/posix/Makefile.in.examples @@ -98,6 +98,11 @@ $(FIREBIRD)/examples/README: $(CP) $(ROOT)/examples/*.* $(FIREBIRD)/examples/ $(CP) $(ROOT)/examples/api/*.* $(FIREBIRD)/examples/api/ $(CP) $(ROOT)/examples/dbcrypt/*.* $(FIREBIRD)/examples/dbcrypt/ + $(CP) $(ROOT)/examples/extauth/* $(FIREBIRD)/examples/extauth/ +ifeq ($(TOMCRYPT_BUILD_FLG),Y) + mkdir -p $(FIREBIRD)/examples/extauth/tomcrypt.include + $(CP) $(ROOT)/extern/libtomcrypt/src/headers/* $(FIREBIRD)/examples/extauth/tomcrypt.include +endif $(CP) $(ROOT)/examples/include/*.* $(FIREBIRD)/examples/include/ $(CP) $(ROOT)/examples/interfaces/*.* $(FIREBIRD)/examples/interfaces/ $(CP) $(ROOT)/examples/package/*.* $(FIREBIRD)/examples/package/ diff --git a/configure.ac b/configure.ac index c0e1398f62..1193e3550e 100644 --- a/configure.ac +++ b/configure.ac @@ -1216,6 +1216,7 @@ dnl # output mkdir -p gen/\$fb_tgt/firebird/examples/api mkdir -p gen/\$fb_tgt/firebird/examples/dbcrypt mkdir -p gen/\$fb_tgt/firebird/examples/empbuild + mkdir -p gen/\$fb_tgt/firebird/examples/extauth mkdir -p gen/\$fb_tgt/firebird/examples/include mkdir -p gen/\$fb_tgt/firebird/examples/interfaces mkdir -p gen/\$fb_tgt/firebird/examples/package diff --git a/examples/extauth/ExtAuth.cpp b/examples/extauth/ExtAuth.cpp new file mode 100644 index 0000000000..3e1ea6335d --- /dev/null +++ b/examples/extauth/ExtAuth.cpp @@ -0,0 +1,452 @@ +/* + * Simple shared-key based authentication plugin + * Each node (firebird server) contains same key which is used to authenticate cross-server + * connections. Each connection coming from one node to another has on target same + * login as it was on source node. + * + * The contents of this file are subject to the Initial + * Developer's Public License Version 1.0 (the "License"); + * you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * https://www.firebirdsql.org/en/initial-developer-s-public-license-version-1-0/ + * + * Software distributed under the License is distributed AS IS, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. + * See the License for the specific language governing rights + * and limitations under the License. + * + * The Original Code was created by Alexander Peshkoff + * for the Firebird Open Source RDBMS project. + * + * Copyright (c) 2020 Alexander Peshkoff + * and all contributors signed below. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + */ + +#include +#include + +#include "TcWrapper.h" + +#define HANDSHAKE_DEBUG(A) + +const unsigned LOGINSIZE = 128u; +const unsigned RANDSIZE = 32u; +const unsigned SALTLEN = 8u; + +typedef unsigned int ULong; + +using namespace std; + +namespace { + +IMaster* master = NULL; + +class PluginModule : public IPluginModuleImpl +{ +public: + PluginModule() + : pluginManager(NULL) + { } + + ~PluginModule() + { + if (pluginManager) + { + pluginManager->unregisterModule(this); + doClean(); + } + } + + void registerMe(IPluginManager* m) + { + pluginManager = m; + pluginManager->registerModule(this); + } + + void doClean() + { + pluginManager = NULL; + } + + void threadDetach() + { } + +private: + IPluginManager* pluginManager; +}; + + +template +class Factory : public IPluginFactoryImpl, ThrowStatusWrapper> +{ +public: + // IPluginFactory implementation + IPluginBase* createPlugin(ThrowStatusWrapper* status, IPluginConfig* factoryParameter) + { + IPluginBase* p = new P(status, factoryParameter); + p->addRef(); + return p; + } +}; + +// +// Common RSA helper +// + +class PluginData +{ +public: + PluginData(ThrowStatusWrapper* status, IPluginConfig* cnf) + : refCounter(0), owner(NULL), iniLvl(0) + { + hash.init(status); + iniLvl = 1; + pseudoRand.init(status); + iniLvl = 2; + + AutoRelease conf(cnf->getDefaultConfig(status)); + if (!conf) + return; + AutoRelease ce(conf->find(status, "Key")); + if (!ce) + return; + + // import a key + unsigned char key[4096]; + unsigned keySize = readHexKey(status, ce->getValue(), key, sizeof(key)); + check(status, rsa_import(key, keySize, &privateKey), + "ExtAuth plugin failed to initialize - error importing private RSA key"); + iniLvl = 3; + } + + ~PluginData() + { + if (iniLvl >= 3) + rsa_free(&privateKey); + if (iniLvl >= 2) + pseudoRand.fini(); + if (iniLvl >= 1) + hash.fini(); + } + +protected: + atomic refCounter; + IReferenceCounted* owner; + + PseudoRandom pseudoRand; + HashSha256 hash; + rsa_key privateKey; + int iniLvl; +}; + + +// +// Client plugin +// + +class ExtAuthClient : public IClientImpl, public PluginData +{ +public: + ExtAuthClient(ThrowStatusWrapper* status, IPluginConfig* cnf) + : PluginData(status, cnf), + ignorePassword(false), + ignoreLogin(false) + { + AutoRelease conf(cnf->getDefaultConfig(status)); + if (conf) + { + AutoRelease igPass(conf->find(status, "IgnorePassword")); + if (igPass) + ignorePassword = igPass->getBoolValue(); + AutoRelease igLgn(conf->find(status, "IgnoreLogin")); + if (igLgn) + ignoreLogin = igLgn->getBoolValue(); + } + } + + // IClient implementation + int authenticate(ThrowStatusWrapper* status, IClientBlock* cBlock); + + int release() + { + if (--refCounter == 0) + { + delete this; + return 0; + } + return 1; + } + + void addRef() + { + ++refCounter; + } + + void setOwner(IReferenceCounted* o) + { + owner = o; + } + + IReferenceCounted* getOwner() + { + return owner; + } + +private: + bool ignorePassword, ignoreLogin; +}; + +int ExtAuthClient::authenticate(ThrowStatusWrapper* status, IClientBlock* cBlock) +{ + try + { + // did we initialize correctly? + if (iniLvl < 3) + return AUTH_CONTINUE; + + // check for missing login from the user + if ((!ignoreLogin) && cBlock->getLogin()) + return AUTH_CONTINUE; + + // check for missing password from the user + if ((!ignorePassword) && cBlock->getPassword()) + return AUTH_CONTINUE; + + // check for presence of authenticatiion block + IAuthBlock* authBlock = cBlock->getAuthBlock(status); + if (!authBlock) + return AUTH_CONTINUE; + if (!authBlock->first(status)) + return AUTH_CONTINUE; + + // and for presence of user name in that authenticatiion block + const char* login = NULL; + do + { + const char* type = authBlock->getType(); + if (type && (strcmp(type, "USER") == 0)) + { + login = authBlock->getName(); + if (login) + break; + } + } while(authBlock->next(status)); + if (!login) + return AUTH_CONTINUE; + + // check if server started to talk to us + unsigned dl = 0; + const unsigned char* data = cBlock->getData(&dl); + if (dl == 0 || !data) + return AUTH_MORE_DATA; + + // decrypt message + unsigned char bytes[RANDSIZE + LOGINSIZE + 1]; + unsigned long outlen = RANDSIZE; + int result = 0; + check(status, rsa_decrypt_key(data, dl, bytes, &outlen, NULL, 0, hash.index, &result, &privateKey), + "Error decrypting message"); + if (outlen < RANDSIZE) + error(status, "Malformed data from server - missing random block"); + + // next append login to random block + unsigned len = strlen(login); + if (len > LOGINSIZE) + len = LOGINSIZE; + memcpy(&bytes[RANDSIZE], login, len); + + // calc hash for whole block + hash_state state; + sha256_init(&state); + check(status, sha256_process(&state, bytes, RANDSIZE + len), "Error hashing message"); + unsigned char digest[256 / 8]; + check(status, sha256_done(&state, digest), "Error extracting hash"); + + // build message + unsigned char msg[4096]; + + // put login to it + memcpy(msg, login, len); + msg[len++] = 0; + + // append sign of hash to it + unsigned long signLen = sizeof(msg) - len; + unsigned char* sign = &msg[len]; + check(status, rsa_sign_hash(digest, sizeof digest, sign, &signLen, &pseudoRand.state, + pseudoRand.index, hash.index, SALTLEN, &privateKey), "Error signing message hash"); + + // send message + cBlock->putData(status, len + signLen, msg); + + // output the wire crypt key + ICryptKey* cKey = cBlock->newKey(status); + cKey->setSymmetric(status, "Symmetric", RANDSIZE, bytes); + HANDSHAKE_DEBUG( fprintf(stderr, "Key ="); for (unsigned n = 0; n < RANDSIZE; ++n) + fprintf(stderr, " %02u", bytes[n]); fprintf(stderr, "\n"); ) + + return AUTH_SUCCESS; + } + catch(const FbException& ex) + { + status->setErrors(ex.getStatus()->getErrors()); + return AUTH_FAILED; + } +} + + +// +// Server plugin +// + +class ExtAuthServer : public IServerImpl, public PluginData +{ +public: + ExtAuthServer(ThrowStatusWrapper* status, IPluginConfig* cnf) + : PluginData(status, cnf), sentData(false) + { } + + // IServer implementation + int authenticate(ThrowStatusWrapper* status, IServerBlock* sBlock, IWriter* writerInterface); + + void setDbCryptCallback(ThrowStatusWrapper* status, ICryptKeyCallback* cryptCallback) + { } + + int release() + { + if (--refCounter == 0) + { + delete this; + return 0; + } + return 1; + } + + void addRef() + { + ++refCounter; + } + + void setOwner(IReferenceCounted* o) + { + owner = o; + } + + IReferenceCounted* getOwner() + { + return owner; + } + +private: + unsigned char msg[RANDSIZE + LOGINSIZE]; + bool sentData; +}; + + +int ExtAuthServer::authenticate(ThrowStatusWrapper* status, IServerBlock* sBlock, IWriter* writerInterface) +{ + try + { + // did we initialize correctly? + if (iniLvl < 3) + return AUTH_CONTINUE; + + unsigned dl = 0; + const unsigned char* data = sBlock->getData(&dl); + if (!sentData) + { + // fbassert(dl == 0 && !data); + + // build message: first of all get some randomness + pseudoRand.getDsc()->read(msg, RANDSIZE, &pseudoRand.state); + + // now encrypt that random block + unsigned char encrypted[4096]; + unsigned long encLen = sizeof encrypted; + check(status, rsa_encrypt_key(msg, RANDSIZE, encrypted, &encLen, NULL, 0, + &pseudoRand.state, pseudoRand.index, hash.index, &privateKey), "Error encrypting message"); + + // send message + sBlock->putData(status, encLen, encrypted); + sentData = true; + + return AUTH_MORE_DATA; + } + + // decompose message + const char* login = reinterpret_cast(data); + unsigned len = strnlen(login, dl); + if (len == dl) + error(status, "Wrong data from client - no signature in a message"); + if (len == 0) + error(status, "Wrong data from client - empty login"); + if (len > LOGINSIZE) + error(status, "Wrong data from client - login too long"); + memcpy(&msg[RANDSIZE], data, len); + const unsigned char* sign = &data[len + 1]; + unsigned long signLen = dl - (len + 1); + + // calc hash for message + hash_state state; + sha256_init(&state); + check(status, sha256_process(&state, msg, RANDSIZE + len), "Error hashing message"); + unsigned char digest[256 / 8]; + check(status, sha256_done(&state, digest), "Error extracting hash"); + + // validate signature + int result = 0; + int err = rsa_verify_hash(sign, signLen, digest, sizeof digest, hash.index, SALTLEN, &result, &privateKey); + if (err != CRYPT_INVALID_PACKET) + check(status, err, "Error verifying digital signature"); + else + result = 0; + if (!result) + error(status, "Malformed data from client - invalid digital signature"); + + // output the wire crypt key + ICryptKey* cKey = sBlock->newKey(status); + cKey->setSymmetric(status, "Symmetric", RANDSIZE, msg); + HANDSHAKE_DEBUG( fprintf(stderr, "Key ="); for (unsigned n = 0; n < RANDSIZE; ++n) + fprintf(stderr, " %02x", msg[n]); fprintf(stderr, "\n"); ) + + // store received login name in auth block + writerInterface->add(status, login); + + return AUTH_SUCCESS; + } + catch(const FbException& ex) + { + status->setErrors(ex.getStatus()->getErrors()); + } + return AUTH_FAILED; +} + + +// +// Static variables +// + +PluginModule module; +Factory clientFactory; +Factory serverFactory; + +} // anonymous namespace + + +#if defined(_WIN32) +#define FB_DLL_EXPORT __declspec(dllexport) +#else +#define FB_DLL_EXPORT +#endif + +extern "C" void FB_DLL_EXPORT FB_PLUGIN_ENTRY_POINT(IMaster* m) +{ + master = m; + IPluginManager* pluginManager = master->getPluginManager(); + + module.registerMe(pluginManager); + pluginManager->registerPluginFactory(IPluginManager::TYPE_AUTH_CLIENT, "ExtAuth", &clientFactory); + pluginManager->registerPluginFactory(IPluginManager::TYPE_AUTH_SERVER, "ExtAuth", &serverFactory); +} diff --git a/examples/extauth/INSTALL b/examples/extauth/INSTALL new file mode 100644 index 0000000000..e02d1c1378 --- /dev/null +++ b/examples/extauth/INSTALL @@ -0,0 +1,64 @@ +0. Brief description. + +ExtAuth plugin is useful when you want to run 'execute statement on external' statement +connecting to databases on non-local servers but do not wish to explicitly add login and +password to your PL/SQL code. When 2 servers completely trust each other this plugin may +be used to enable access to remote database without entering login and password in SQL +code. To ensure that connection comes from trusted source shared secret key (placed into +plugin's .conf file) is used. That means that the value of a "Key" parameter should be +exacly the same for all trusting each other hosts. Pay attention - SQL name of connected +user on remote host may not match local logon, it depends also upon mappings on remote +host. + +1. Before starting the build. + +This authentication plugin is using TomCrypt (https://www.libtom.net/LibTomCrypt/) library. +Firebird since v.4 is actively using it. Depending upon build type tomcrypt binary may be +included or not included into your package. In a case when it's included you will find +appropriate H-files in tomcrypt.include subdir. If not that means that native OS library +is used and you should also work with it. Depending upon your OS you will may be need +to install development package for tomcrypt. + +2. Building plugin. + +Type 'make' in this directory. Build system supposes that it was not moved out of standard +firebird tree. In a case when you did it manually you will sooner of all have to change +Makefile appropriately. If you use firebird with different operating systems and/or hardware +you should build plugin for each used configuration. + +3. Installing plugin. + +Makefile has install target (make install) which may be used for current box. However +this is not full solution because plugin is supposed to be used to connect at least two +separate servers. See 'Testing' for more details. + +4.Testing. + +- imagine you have to hosts: host1 and host2; +- generate configuration file using extauth_keygen utility on any of them (only ONCE - +on ONE host !!!); +- copy that file and plugin itself to $FIREBIRD/plugins directory on each host; +- modife firebird.cond, it should contain something like: + AuthServer = Srp256, ExtAuth + AuthClient = Srp256, ExtAuth +lines, certainly something else may be used instead recommended Srp256; +- if you need WIN_SSPI plugin please add it AFTER ExtAuth; +- do not forget to restart firebird after reconfiguring it; +- create minimal required mapping on host1: + CREATE MAPPING EXT USING PLUGIN EXTAUTH FROM ANY USER TO USER EXTUSER; +- run the following script on host2: + ^SET TERM ^; + EXECUTE BLOCK RETURNS(REMNAME CHAR(32)) AS BEGIN + EXECUTE STATEMENT 'SELECT CURRENT_USER FROM RDB$DATABASE' + ON EXTERNAL 'host1:employee' INTO :REMNAME; + SUSPEND; + END^ + SET TERM ;^ +you should get something like this: + REMNAME + ============================== + EXTUSER +- explicitly specifying login and/or password in SQL statement normally deactivates +this plugin but one can use IgnoreLogin and IgnorePassword parameters to change that. + + diff --git a/examples/extauth/Makefile b/examples/extauth/Makefile new file mode 100644 index 0000000000..b5fbff9327 --- /dev/null +++ b/examples/extauth/Makefile @@ -0,0 +1,78 @@ +# The contents of this file are subject to the Initial +# Developer's Public License Version 1.0 (the "License"); +# you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# https://www.firebirdsql.org/en/initial-developer-s-public-license-version-1-0/ +# +# Software distributed under the License is distributed AS IS, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. +# See the License for the specific language governing rights +# and limitations under the License. +# +# The Original Code was created by Alexander Peshkoff +# for the Firebird Open Source RDBMS project. +# +# Copyright (c) 2020 Alexander Peshkoff +# and all contributors signed below. +# +# All Rights Reserved. +# Contributor(s): ______________________________________. + +ROOT=../.. +#ROOT=$(shell cd ../..; pwd) + +PLUGINS=$(ROOT)/plugins +BIN=$(ROOT)/bin +LIB=$(ROOT)/lib + +LIB_PREFIX=lib +SHRLIB_EXT=so + +makePluginName=$(PLUGINS)/$(LIB_PREFIX)$(1).$(SHRLIB_EXT) + +TOMCRYPT_COMPILE=-DLTC_PTHREAD -DUSE_LTM -DLTM_DESC +OwnInclude=$(shell [ -d tomcrypt.include ] && echo Yes || echo No) +ifeq ($(OwnInclude), Yes) +TOMCRYPT_COMPILE += -Itomcrypt.include +TOMCRYPT_LINK=-L$(LIB)/.tm +endif +BLD_SIMPLE_KEY_AUTH=libExtAuth.so +SIMPLE_KEY_AUTH=$(call makePluginName,ExtAuth) +BLD_KEYGEN=extauth_keygen +KEYGEN=$(BIN)/$(BLD_KEYGEN) + +KEYGEN_objects=keygen.o +TCWRAP_objects=TcWrapper.o +KEY_AUTH_objects=ExtAuth.o + +CXXFLAGS=-std=c++11 -pthread -I$(ROOT)/include -fPIC $(TOMCRYPT_COMPILE) +LDFLAGS=-pthread -L$(LIB) -Wl,-rpath,'$$ORIGIN/../lib' $(TOMCRYPT_LINK) + +LINK=c++ +LINK_LIBS=-lfbclient -ltomcrypt + +.PHONY: all keygen plugin install + +all: keygen plugin + +keygen: $(BLD_KEYGEN) + +$(BLD_KEYGEN): $(KEYGEN_objects) $(TCWRAP_objects) + $(LINK) $(LDFLAGS) $^ -o $@ $(LINK_LIBS) + +plugin: $(BLD_SIMPLE_KEY_AUTH) + +$(BLD_SIMPLE_KEY_AUTH): $(KEY_AUTH_objects) $(TCWRAP_objects) + $(LINK) -shared $(LDFLAGS) $^ -o $@ $(LINK_LIBS) + +clean: + rm -f *.o* $(BLD_KEYGEN) $(BLD_SIMPLE_KEY_AUTH) + +install: $(SIMPLE_KEY_AUTH) $(KEYGEN) + +$(SIMPLE_KEY_AUTH): $(BLD_SIMPLE_KEY_AUTH) + cp $^ $@ + +$(KEYGEN): $(BLD_KEYGEN) + cp $^ $@ + diff --git a/examples/extauth/TcWrapper.cpp b/examples/extauth/TcWrapper.cpp new file mode 100644 index 0000000000..d4feacebb9 --- /dev/null +++ b/examples/extauth/TcWrapper.cpp @@ -0,0 +1,122 @@ +/* + * Tomcrypt library <= firebird : c++ wrapper. + * + * The contents of this file are subject to the Initial + * Developer's Public License Version 1.0 (the "License"); + * you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * https://www.firebirdsql.org/en/initial-developer-s-public-license-version-1-0/ + * + * Software distributed under the License is distributed AS IS, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. + * See the License for the specific language governing rights + * and limitations under the License. + * + * The Original Code was created by Alexander Peshkoff + * for the Firebird Open Source RDBMS project. + * + * Copyright (c) 2020 Alexander Peshkoff + * and all contributors signed below. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + */ + +#include "TcWrapper.h" + +namespace { +// LTC hack +class GInit +{ +public: + GInit() + { + ltc_mp = ltm_desc; + } +}; +GInit gInit; +} + +void error(ThrowStatusWrapper* status, const char* text) +{ + if (! status) + throw text; + + ISC_STATUS_ARRAY v; + v[0] = isc_arg_gds; + v[1] = isc_random; + v[2] = isc_arg_string; + v[3] = (ISC_STATUS) text; + v[4] = isc_arg_end; + + throw FbException(status, v); +} + +void check(ThrowStatusWrapper* status, int err, const char* text) +{ + if (err == CRYPT_OK) + return; + + char buf[256]; + sprintf(buf, "%s: %s", text, error_to_string(err)); + error(status, buf); +} + +unsigned readHexKey(ThrowStatusWrapper* status, const char* hex, unsigned char* key, unsigned bufSize) +{ + unsigned char* k = key; + const char* const end = hex + strlen(hex) - 1; + for (const char* s = hex; s < end; s += 2) + { + if (k - key >= bufSize) + break; + + // FF + char ss[3]; + ss[0] = s[0]; + ss[1] = s[1]; + ss[2] = 0; + unsigned c = strtoul(ss, NULL, 16); + if (c > 255) + error(status, "Key format error"); + *k++ = static_cast(c); + } + return k - key; +} + +void PseudoRandom::init(ThrowStatusWrapper* status) +{ + // LTC hack + ltc_mp = ltm_desc; + + // register yarrow + index = register_prng(&yarrow_desc); + if (index == -1) + error(status, "Error registering PRNG yarrow"); + + // setup the PRNG + check(status, yarrow_start(&state), "Error starting PRNG yarrow"); + check(status, rng_make_prng(64, index, &state, NULL), "Error setting up PRNG yarrow"); +} + +void PseudoRandom::fini() +{ + yarrow_done(&state); +} + +const PseudoRandom::PrngDescriptor* PseudoRandom::getDsc() +{ + return &yarrow_desc; +} + +void Hash::init(ThrowStatusWrapper* status, const ltc_hash_descriptor* desc) +{ + // LTC hack + ltc_mp = ltm_desc; + + /* register SHA256 */ + index = register_hash(desc); + if (index == -1) + error(status, "Error registering SHA256"); +} + diff --git a/examples/extauth/TcWrapper.h b/examples/extauth/TcWrapper.h new file mode 100644 index 0000000000..14f9e1826e --- /dev/null +++ b/examples/extauth/TcWrapper.h @@ -0,0 +1,210 @@ +/* + * Tomcrypt library <= firebird : c++ wrapper. + * + * The contents of this file are subject to the Initial + * Developer's Public License Version 1.0 (the "License"); + * you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * https://www.firebirdsql.org/en/initial-developer-s-public-license-version-1-0/ + * + * Software distributed under the License is distributed AS IS, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. + * See the License for the specific language governing rights + * and limitations under the License. + * + * The Original Code was created by Alexander Peshkoff + * for the Firebird Open Source RDBMS project. + * + * Copyright (c) 2020 Alexander Peshkoff + * and all contributors signed below. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + */ + +#include + +#include +using namespace Firebird; + +#include + +void error(ThrowStatusWrapper* status, const char* text); +void check(ThrowStatusWrapper* status, int err, const char* text); +unsigned readHexKey(ThrowStatusWrapper* status, const char* hex, unsigned char* key, unsigned bufSize); + +class PseudoRandom +{ +public: +#if CRYPT > 0x0100 + typedef ltc_prng_descriptor PrngDescriptor; +#else + typedef _prng_descriptor PrngDescriptor; +#endif + + void init(ThrowStatusWrapper* status); + void fini(); + const PrngDescriptor* getDsc(); + + int index; + prng_state state; +}; + +class Hash +{ +protected: + void init(ThrowStatusWrapper* status, const ltc_hash_descriptor* desc); + +public: + void fini() + { } + + int index; +}; + +class HashSha1 : public Hash +{ +public: + void init(ThrowStatusWrapper* status) + { + Hash::init(status, &sha1_desc); + } +}; + +class HashSha256 : public Hash +{ +public: + void init(ThrowStatusWrapper* status) + { + Hash::init(status, &sha256_desc); + } +}; + +// controls reference counter of the object where points +enum NoIncrement {NO_INCREMENT}; +template +class RefPtr +{ +public: + RefPtr() : ptr(NULL) + { } + + explicit RefPtr(T* p) : ptr(p) + { + if (ptr) + { + ptr->addRef(); + } + } + + // This special form of ctor is used to create refcounted ptr from interface, + // returned by a function (which increments counter on return) + RefPtr(NoIncrement x, T* p) : ptr(p) + { } + + RefPtr(const RefPtr& r) : ptr(r.ptr) + { + if (ptr) + { + ptr->addRef(); + } + } + + ~RefPtr() + { + if (ptr) + { + ptr->release(); + } + } + + T* operator=(T* p) + { + return assign(p); + } + + T* operator=(const RefPtr& r) + { + return assign(r.ptr); + } + + operator T*() + { + return ptr; + } + + T* operator->() + { + return ptr; + } + + operator const T*() const + { + return ptr; + } + + const T* operator->() const + { + return ptr; + } + + bool operator !() const + { + return !ptr; + } + + bool operator ==(const RefPtr& r) const + { + return ptr == r.ptr; + } + + bool operator !=(const RefPtr& r) const + { + return ptr != r.ptr; + } + + void clear() throw() // Used after detach/commit/close/etc., i.e. release() not needed + { + ptr = NULL; + } + + void assignNoIncrement(T* const p) + { + assign(NULL); + ptr = p; + } + +private: + T* assign(T* const p) + { + if (ptr != p) + { + if (p) + { + p->addRef(); + } + + T* tmp = ptr; + ptr = p; + + if (tmp) + { + tmp->release(); + } + } + + return ptr; + } + + T* ptr; +}; + +// Often used form of RefPtr +template +class AutoRelease : public RefPtr +{ +public: + AutoRelease(R* r) + : RefPtr(NO_INCREMENT, r) + { } +}; diff --git a/examples/extauth/keygen.cpp b/examples/extauth/keygen.cpp new file mode 100644 index 0000000000..b7061d0d78 --- /dev/null +++ b/examples/extauth/keygen.cpp @@ -0,0 +1,73 @@ +/* + * RSA key generate. + * + * The contents of this file are subject to the Initial + * Developer's Public License Version 1.0 (the "License"); + * you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * https://www.firebirdsql.org/en/initial-developer-s-public-license-version-1-0/ + * + * Software distributed under the License is distributed AS IS, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. + * See the License for the specific language governing rights + * and limitations under the License. + * + * The Original Code was created by Alexander Peshkoff + * for the Firebird Open Source RDBMS project. + * + * Copyright (c) 2020 Alexander Peshkoff + * and all contributors signed below. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + */ + +#include "TcWrapper.h" + +int main(int ac, char** av) +{ + try + { + int len = ac > 1 ? atoi(av[1]) : 2048; + + PseudoRandom pseudoRand; + pseudoRand.init(NULL); + + rsa_key key; + check(NULL, rsa_make_key(&pseudoRand.state, pseudoRand.index, len / 8, 65537, &key), + "Error making RSA key"); + + unsigned char outbuf[4096]; + unsigned long outlen = sizeof outbuf; + check(NULL, rsa_export(outbuf, &outlen, PK_PRIVATE, &key), + "Error exporting private RSA key"); + + const char* const file = "ExtAuth.conf"; + FILE* conf = fopen(file, "w"); + if (!conf) + { + perror(file); + return 1; + } + + fprintf(conf, "Key = "); + for (unsigned i = 0; i < outlen; ++i) + fprintf(conf, "%02x", outbuf[i]); + fprintf(conf, "\n#IgnoreLogin = No\n#IgnorePassword = No\n"); + + if (fclose(conf) != 0) + { + perror(file); + return 1; + } + printf("Wrote configuration file %s\n", file); + } + catch (const char* message) + { + fprintf(stderr, "%s\n", message); + return 1; + } + + return 0; +} + diff --git a/examples/readme b/examples/readme index e53afb2392..b79d9bb03c 100644 --- a/examples/readme +++ b/examples/readme @@ -65,7 +65,8 @@ stat12.e Event wait and signaling. stat12t.e WHENEVER SQLERROR and BASED_ON clause are illustrated by many programs. -^L + + Embedded Dynamic SQL @@ -84,7 +85,8 @@ dyn5.e Demonstrate dynamic reallocation of SQLDA and 'describe' statement. dynfull.e A full_dsql program (process unknown statements). VARY struct is used by dyn3.e, dynfull.e. -^L + + API Interface @@ -140,3 +142,42 @@ SQLCODE extraction from status is covered by several programs. Zero transaction handle is covered in several programs, ex. api14.e. + +Object-oriented API (interfaces) + +Program Description +--------- -------------------------------------------------------- +01.create.cpp / A sample of creating new database and new table in it. +01.create.pas Has pascal (delphi) implementation. + +02.update.cpp UPDATE statement with parameters. + +03.select.cpp SELECT statement without input parameters. + +04.print_table.cpp Open cursor from attachment and access blob data. + +05.user_metadata.cpp Cursor with user-implemented interface of message format. + +06.fb_message.cpp Use of static messages and decfloat values. + +07.blob.cpp Loading data into blob and reading it. + +08.events.cpp Working with events. + +09.service.cpp Using services: prints server version and db statistics. + +10.backup.cpp Using services: backup database. + +11.batch.cpp Working with batch interface. + +12.batch_isc.cpp Working with batch interface from ISC API. + + +FbSampleAtomic typedef required in many examples. + + + +dbcrypt - a sample of XOR database encryption (do not use in production!!!) + +extauth - authentication for cross-server connections based on having same secret key on all servers +