mirror of https://github.com/postgres/postgres
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.compull/51/head
parent
ffd398021c
commit
896fcdb230
@ -0,0 +1 @@ |
||||
tmp_check |
||||
@ -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)
|
||||
@ -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----- |
||||
@ -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); |
||||
|
||||
} |
||||
@ -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(); |
||||
Loading…
Reference in new issue