static int ssl_passwd_cb(char *buf, int size, int rwflag, void *userdata);
static int verify_cb(int, X509_STORE_CTX *);
static void info_cb(const SSL *ssl, int type, int args);
-static bool initialize_ecdh(SSL_CTX *context, bool failOnError);
+static bool initialize_ecdh(SSL_CTX *context, bool isServerStart);
static const char *SSLerrmessage(unsigned long ecode);
static char *X509_NAME_to_cstring(X509_NAME *name);
static SSL_CTX *SSL_context = NULL;
static bool SSL_initialized = false;
+static bool ssl_passwd_cb_called = false;
/* ------------------------------------------------------------ */
/* Hardcoded values */
/*
* Initialize global SSL context.
*
- * If failOnError is true, report any errors as FATAL (so we don't return).
- * Otherwise, log errors at LOG level and return -1 to indicate trouble.
- * Returns 0 if OK.
+ * If isServerStart is true, report any errors as FATAL (so we don't return).
+ * Otherwise, log errors at LOG level and return -1 to indicate trouble,
+ * preserving the old SSL state if any. Returns 0 if OK.
*/
int
-be_tls_init(bool failOnError)
+be_tls_init(bool isServerStart)
{
STACK_OF(X509_NAME) *root_cert_list = NULL;
SSL_CTX *context;
context = SSL_CTX_new(SSLv23_method());
if (!context)
{
- ereport(failOnError ? FATAL : LOG,
+ ereport(isServerStart ? FATAL : LOG,
(errmsg("could not create SSL context: %s",
SSLerrmessage(ERR_get_error()))));
goto error;
SSL_CTX_set_mode(context, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
/*
- * Override OpenSSL's default handling of passphrase-protected files.
+ * If reloading, override OpenSSL's default handling of
+ * passphrase-protected files, because we don't want to prompt for a
+ * passphrase in an already-running server. (Not that the default
+ * handling is very desirable during server start either, but some people
+ * insist we need to keep it.)
*/
- SSL_CTX_set_default_passwd_cb(context, ssl_passwd_cb);
+ if (!isServerStart)
+ SSL_CTX_set_default_passwd_cb(context, ssl_passwd_cb);
/*
* Load and verify server's certificate and private key
*/
if (SSL_CTX_use_certificate_chain_file(context, ssl_cert_file) != 1)
{
- ereport(failOnError ? FATAL : LOG,
+ ereport(isServerStart ? FATAL : LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("could not load server certificate file \"%s\": %s",
ssl_cert_file, SSLerrmessage(ERR_get_error()))));
if (stat(ssl_key_file, &buf) != 0)
{
- ereport(failOnError ? FATAL : LOG,
+ ereport(isServerStart ? FATAL : LOG,
(errcode_for_file_access(),
errmsg("could not access private key file \"%s\": %m",
ssl_key_file)));
if (!S_ISREG(buf.st_mode))
{
- ereport(failOnError ? FATAL : LOG,
+ ereport(isServerStart ? FATAL : LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("private key file \"%s\" is not a regular file",
ssl_key_file)));
}
/*
- * Refuse to load files owned by users other than us or root.
+ * Refuse to load key files owned by users other than us or root.
*
* XXX surely we can check this on Windows somehow, too.
*/
#if !defined(WIN32) && !defined(__CYGWIN__)
if (buf.st_uid != geteuid() && buf.st_uid != 0)
{
- ereport(failOnError ? FATAL : LOG,
+ ereport(isServerStart ? FATAL : LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("private key file \"%s\" must be owned by the database user or root",
ssl_key_file)));
if ((buf.st_uid == geteuid() && buf.st_mode & (S_IRWXG | S_IRWXO)) ||
(buf.st_uid == 0 && buf.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)))
{
- ereport(failOnError ? FATAL : LOG,
+ ereport(isServerStart ? FATAL : LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("private key file \"%s\" has group or world access",
ssl_key_file),
}
#endif
+ /*
+ * OK, try to load the private key file.
+ */
+ ssl_passwd_cb_called = false;
+
if (SSL_CTX_use_PrivateKey_file(context,
ssl_key_file,
SSL_FILETYPE_PEM) != 1)
{
- ereport(failOnError ? FATAL : LOG,
- (errcode(ERRCODE_CONFIG_FILE_ERROR),
- errmsg("could not load private key file \"%s\": %s",
- ssl_key_file, SSLerrmessage(ERR_get_error()))));
+ if (ssl_passwd_cb_called)
+ ereport(isServerStart ? FATAL : LOG,
+ (errcode(ERRCODE_CONFIG_FILE_ERROR),
+ errmsg("private key file \"%s\" cannot be reloaded because it requires a passphrase",
+ ssl_key_file)));
+ else
+ ereport(isServerStart ? FATAL : LOG,
+ (errcode(ERRCODE_CONFIG_FILE_ERROR),
+ errmsg("could not load private key file \"%s\": %s",
+ ssl_key_file, SSLerrmessage(ERR_get_error()))));
goto error;
}
if (SSL_CTX_check_private_key(context) != 1)
{
- ereport(failOnError ? FATAL : LOG,
+ ereport(isServerStart ? FATAL : LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("check of private key failed: %s",
SSLerrmessage(ERR_get_error()))));
SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
/* set up ephemeral ECDH keys */
- if (!initialize_ecdh(context, failOnError))
+ if (!initialize_ecdh(context, isServerStart))
goto error;
/* set up the allowed cipher list */
if (SSL_CTX_set_cipher_list(context, SSLCipherSuites) != 1)
{
- ereport(failOnError ? FATAL : LOG,
+ ereport(isServerStart ? FATAL : LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("could not set the cipher list (no valid ciphers available)")));
goto error;
if (SSL_CTX_load_verify_locations(context, ssl_ca_file, NULL) != 1 ||
(root_cert_list = SSL_load_client_CA_file(ssl_ca_file)) == NULL)
{
- ereport(failOnError ? FATAL : LOG,
+ ereport(isServerStart ? FATAL : LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("could not load root certificate file \"%s\": %s",
ssl_ca_file, SSLerrmessage(ERR_get_error()))));
}
else
{
- ereport(failOnError ? FATAL : LOG,
+ ereport(isServerStart ? FATAL : LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("could not load SSL certificate revocation list file \"%s\": %s",
ssl_crl_file, SSLerrmessage(ERR_get_error()))));
*
* If OpenSSL is told to use a passphrase-protected server key, by default
* it will issue a prompt on /dev/tty and try to read a key from there.
- * That's completely no good for a postmaster SIGHUP cycle, not to mention
- * SSL context reload in an EXEC_BACKEND postmaster child. So override it
- * with this dummy function that just returns an empty passphrase,
- * guaranteeing failure. Later we might think about collecting a passphrase
- * at server start and feeding it to OpenSSL repeatedly, but we'd still
- * need this callback for that.
+ * That's no good during a postmaster SIGHUP cycle, not to mention SSL context
+ * reload in an EXEC_BACKEND postmaster child. So override it with this dummy
+ * function that just returns an empty passphrase, guaranteeing failure.
*/
static int
ssl_passwd_cb(char *buf, int size, int rwflag, void *userdata)
{
- ereport(LOG,
- (errcode(ERRCODE_CONFIG_FILE_ERROR),
- errmsg("server's private key file requires a passphrase")));
+ /* Set flag to change the error message we'll report */
+ ssl_passwd_cb_called = true;
+ /* And return empty string */
Assert(size > 0);
buf[0] = '\0';
return 0;
}
static bool
-initialize_ecdh(SSL_CTX *context, bool failOnError)
+initialize_ecdh(SSL_CTX *context, bool isServerStart)
{
#ifndef OPENSSL_NO_ECDH
EC_KEY *ecdh;
nid = OBJ_sn2nid(SSLECDHCurve);
if (!nid)
{
- ereport(failOnError ? FATAL : LOG,
+ ereport(isServerStart ? FATAL : LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("ECDH: unrecognized curve name: %s", SSLECDHCurve)));
return false;
ecdh = EC_KEY_new_by_curve_name(nid);
if (!ecdh)
{
- ereport(failOnError ? FATAL : LOG,
+ ereport(isServerStart ? FATAL : LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("ECDH: could not create key")));
return false;