mirror of
https://github.com/postgres/postgres.git
synced 2025-04-24 10:47:04 +03:00
Provide a TLS init hook
The default hook function sets the default password callback function. In order to allow preloaded libraries to have an opportunity to override the default, TLS initialization if now delayed slightly until after shared preloaded libraries have been loaded. A test module is provided which contains a trivial example that decodes an obfuscated password for an SSL certificate. Author: Andrew Dunstan Reviewed By: Andreas Karlsson, Asaba Takanori Discussion: https://postgr.es/m/04116472-818b-5859-1d74-3d995aab2252@2ndQuadrant.com
This commit is contained in:
parent
ffd398021c
commit
896fcdb230
@ -45,6 +45,9 @@
|
||||
#include "tcop/tcopprot.h"
|
||||
#include "utils/memutils.h"
|
||||
|
||||
/* default init hook can be overridden by a shared library */
|
||||
static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
|
||||
openssl_tls_init_hook_typ openssl_tls_init_hook = default_openssl_tls_init;
|
||||
|
||||
static int my_sock_read(BIO *h, char *buf, int size);
|
||||
static int my_sock_write(BIO *h, const char *buf, int size);
|
||||
@ -117,27 +120,10 @@ be_tls_init(bool isServerStart)
|
||||
SSL_CTX_set_mode(context, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
|
||||
|
||||
/*
|
||||
* Set password callback
|
||||
* Call init hook (usually to set password callback)
|
||||
*/
|
||||
if (isServerStart)
|
||||
{
|
||||
if (ssl_passphrase_command[0])
|
||||
SSL_CTX_set_default_passwd_cb(context, ssl_external_passwd_cb);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ssl_passphrase_command[0] && ssl_passphrase_command_supports_reload)
|
||||
SSL_CTX_set_default_passwd_cb(context, ssl_external_passwd_cb);
|
||||
else
|
||||
(* openssl_tls_init_hook)(context, isServerStart);
|
||||
|
||||
/*
|
||||
* If reloading and no external command is configured, override
|
||||
* OpenSSL's default handling of passphrase-protected files,
|
||||
* because we don't want to prompt for a passphrase in an
|
||||
* already-running server.
|
||||
*/
|
||||
SSL_CTX_set_default_passwd_cb(context, dummy_ssl_passwd_cb);
|
||||
}
|
||||
/* used by the callback */
|
||||
ssl_is_server_start = isServerStart;
|
||||
|
||||
@ -1338,3 +1324,27 @@ ssl_protocol_version_to_openssl(int v)
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
default_openssl_tls_init(SSL_CTX *context, bool isServerStart)
|
||||
{
|
||||
if (isServerStart)
|
||||
{
|
||||
if (ssl_passphrase_command[0])
|
||||
SSL_CTX_set_default_passwd_cb(context, ssl_external_passwd_cb);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ssl_passphrase_command[0] && ssl_passphrase_command_supports_reload)
|
||||
SSL_CTX_set_default_passwd_cb(context, ssl_external_passwd_cb);
|
||||
else
|
||||
/*
|
||||
* If reloading and no external command is configured, override
|
||||
* OpenSSL's default handling of passphrase-protected files,
|
||||
* because we don't want to prompt for a passphrase in an
|
||||
* already-running server.
|
||||
*/
|
||||
SSL_CTX_set_default_passwd_cb(context, dummy_ssl_passwd_cb);
|
||||
}
|
||||
}
|
||||
|
@ -972,17 +972,6 @@ PostmasterMain(int argc, char *argv[])
|
||||
*/
|
||||
LocalProcessControlFile(false);
|
||||
|
||||
/*
|
||||
* Initialize SSL library, if specified.
|
||||
*/
|
||||
#ifdef USE_SSL
|
||||
if (EnableSSL)
|
||||
{
|
||||
(void) secure_initialize(true);
|
||||
LoadedSSL = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Register the apply launcher. Since it registers a background worker,
|
||||
* it needs to be called before InitializeMaxBackends(), and it's probably
|
||||
@ -996,6 +985,17 @@ PostmasterMain(int argc, char *argv[])
|
||||
*/
|
||||
process_shared_preload_libraries();
|
||||
|
||||
/*
|
||||
* Initialize SSL library, if specified.
|
||||
*/
|
||||
#ifdef USE_SSL
|
||||
if (EnableSSL)
|
||||
{
|
||||
(void) secure_initialize(true);
|
||||
LoadedSSL = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Now that loadable modules have had their chance to register background
|
||||
* workers, calculate MaxBackends.
|
||||
|
@ -287,6 +287,10 @@ extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);
|
||||
extern char *be_tls_get_certificate_hash(Port *port, size_t *len);
|
||||
#endif
|
||||
|
||||
/* init hook for SSL, the default sets the password callback if appropriate */
|
||||
typedef void(* openssl_tls_init_hook_typ)(SSL_CTX *context, bool isServerStart);
|
||||
extern openssl_tls_init_hook_typ openssl_tls_init_hook;
|
||||
|
||||
#endif /* USE_SSL */
|
||||
|
||||
#ifdef ENABLE_GSS
|
||||
|
@ -25,4 +25,9 @@ SUBDIRS = \
|
||||
unsafe_tests \
|
||||
worker_spi
|
||||
|
||||
ifeq ($(with_openssl),yes)
|
||||
SUBDIRS += ssl_passphrase_callback
|
||||
endif
|
||||
|
||||
|
||||
$(recurse)
|
||||
|
1
src/test/modules/ssl_passphrase_callback/.gitignore
vendored
Normal file
1
src/test/modules/ssl_passphrase_callback/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
tmp_check
|
24
src/test/modules/ssl_passphrase_callback/Makefile
Normal file
24
src/test/modules/ssl_passphrase_callback/Makefile
Normal file
@ -0,0 +1,24 @@
|
||||
# ssl_passphrase Makefile
|
||||
|
||||
export with_openssl
|
||||
|
||||
MODULE_big = ssl_passphrase_func
|
||||
OBJS = ssl_passphrase_func.o $(WIN32RES)
|
||||
PGFILEDESC = "callback function to provide a passphrase"
|
||||
|
||||
ifdef USE_PGXS
|
||||
PG_CONFIG = pg_config
|
||||
PGXS := $(shell $(PG_CONFIG) --pgxs)
|
||||
include $(PGXS)
|
||||
else
|
||||
subdir = src/test/modules/ssl_passphrase_callback
|
||||
top_builddir = ../../../..
|
||||
include $(top_builddir)/src/Makefile.global
|
||||
include $(top_srcdir)/contrib/contrib-global.mk
|
||||
endif
|
||||
|
||||
check: prove-check
|
||||
|
||||
prove-check: ssl_passphrase_func$(DLSUFFIX) | temp-install
|
||||
@echo running prove ...
|
||||
$(prove_check)
|
19
src/test/modules/ssl_passphrase_callback/server.crt
Normal file
19
src/test/modules/ssl_passphrase_callback/server.crt
Normal file
@ -0,0 +1,19 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDCTCCAfGgAwIBAgIUfHgPLNys4V0d0cWrzRHqfs91LFMwDQYJKoZIhvcNAQEL
|
||||
BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTIwMDMyMTE0MDM1OVoXDTQ3MDgw
|
||||
NzE0MDM1OVowFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEF
|
||||
AAOCAQ8AMIIBCgKCAQEA2j0PZwmeahBC7QpG7i9/VUVJrLzy+b8oVaqZUO6nlPbY
|
||||
wuPISYTO/jqc0XDfs/Gb0kccDJ6bPfNfvSnRTG1omE6OO9YjR0u3296l4bWAmYVq
|
||||
q4SesgQmm1Wy8ODNpeGaoBUwR51OB/gFHFjUlqAjRwOmrTCbDiAsLt7e+cx+W26r
|
||||
2SrJIweiSJsqaQsMMaqlY2qpHnYgWfqRUTqwXqlno0dXuqBt+KKgqeHMY3w3XS51
|
||||
8roOI0+Q9KWsexL/aYnLwMRsHRMZcthhzTK6HD/OrLh9CxURImr4ed9TtsNiZltA
|
||||
KqLTeGbtS1D2AvFqJU8n5DvtU+26wDrHu6pEM3kSJQIDAQABo1MwUTAdBgNVHQ4E
|
||||
FgQUkkfa08hDnxYs1UjG2ydCBJs1b2AwHwYDVR0jBBgwFoAUkkfa08hDnxYs1UjG
|
||||
2ydCBJs1b2AwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAjsJh
|
||||
p4tCopCA/Pvxupv3VEwGJ+nbH7Zg/hp+o2IWuHBOK1qrkyXBv34h/69bRnWZ5UFV
|
||||
HxQwL7CjNtjZu9SbpKkaHbZXPWANC9fbPKdBz9fAEwunf33KbZe3dPv/7xbJirMz
|
||||
e+j5V0LE0Spkr/p89LipXfjGw0jLC8VRTx/vKPnmbiBsCKw5SQKh3w7CcBx84Y6q
|
||||
Nc27WQ8ReR4W4X1zHGN6kEV4H+yPN2Z9OlSixTiSNvr2mtJQsZa7gK7Wwfm79RN7
|
||||
5Kf3l8b6e2BToJwLorpK9mvu41NtwRzl4UoJ1BFJDyhMplFMd8RcwTW6yT2biOFC
|
||||
lYCajcBoms3IiyqBog==
|
||||
-----END CERTIFICATE-----
|
30
src/test/modules/ssl_passphrase_callback/server.key
Normal file
30
src/test/modules/ssl_passphrase_callback/server.key
Normal file
@ -0,0 +1,30 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
Proc-Type: 4,ENCRYPTED
|
||||
DEK-Info: AES-256-CBC,DB0E7068D4DCE79FFE63C95B8D8F7CEA
|
||||
|
||||
Y4uvnlWX/kyulqsmt8aWI55vKFdfJL4wEZItL8ZKlQFuZuxC9w0OworyjTdqO38R
|
||||
v9hwnetZBDgK8kEv6U7wR58mTfwHHCGuxYgSiPZtiW7btS4zu16ePdh8oBEzCxjW
|
||||
ALrCFt7uvRu5h2AWy/4BgV4gLNVPNB+lJFABtUoiSnUDr7+bcx7UjZ4An6HriGxC
|
||||
Kg/N1pKjT/xiKOy+yHtrp1Jih5HYDE4i99jPtMuTROf8Uyz7ibyrdd/E7QNvANQN
|
||||
Cmw4I4Xk4hZ68F0iGU0C0wLND3pWbeYPKorpo3PkI4Du+Aqlg15ae5u8CtU3fXGJ
|
||||
mq4/qLGAi1sr/45f5P5a3Q8BQpKkCmGopXMaFYOOiaf3YYgD1eVOxLhsCWqUB+O8
|
||||
ygcTNRCoKhzY+ULComXp880J3fFk5b92g4Hm1PAO42uDKzhWSnrmCBJ7ynXvnEc+
|
||||
JqhiE8Obrp6FBIHvfN26JtHcXTd/bgUMXSh7AXjsotfvPPV0URve9JJG+RnwckeT
|
||||
K3AYDOQK/lbqDGliNqHg1WiMSA2oHSqDhUMB0Sm0jh6+jxCQlsmSDvPvJfWRo5wY
|
||||
zbZZZARQnFUaHa9CZVdFxbaPGhYU6vAwxDqi42osSJEdf68Gy2KVXcelqpU/2dKk
|
||||
aHfTgAWOsajbgt9p+0369TeZb39+zLODdDJnvZYiu1pTASHP5VrJ2xHhu5zOdjXm
|
||||
GafYiPwYBM280wkIVQ0HsTX7BViU2R/7W3FqflXgQvBiraVQVwHyaX4bOU1a3rzg
|
||||
emHNLTCpRamT0i/D0tkEPgS42bWSVi9ko5Mn9yb+qToBjAOLVUOAOs9Bv3qxawhI
|
||||
XFbBDZ7DS59l2yV6eQkrG7DUCLDf4dv4WZeBnhrPe/Jg8HKcsKcJYV3cejZh8sgu
|
||||
XHeCU50+jpJDfTZVPW3TjZWmrTqStGwF1UFpj+tTsTcX+OHAY/shFs3bBZulAsMy
|
||||
5UWZWzyWHMWr/wbxW7dbhTb1gNmOgpQQz9dunSgcZ8umzSGLa0ZGmnQj9P/kZkQA
|
||||
RenuswH5O7CK/MDmf3J6svwyLt/jULmH26MZTcNu7igT6dj3VMSwkoQQaaQdtmzb
|
||||
glzN3uqf8qM+CEjV8dxlt8fv6KJV7gvoYfPAz+1pp5DVJBmRo/+b4e/d4QTV9iWS
|
||||
ScBYdonc9WXcrjmExX9+Wf/K/IKfLnKLIi2MZ3pwr1n7yY+dMeF6iREYSjFVIpZd
|
||||
MH3G9/SxTrqR7X/eHjwdv1UupYYyaDag8wpVn1RMCb0xYqh2/QP1k0pQycckL0WQ
|
||||
lieXibEuQhV/heXcqt83G6pGqLImc6YPYU46jdGpPIMyOK+ZSqJTHUWHfRMQTIMz
|
||||
varR2M3uhHvwUFzmvjLh/o6I3r0a0Rl1MztpYfjBV6MS4BKYfraWZ0kxCyV+e6tz
|
||||
O7vD0P5W2qm6b89Md3nqjUcbOM8AojcfBl3xpQrpSdgJ25YJBoJ9L2I2pIMNCK/x
|
||||
yDNEJl7yP87fdHfXZm2VoUXclDUYHyNys9Rtv9NSr+VNkIMcqrCHEgpAxwQQ5NsO
|
||||
/vOZe3wjhXXLyRO7Nh5W8jojw3xcb9c9avFUWUvM2BaS4vEYcItUoF4QuHohrCwk
|
||||
-----END RSA PRIVATE KEY-----
|
@ -0,0 +1,88 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* ssl_passphrase_func.c
|
||||
*
|
||||
* Loadable PostgreSQL module fetch an ssl passphrase for the server cert.
|
||||
* instead of calling an external program. This implementation just hands
|
||||
* back the configured password rot13'd.
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "postgres.h"
|
||||
|
||||
#include <float.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "libpq/libpq.h"
|
||||
#include "libpq/libpq-be.h"
|
||||
#include "utils/guc.h"
|
||||
|
||||
PG_MODULE_MAGIC;
|
||||
|
||||
void _PG_init(void);
|
||||
void _PG_fini(void);
|
||||
|
||||
static char *ssl_passphrase = NULL;
|
||||
|
||||
/* callback function */
|
||||
static int rot13_passphrase(char *buf, int size, int rwflag, void *userdata);
|
||||
/* hook function to set the callback */
|
||||
static void set_rot13(SSL_CTX *context, bool isServerStart);
|
||||
/*
|
||||
* Module load callback
|
||||
*/
|
||||
void
|
||||
_PG_init(void)
|
||||
{
|
||||
/* Define custom GUC variable. */
|
||||
DefineCustomStringVariable("ssl_passphrase.passphrase",
|
||||
"passphrase before transformation",
|
||||
NULL,
|
||||
&ssl_passphrase,
|
||||
NULL,
|
||||
PGC_SIGHUP,
|
||||
0, /* no flags required */
|
||||
NULL,
|
||||
NULL,
|
||||
NULL);
|
||||
if (ssl_passphrase)
|
||||
openssl_tls_init_hook = set_rot13;
|
||||
}
|
||||
|
||||
void
|
||||
_PG_fini(void)
|
||||
{
|
||||
/* do nothing yet */
|
||||
}
|
||||
|
||||
static void
|
||||
set_rot13(SSL_CTX *context, bool isServerStart)
|
||||
{
|
||||
/* warn if the user has set ssl_passphrase_command */
|
||||
if(ssl_passphrase_command[0])
|
||||
ereport(WARNING,
|
||||
(errmsg("ssl_passphrase_command setting ignored by ssl_passphrase_func module")));
|
||||
|
||||
SSL_CTX_set_default_passwd_cb(context, rot13_passphrase);
|
||||
}
|
||||
|
||||
static int
|
||||
rot13_passphrase(char *buf, int size, int rwflag, void *userdata)
|
||||
{
|
||||
|
||||
Assert(ssl_passphrase != NULL);
|
||||
StrNCpy(buf, ssl_passphrase, size);
|
||||
for (char *p = buf; *p; p++)
|
||||
{
|
||||
char c = *p;
|
||||
|
||||
if ((c >= 'a' && c <= 'm') || (c >= 'A' && c <= 'M'))
|
||||
*p = c + 13;
|
||||
else if ((c >= 'n' && c <= 'z') || (c >= 'N' && c <= 'Z'))
|
||||
*p = c - 13;
|
||||
}
|
||||
|
||||
return strlen(buf);
|
||||
|
||||
}
|
80
src/test/modules/ssl_passphrase_callback/t/001_testfunc.pl
Normal file
80
src/test/modules/ssl_passphrase_callback/t/001_testfunc.pl
Normal file
@ -0,0 +1,80 @@
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use File::Copy;
|
||||
|
||||
use TestLib;
|
||||
use Test::More;
|
||||
use PostgresNode;
|
||||
|
||||
unless (($ENV{with_openssl} || 'no') eq 'yes')
|
||||
{
|
||||
plan skip_all => 'SSL not supported by this build';
|
||||
}
|
||||
|
||||
my $clearpass = "FooBaR1";
|
||||
my $rot13pass = "SbbOnE1";
|
||||
|
||||
# self-signed cert was generated like this:
|
||||
# system('openssl req -new -x509 -days 10000 -nodes -out server.crt -keyout server.ckey -subj "/CN=localhost"');
|
||||
# add the cleartext passphrase to the key, remove the unprotected key
|
||||
# system("openssl rsa -aes256 -in server.ckey -out server.key -passout pass:$clearpass");
|
||||
# unlink "server.ckey";
|
||||
|
||||
|
||||
my $node = get_new_node('main');
|
||||
$node->init;
|
||||
$node->append_conf('postgresql.conf',
|
||||
"ssl_passphrase.passphrase = '$rot13pass'");
|
||||
$node->append_conf('postgresql.conf',
|
||||
"shared_preload_libraries = 'ssl_passphrase_func'");
|
||||
$node->append_conf('postgresql.conf', "listen_addresses = 'localhost'");
|
||||
$node->append_conf('postgresql.conf', "ssl = 'on'");
|
||||
|
||||
my $ddir = $node->data_dir;
|
||||
|
||||
# install certificate and protected key
|
||||
copy("server.crt", $ddir);
|
||||
copy("server.key", $ddir);
|
||||
chmod 0600, "$ddir/server.key";
|
||||
|
||||
$node->start;
|
||||
|
||||
# if the server is running we must have successfully transformed the passphrase
|
||||
ok(-e "$ddir/postmaster.pid", "postgres started");
|
||||
|
||||
$node->stop('fast');
|
||||
|
||||
# should get a warning if ssl_passphrase_command is set
|
||||
my $log = $node->rotate_logfile();
|
||||
|
||||
$node->append_conf('postgresql.conf',
|
||||
"ssl_passphrase_command = 'echo spl0tz'");
|
||||
|
||||
$node->start;
|
||||
|
||||
$node->stop('fast');
|
||||
|
||||
my $log_contents = slurp_file($log);
|
||||
|
||||
like(
|
||||
$log_contents,
|
||||
qr/WARNING.*ssl_passphrase_command setting ignored by ssl_passphrase_func module/,
|
||||
"ssl_passphrase_command set warning");
|
||||
|
||||
# set the wrong passphrase
|
||||
$node->append_conf('postgresql.conf', "ssl_passphrase.passphrase = 'blurfl'");
|
||||
|
||||
# try to start the server again
|
||||
my $ret = TestLib::system_log('pg_ctl', '-D', $node->data_dir, '-l',
|
||||
$node->logfile, 'start');
|
||||
|
||||
|
||||
# with a bad passphrase the server should not start
|
||||
ok($ret, "pg_ctl fails with bad passphrase");
|
||||
ok(!-e "$ddir/postmaster.pid", "postgres not started with bad passphrase");
|
||||
|
||||
# just in case
|
||||
$node->stop('fast');
|
||||
|
||||
done_testing();
|
@ -430,7 +430,7 @@ sub mkvcbuild
|
||||
|
||||
if (!$solution->{options}->{openssl})
|
||||
{
|
||||
push @contrib_excludes, 'sslinfo';
|
||||
push @contrib_excludes, 'sslinfo', 'ssl_passphrase_callback';
|
||||
}
|
||||
|
||||
if (!$solution->{options}->{uuid})
|
||||
|
Loading…
x
Reference in New Issue
Block a user