1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-25 01:02:05 +03:00

libq support for sslpassword connection param, DER format keys

This patch providies for support for password protected SSL client
keys in libpq, and for DER format keys, both encrypted and unencrypted.
There is a new connection parameter sslpassword, which is supplied to
the OpenSSL libraries via a callback function. The callback function can
also be set by an application by calling PQgetSSLKeyPassHook(). There is
also a function to retreive the connection setting, PQsslpassword().

Craig Ringer and Andrew Dunstan

Reviewed by: Greg Nancarrow

Discussion: https://postgr.es/m/f7ee88ed-95c4-95c1-d4bf-7b415363ab62@2ndQuadrant.com
This commit is contained in:
Andrew Dunstan
2019-11-30 15:27:13 -05:00
parent 3ff660bbeb
commit 4dc6355210
13 changed files with 376 additions and 17 deletions

View File

@ -70,6 +70,7 @@ static int initialize_SSL(PGconn *conn);
static PostgresPollingStatusType open_client_SSL(PGconn *);
static char *SSLerrmessage(unsigned long ecode);
static void SSLerrfree(char *buf);
static int PQssl_passwd_cb(char *buf, int size, int rwflag, void *userdata);
static int my_sock_read(BIO *h, char *buf, int size);
static int my_sock_write(BIO *h, const char *buf, int size);
@ -93,6 +94,7 @@ static long win32_ssl_create_mutex = 0;
#endif
#endif /* ENABLE_THREAD_SAFETY */
static PQsslKeyPassHook_type PQsslKeyPassHook = NULL;
/* ------------------------------------------------------------ */
/* Procedures common to all secure sessions */
@ -818,6 +820,26 @@ initialize_SSL(PGconn *conn)
return -1;
}
/*
* Delegate the client cert password prompt to the libpq wrapper
* callback if any is defined.
*
* If the application hasn't installed its own and the sslpassword
* parameter is non-null, we install ours now to make sure we
* supply PGconn->sslpassword to OpenSSL instead of letting it
* prompt on stdin.
*
* This will replace OpenSSL's default PEM_def_callback (which
* prompts on stdin), but we're only setting it for this SSL
* context so it's harmless.
*/
if (PQsslKeyPassHook
|| (conn->sslpassword && strlen(conn->sslpassword) > 0))
{
SSL_CTX_set_default_passwd_cb(SSL_context, PQssl_passwd_cb);
SSL_CTX_set_default_passwd_cb_userdata(SSL_context, conn);
}
/* Disable old protocol versions */
SSL_CTX_set_options(SSL_context, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
@ -1123,11 +1145,29 @@ initialize_SSL(PGconn *conn)
{
char *err = SSLerrmessage(ERR_get_error());
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not load private key file \"%s\": %s\n"),
fnbuf, err);
/*
* We'll try to load the file in DER (binary ASN.1) format, and if
* that fails too, report the original error. This could mask
* issues where there's something wrong with a DER-format cert, but
* we'd have to duplicate openssl's format detection to be smarter
* than this. We can't just probe for a leading -----BEGIN because
* PEM can have leading non-matching lines and blanks. OpenSSL
* doesn't expose its get_name(...) and its PEM routines don't
* differentiate between failure modes in enough detail to let us
* tell the difference between "not PEM, try DER" and "wrong
* password".
*/
if (SSL_use_PrivateKey_file(conn->ssl, fnbuf, SSL_FILETYPE_ASN1) != 1)
{
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not load private key file \"%s\": %s\n"),
fnbuf, err);
SSLerrfree(err);
return -1;
}
SSLerrfree(err);
return -1;
}
}
@ -1580,3 +1620,54 @@ my_SSL_set_fd(PGconn *conn, int fd)
err:
return ret;
}
/*
* This is the default handler to return a client cert password from
* conn->sslpassword. Apps may install it explicitly if they want to
* prevent openssl from ever prompting on stdin.
*/
int
PQdefaultSSLKeyPassHook(char *buf, int size, PGconn *conn)
{
if (conn->sslpassword)
{
if (strlen(conn->sslpassword) + 1 > size)
fprintf(stderr, libpq_gettext("WARNING: sslpassword truncated"));
strncpy(buf, conn->sslpassword, size);
buf[size-1] = '\0';
return strlen(buf);
}
else
{
buf[0] = '\0';
return 0;
}
}
PQsslKeyPassHook_type
PQgetSSLKeyPassHook(void)
{
return PQsslKeyPassHook;
}
void
PQsetSSLKeyPassHook(PQsslKeyPassHook_type hook)
{
PQsslKeyPassHook = hook;
}
/*
* Supply a password to decrypt a client certificate.
*
* This must match OpenSSL type pem_passwd_cb.
*/
static int
PQssl_passwd_cb(char *buf, int size, int rwflag, void *userdata)
{
PGconn *conn = userdata;
if (PQsslKeyPassHook)
return PQsslKeyPassHook(buf, size, conn);
else
return PQdefaultSSLKeyPassHook(buf, size, conn);
}