--ELM724701686-5621-0_
Content-Type: text/plain; charset=US-ASCII
Content-Transfer-Encoding: 7bit
Attached are a revised set of SSL patches. Many of these patches
are motivated by security concerns, it's not just bug fixes. The key
differences (from stock 7.2.1) are:
*) almost all code that directly uses the OpenSSL library is in two
new files,
src/interfaces/libpq/fe-ssl.c
src/backend/postmaster/be-ssl.c
in the long run, it would be nice to merge these two files.
*) the legacy code to read and write network data have been
encapsulated into read_SSL() and write_SSL(). These functions
should probably be renamed - they handle both SSL and non-SSL
cases.
the remaining code should eliminate the problems identified
earlier, albeit not very cleanly.
*) both front- and back-ends will send a SSL shutdown via the
new close_SSL() function. This is necessary for sessions to
work properly.
(Sessions are not yet fully supported, but by cleanly closing
the SSL connection instead of just sending a TCP FIN packet
other SSL tools will be much happier.)
*) The client certificate and key are now expected in a subdirectory
of the user's home directory. Specifically,
- the directory .postgresql must be owned by the user, and
allow no access by 'group' or 'other.'
- the file .postgresql/postgresql.crt must be a regular file
owned by the user.
- the file .postgresql/postgresql.key must be a regular file
owned by the user, and allow no access by 'group' or 'other'.
At the current time encrypted private keys are not supported.
There should also be a way to support multiple client certs/keys.
*) the front-end performs minimal validation of the back-end cert.
Self-signed certs are permitted, but the common name *must*
match the hostname used by the front-end. (The cert itself
should always use a fully qualified domain name (FDQN) in its
common name field.)
This means that
psql -h eris db
will fail, but
psql -h eris.example.com db
will succeed. At the current time this must be an exact match;
future patches may support any FQDN that resolves to the address
returned by getpeername(2).
Another common "problem" is expiring certs. For now, it may be
a good idea to use a very-long-lived self-signed cert.
As a compile-time option, the front-end can specify a file
containing valid root certificates, but it is not yet required.
*) the back-end performs minimal validation of the client cert.
It allows self-signed certs. It checks for expiration. It
supports a compile-time option specifying a file containing
valid root certificates.
*) both front- and back-ends default to TLSv1, not SSLv3/SSLv2.
*) both front- and back-ends support DSA keys. DSA keys are
moderately more expensive on startup, but many people consider
them preferable than RSA keys. (E.g., SSH2 prefers DSA keys.)
*) if /dev/urandom exists, both client and server will read 16k
of randomization data from it.
*) the server can read empheral DH parameters from the files
$DataDir/dh512.pem
$DataDir/dh1024.pem
$DataDir/dh2048.pem
$DataDir/dh4096.pem
if none are provided, the server will default to hardcoded
parameter files provided by the OpenSSL project.
Remaining tasks:
*) the select() clauses need to be revisited - the SSL abstraction
layer may need to absorb more of the current code to avoid rare
deadlock conditions. This also touches on a true solution to
the pg_eof() problem.
*) the SIGPIPE signal handler may need to be revisited.
*) support encrypted private keys.
*) sessions are not yet fully supported. (SSL sessions can span
multiple "connections," and allow the client and server to avoid
costly renegotiations.)
*) makecert - a script that creates back-end certs.
*) pgkeygen - a tool that creates front-end certs.
*) the whole protocol issue, SASL, etc.
Bear
--ELM724701686-5621-0_
Content-Type: text/plain; charset=US-ASCII
Content-Disposition: attachment; filename=diff
Content-Description: /tmp/diff
Content-Transfer-Encoding: 7bit
--- postgresql-7.2.1.orig/src/include/libpq/libpq-be.h Sun Nov 11 22:43:25 2001
*/
#ifdef USE_SSL
SSL *ssl;
+ X509 *peer;
#endif
} Port;
--- postgresql-7.2.1.orig/src/interfaces/libpq/libpq-int.h Mon Nov 5 10:46:38 2001
bool allow_ssl_try; /* Allowed to try SSL negotiation */
bool require_ssl; /* Require SSL to make connection */
SSL *ssl; /* SSL status, if have SSL connection */
+ X509 *peer; /* server certificate */
#endif
/* Buffer for current error message */
--- postgresql-7.2.1.orig/src/backend/libpq/pqcomm.c Tue Dec 4 13:57:22 2001
#include "libpq/libpq.h"
#include "miscadmin.h"
+/* these functions are misnamed - they handle both SSL and non-SSL case */
+extern ssize_t read_SSL(Port *, void *ptr, size_t len);
+extern ssize_t write_SSL(Port *, const void *ptr, size_t len);
+
+#ifdef USE_SSL
+extern void close_SSL(Port *);
+#endif /* USE_SSL */
+
static void pq_close(void);
{
if (MyProcPort != NULL)
{
+#ifdef USE_SSL
+ close_SSL(MyProcPort);
+#endif /* USE_SSL */
close(MyProcPort->sock);
/* make sure any subsequent attempts to do I/O fail cleanly */
void
StreamClose(int sock)
{
+ /* FIXME - what about closing SSL connections? */
close(sock);
}
{
int r;
-#ifdef USE_SSL
- if (MyProcPort->ssl)
- r = SSL_read(MyProcPort->ssl, PqRecvBuffer + PqRecvLength,
- PQ_BUFFER_SIZE - PqRecvLength);
- else
-#endif
- r = recv(MyProcPort->sock, PqRecvBuffer + PqRecvLength,
- PQ_BUFFER_SIZE - PqRecvLength, 0);
+ r = read_SSL(MyProcPort, PqRecvBuffer + PqRecvLength,
+ PQ_BUFFER_SIZE - PqRecvLength);
if (r < 0)
elog(DEBUG, "pq_recvbuf: recv() failed: %m");
return EOF;
}
+#ifdef USE_SSL
+ if (r == 0 && !MyProcPort->ssl)
+#else /* USE_SSL */
if (r == 0)
+#endif /* USE_SSL */
{
/* as above, only write to postmaster log */
{
int r;
-#ifdef USE_SSL
- if (MyProcPort->ssl)
- r = SSL_write(MyProcPort->ssl, bufptr, bufend - bufptr);
- else
-#endif
- r = send(MyProcPort->sock, bufptr, bufend - bufptr, 0);
+ r = write_SSL(MyProcPort, bufptr, bufend - bufptr);
+#ifdef USE_SSL
+ if (r < 0 || (r == 0 && !MyProcPort->ssl))
+#else /* USE_SSL */
if (r <= 0)
+#endif /* USE_SSL */
{
if (errno == EINTR)
pq_eof(void)
{
char x;
- int res;
+ int res = 1;
+#ifndef USE_SSL /* not a good solution, but better than nothing */
res = recv(MyProcPort->sock, &x, 1, MSG_PEEK);
elog(DEBUG, "pq_eof: recv() failed: %m");
return EOF;
}
+#endif /* USE_SSL */
+
if (res == 0)
return EOF;
else
--- postgresql-7.2.1.orig/src/backend/postmaster/postmaster.c Fri Mar 15 12:20:47 2002
static int ServerSock_UNIX = INVALID_SOCK; /* stream socket server */
#endif
-#ifdef USE_SSL
-static SSL_CTX *SSL_context = NULL; /* Global SSL context */
-#endif
-
/*
* Set by the -o option
#define ShutdownDataBase() SSDataBase(BS_XLOG_SHUTDOWN)
#ifdef USE_SSL
-static void InitSSL(void);
-static const char *SSLerrmessage(void);
+extern int initialize_ctx(const char *, void (*err)(const char *fmt,...));
+extern void destroy_ctx(void);
+extern int open_SSL_server(Port *);
+extern void close_SSL(Port *);
#endif
ExitPostmaster(1);
}
if (EnableSSL)
- InitSSL();
+ {
+ if (initialize_ctx(NULL, postmaster_error) == -1)
+ ExitPostmaster(1);
+ }
#endif
#ifdef USE_SSL
if (SSLok == 'S')
{
- if (!(port->ssl = SSL_new(SSL_context)) ||
- !SSL_set_fd(port->ssl, port->sock) ||
- SSL_accept(port->ssl) <= 0)
+ if (open_SSL_server(port) != STATUS_OK)
{
- elog(DEBUG, "failed to initialize SSL connection: %s (%m)",
- SSLerrmessage());
return STATUS_ERROR;
}
ConnFree(Port *conn)
{
#ifdef USE_SSL
- if (conn->ssl)
- SSL_free(conn->ssl);
+ close_SSL(conn);
#endif
+ if (conn->sock != -1)
+ close(conn->sock);
free(conn);
}
return cnt;
}
-#ifdef USE_SSL
-
-/*
- * Initialize SSL library and structures
- */
-static void
-InitSSL(void)
-{
- char fnbuf[2048];
-
- SSL_load_error_strings();
- SSL_library_init();
- SSL_context = SSL_CTX_new(SSLv23_method());
- if (!SSL_context)
- {
- postmaster_error("failed to create SSL context: %s",
- SSLerrmessage());
- ExitPostmaster(1);
- }
- snprintf(fnbuf, sizeof(fnbuf), "%s/server.crt", DataDir);
- if (!SSL_CTX_use_certificate_file(SSL_context, fnbuf, SSL_FILETYPE_PEM))
- {
- postmaster_error("failed to load server certificate (%s): %s",
- fnbuf, SSLerrmessage());
- ExitPostmaster(1);
- }
- snprintf(fnbuf, sizeof(fnbuf), "%s/server.key", DataDir);
- if (!SSL_CTX_use_PrivateKey_file(SSL_context, fnbuf, SSL_FILETYPE_PEM))
- {
- postmaster_error("failed to load private key file (%s): %s",
- fnbuf, SSLerrmessage());
- ExitPostmaster(1);
- }
- if (!SSL_CTX_check_private_key(SSL_context))
- {
- postmaster_error("check of private key failed: %s",
- SSLerrmessage());
- ExitPostmaster(1);
- }
-}
-
-/*
- * Obtain reason string for last SSL error
- *
- * Some caution is needed here since ERR_reason_error_string will
- * return NULL if it doesn't recognize the error code. We don't
- * want to return NULL ever.
- */
-static const char *
-SSLerrmessage(void)
-{
- unsigned long errcode;
- const char *errreason;
- static char errbuf[32];
-
- errcode = ERR_get_error();
- if (errcode == 0)
- return "No SSL error reported";
- errreason = ERR_reason_error_string(errcode);
- if (errreason != NULL)
- return errreason;
- snprintf(errbuf, sizeof(errbuf), "SSL error code %lu", errcode);
- return errbuf;
-}
-
-#endif /* USE_SSL */
/*
* Fire off a subprocess for startup/shutdown/checkpoint.
--- postgresql-7.2.1.orig/src/bin/psql/startup.c Mon Nov 5 10:46:31 2001
{
int sslbits = -1;
SSL *ssl;
+ X509 *peer;
+ char sn[256];
+ long l;
ssl = PQgetssl(pset.db);
if (!ssl)
return; /* no SSL */
+/* peer = pset.db.peer; */
+ if ((peer = SSL_get_peer_certificate(ssl)) != NULL)
+ {
+ X509_NAME_oneline(X509_get_subject_name(peer), sn, sizeof sn);
+ }
+ else
+ {
+ strncpy(sn, "(anonymous)", sizeof sn);
+ }
+ printf(gettext("SSL connection\n"));
+ printf(gettext("(host: %s)\n"), sn);
+
SSL_get_cipher_bits(ssl, &sslbits);
- printf(gettext("SSL connection (cipher: %s, bits: %i)\n\n"),
+ printf(gettext("(protocol: %s)\n"), SSL_get_version(ssl)),
+ printf(gettext("(cipher: %s, bits: %i)\n"),
SSL_get_cipher(ssl), sslbits);
+ l = SSL_get_default_timeout(ssl);
+ printf(gettext("(timeout: %ld:%02ld:%02ld)\n\n"),
+ l / 3600L, (l / 60L) % 60L, l % 60L);
}
#endif
--- postgresql-7.2.1.orig/src/interfaces/libpq/fe-connect.c Sat Nov 10 19:09:05 2001
#endif
-#ifdef USE_SSL
-static SSL_CTX *SSL_context = NULL;
-#endif
-
#define NOTIFYLIST_INITIAL_SIZE 10
#define NOTIFYLIST_GROWBY 10
static void defaultNoticeProcessor(void *arg, const char *message);
static int parseServiceInfo(PQconninfoOption *options,
PQExpBuffer errorMessage);
+
#ifdef USE_SSL
-static const char *SSLerrmessage(void);
+extern int initialize_ctx(const char *passwd, void (*err)(const char *fmt,...), PGconn *);
+extern void destroy_ctx(PGconn *);
+extern int open_SSL_client(PGconn *);
+extern void close_SSL(PGconn *);
+extern SSL *PQgetssl(PGconn *);
#endif
}
if (SSLok == 'S')
{
- if (!SSL_context)
- {
- SSL_load_error_strings();
- SSL_library_init();
- SSL_context = SSL_CTX_new(SSLv23_method());
- if (!SSL_context)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not create SSL context: %s\n"),
- SSLerrmessage());
- goto connect_errReturn;
- }
- }
- if (!(conn->ssl = SSL_new(SSL_context)) ||
- !SSL_set_fd(conn->ssl, conn->sock) ||
- SSL_connect(conn->ssl) <= 0)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not establish SSL connection: %s\n"),
- SSLerrmessage());
+ if (initialize_ctx(NULL, NULL, conn) == -1)
+ goto connect_errReturn;
+ if (open_SSL_client(conn) == -1)
goto connect_errReturn;
- }
/* SSL connection finished. Continue to send startup packet */
}
goto connect_errReturn;
}
}
- if (conn->require_ssl && !conn->ssl)
+ if (conn->require_ssl && !PQgetssl(conn))
{
/* Require SSL, but server does not support/want it */
return;
pqClearAsyncResult(conn); /* deallocate result and curTuple */
#ifdef USE_SSL
- if (conn->ssl)
- SSL_free(conn->ssl);
+ close_SSL(conn);
#endif
if (conn->sock >= 0)
}
-#ifdef USE_SSL
-
-/*
- * Obtain reason string for last SSL error
- *
- * Some caution is needed here since ERR_reason_error_string will
- * return NULL if it doesn't recognize the error code. We don't
- * want to return NULL ever.
- */
-static const char *
-SSLerrmessage(void)
-{
- unsigned long errcode;
- const char *errreason;
- static char errbuf[32];
-
- errcode = ERR_get_error();
- if (errcode == 0)
- return "No SSL error reported";
- errreason = ERR_reason_error_string(errcode);
- if (errreason != NULL)
- return errreason;
- snprintf(errbuf, sizeof(errbuf), "SSL error code %lu", errcode);
- return errbuf;
-}
-
-#endif /* USE_SSL */
-
-
/* =========== accessor functions for PGconn ========= */
char *
PQsetClientEncoding(PGconn *conn, const char *encoding)
{
return -1;
-}
-#endif
-
-#ifdef USE_SSL
-SSL *
-PQgetssl(PGconn *conn)
-{
- if (!conn)
- return NULL;
- return conn->ssl;
}
#endif
--- postgresql-7.2.1.orig/src/interfaces/libpq/fe-misc.c Sun Dec 2 17:28:24 2001
#include "mb/pg_wchar.h"
#endif
+/* these functions are misnamed - they handle both SSL and non-SSL case */
+extern ssize_t read_SSL (PGconn *, void *ptr, size_t);
+extern ssize_t write_SSL (PGconn *, const void *ptr, size_t);
+
+#ifdef USE_SSL
+extern ssize_t close_SSL (PGconn *);
+#endif
#define DONOTICE(conn,message) \
/* OK, try to read some data */
tryAgain:
-#ifdef USE_SSL
- if (conn->ssl)
- nread = SSL_read(conn->ssl, conn->inBuffer + conn->inEnd,
- conn->inBufSize - conn->inEnd);
- else
-#endif
- nread = recv(conn->sock, conn->inBuffer + conn->inEnd,
- conn->inBufSize - conn->inEnd, 0);
+ nread = read_SSL(conn, conn->inBuffer + conn->inEnd,
+ conn->inBufSize - conn->inEnd);
if (nread < 0)
{
* arrived.
*/
tryAgain2:
-#ifdef USE_SSL
- if (conn->ssl)
- nread = SSL_read(conn->ssl, conn->inBuffer + conn->inEnd,
- conn->inBufSize - conn->inEnd);
- else
-#endif
- nread = recv(conn->sock, conn->inBuffer + conn->inEnd,
- conn->inBufSize - conn->inEnd, 0);
+ nread = read_SSL(conn, conn->inBuffer + conn->inEnd,
+ conn->inBufSize - conn->inEnd);
if (nread < 0)
{
"\tThis probably means the server terminated abnormally\n"
"\tbefore or while processing the request.\n"));
conn->status = CONNECTION_BAD; /* No more connection to backend */
+#ifdef USE_SSL
+ close_SSL(conn);
+#endif
#ifdef WIN32
closesocket(conn->sock);
/* while there's still data to send */
while (len > 0)
{
- /* Prevent being SIGPIPEd if backend has closed the connection. */
-#ifndef WIN32
- pqsigfunc oldsighandler = pqsignal(SIGPIPE, SIG_IGN);
-#endif
-
int sent;
-#ifdef USE_SSL
- if (conn->ssl)
- sent = SSL_write(conn->ssl, ptr, len);
- else
-#endif
- sent = send(conn->sock, ptr, len, 0);
-
-#ifndef WIN32
- pqsignal(SIGPIPE, oldsighandler);
-#endif
+ sent = write_SSL(conn, ptr, len);
if (sent < 0)
*/
#ifdef USE_SSL
/* can't do anything for our SSL users yet */
- if (conn->ssl == NULL)
+ if (PQgetssl(conn) == NULL)
{
#endif
if (pqIsnonblocking(conn))
--ELM724701686-5621-0_
Content-Type: text/plain; charset=US-ASCII
Content-Disposition: attachment; filename=be-ssl.c
Content-Description: /tmp/be-ssl.c
Content-Transfer-Encoding: 7bit
#include "postgres.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
#include <sys/file.h>
#include "libpq/libpq.h"
#include "libpq/pqsignal.h"
#include "miscadmin.h"
#ifdef USE_SSL
#include <openssl/ssl.h>
#include <openssl/rand.h>
#include <openssl/e_os.h>
#endif
#ifdef USE_SSL
ssize_t read_SSL(Port *, void *, size_t len);
ssize_t write_SSL(Port *, const void *, size_t len);
int initialize_ctx(const char *, void (*err)(const char *fmt,...));
void destroy_ctx(void);
int open_SSL_server(Port *);
void close_SSL (Port *);
static int password_cb();
static const char * SSLerrmessage(void);
static void info_cb(SSL *ssl, int type, int args);
static int verify_cb(int ok, X509_STORE_CTX *ctx);
static DH *tmp_dh_cb(SSL *, int, int);
#endif
#define PING() fprintf(stderr, "%s, line %d, %s\n", __FILE__, __LINE__, __func__)
#ifdef USE_SSL
static SSL_CTX *ctx = NULL;
static int postmaster_session_id_context = 1; /* anything will do */
/*
* These are the N-bit DH parameters from "Assigned Number for
* SKIP Protocols." ( http://www.***.com/ ).
* See there for how they were generated.
*/
static const char file_dh512[] =
"-----BEGIN DH PARAMETERS-----\n\
MEYCQQD1Kv884bEpQBgRjXyEpwpy1obEAxnIByl6ypUM2Zafq9AKUJsCRtMIPWak\n\
XUGfnHy9iUsiGSa6q6Jew1XpKgVfAgEC\n\
-----END DH PARAMETERS-----\n";
static const char file_dh1024[] =
"-----BEGIN DH PARAMETERS-----\n\
MIGHAoGBAPSI/VhOSdvNILSd5JEHNmszbDgNRR0PfIizHHxbLY7288kjwEPwpVsY\n\
jY67VYy4XTjTNP18F1dDox0YbN4zISy1Kv884bEpQBgRjXyEpwpy1obEAxnIByl6\n\
ypUM2Zafq9AKUJsCRtMIPWakXUGfnHy9iUsiGSa6q6Jew1XpL3jHAgEC\n\
-----END DH PARAMETERS-----\n";
static const char file_dh2048[] =
"-----BEGIN DH PARAMETERS-----\n\
MIIBCAKCAQEA9kJXtwh/CBdyorrWqULzBej5UxE5T7bxbrlLOCDaAadWoxTpj0BV\n\
89AHxstDqZSt90xkhkn4DIO9ZekX1KHTUPj1WV/cdlJPPT2N286Z4VeSWc39uK50\n\
T8X8dryDxUcwYc58yWb/Ffm7/ZFexwGq01uejaClcjrUGvC/RgBYK+X0iP1YTknb\n\
zSC0neSRBzZrM2w4DUUdD3yIsxx8Wy2O9vPJI8BD8KVbGI2Ou1WMuF040zT9fBdX\n\
Q6MdGGzeMyEstSr/POGxKUAYEY18hKcKctaGxAMZyAcpesqVDNmWn6vQClCbAkbT\n\
CD1mpF1Bn5x8vYlLIhkmuquiXsNV6TILOwIBAg==\n\
-----END DH PARAMETERS-----\n";
static const char file_dh4096[] =
"-----BEGIN DH PARAMETERS-----\n\
MIICCAKCAgEA+hRyUsFN4VpJ1O8JLcCo/VWr19k3BCgJ4uk+d+KhehjdRqNDNyOQ\n\
l/MOyQNQfWXPeGKmOmIig6Ev/nm6Nf9Z2B1h3R4hExf+zTiHnvVPeRBhjdQi81rt\n\
Xeoh6TNrSBIKIHfUJWBh3va0TxxjQIs6IZOLeVNRLMqzeylWqMf49HsIXqbcokUS\n\
Vt1BkvLdW48j8PPv5DsKRN3tloTxqDJGo9tKvj1Fuk74A+Xda1kNhB7KFlqMyN98\n\
VETEJ6c7KpfOo30mnK30wqw3S8OtaIR/maYX72tGOno2ehFDkq3pnPtEbD2CScxc\n\
alJC+EL7RPk5c/tgeTvCngvc1KZn92Y//EI7G9tPZtylj2b56sHtMftIoYJ9+ODM\n\
sccD5Piz/rejE3Ome8EOOceUSCYAhXn8b3qvxVI1ddd1pED6FHRhFvLrZxFvBEM9\n\
ERRMp5QqOaHJkM+Dxv8Cj6MqrCbfC4u+ZErxodzuusgDgvZiLF22uxMZbobFWyte\n\
OvOzKGtwcTqO/1wV5gKkzu1ZVswVUQd5Gg8lJicwqRWyyNRczDDoG9jVDxmogKTH\n\
AaqLulO7R8Ifa1SwF2DteSGVtgWEN8gDpN3RBmmPTDngyF2DHb5qmpnznwtFKdTL\n\
KWbuHn491xNO25CQWMtem80uKw+pTnisBRF/454n1Jnhub144YRBoN8CAQI=\n\
-----END DH PARAMETERS-----\n";
#endif /* USE_SSL */
/*
* Read data from network.
*/
ssize_t read_SSL (Port *port, void *ptr, size_t len)
{
ssize_t n;
#ifdef USE_SSL
if (port->ssl)
{
n = SSL_read(port->ssl, ptr, len);
switch (SSL_get_error(port->ssl, n))
{
case SSL_ERROR_NONE:
break;
case SSL_ERROR_WANT_READ:
break;
case SSL_ERROR_SYSCALL:
elog(ERROR, "SSL SYSCALL error");
errno = get_last_socket_error();
break;
case SSL_ERROR_SSL:
elog(ERROR, "SSL error: %s", SSLerrmessage());
errno = ECONNRESET;
break;
case SSL_ERROR_ZERO_RETURN:
elog(DEBUG, "SSL shutdown by peer");
errno = ECONNRESET;
break;
}
}
else
#endif /* USE_SSL */
n = recv(port->sock, ptr, len, 0);
return n;
Quote:
}
/*
* Write data to network.
*/
ssize_t write_SSL (Port *port, const void *ptr, size_t len)
{
ssize_t n;
/* prevent being SIGPIPEd if frontend has closed the connection. */
#ifndef WIN32
pqsigfunc oldsighandler = pqsignal(SIGPIPE, SIG_IGN);
#endif
#ifdef USE_SSL
if (port->ssl)
{
n = SSL_write(port->ssl, ptr, len);
switch (SSL_get_error(port->ssl, n))
{
case SSL_ERROR_NONE:
break;
case SSL_ERROR_WANT_WRITE:
break;
case SSL_ERROR_SYSCALL:
errno = get_last_socket_error();
break;
case SSL_ERROR_SSL:
elog(ERROR, "SSL error: %s", SSLerrmessage());
errno = ECONNRESET;
break;
case SSL_ERROR_ZERO_RETURN:
elog(DEBUG, "SSL shutdown by peer");
errno = ECONNRESET;
break;
}
}
else
#endif /* USE_SSL */
n = send(port->sock, ptr, len, 0);
#ifndef WIN32
pqsignal(SIGPIPE, oldsighandler);
#endif
return n;
Quote:
}
#ifdef USE_SSL
/*
* Callback used by SSL to provide information messages.
*/
static void
info_cb (SSL *ssl, int type, int args)
{
switch (type)
{
case SSL_CB_HANDSHAKE_START:
elog(DEBUG, "SSL: Handshake start");
break;
case SSL_CB_HANDSHAKE_DONE:
elog(DEBUG, "SSL: Handshake done");
break;
case SSL_CB_ACCEPT_LOOP:
if (DebugLvl >= 3)
elog(DEBUG, "SSL: Accept loop...");
break;
case SSL_CB_ACCEPT_EXIT:
elog(DEBUG, "SSL: Accept exit (%d)", args);
break;
case SSL_CB_CONNECT_LOOP:
elog(DEBUG, "SSL: Connect loop...");
break;
case SSL_CB_CONNECT_EXIT:
elog(DEBUG, "SSL: Connect exit (%d)", args);
break;
case SSL_CB_READ_ALERT:
elog(DEBUG, "SSL: Read alert (0x%04x)", args);
break;
case SSL_CB_WRITE_ALERT:
elog(DEBUG, "SSL: Write alert (0x%04x)", args);
break;
}
Quote:
}
/*
* Use a password specified via the ctx default userdata field.
* Clear it once it's been used.
*
* returns -1 on error, 0 on no data, or length of password.
*/
static int
password_cb (char *buf, int size, int rwflag, void *userdata)
{
int n;
char *pass = ctx->default_passwd_callback_userdata;
if (pass == NULL)
return 0;
n = strlen(pass);
if (n > size)
n = size;
strncpy(buf, pass, n);
SSL_CTX_set_default_passwd_cb_userdata(ctx, NULL);
return n;
Quote:
}
/*
* Null authentication callback
*/
static int
verify_cb (int ok, X509_STORE_CTX *ctx)
{
char sn[256], buf[256];
X509 *cert;
int err, depth, n;
BIO *bio;
cert = X509_STORE_CTX_get_current_cert(ctx);
err = X509_STORE_CTX_get_error(ctx);
depth= X509_STORE_CTX_get_error_depth(ctx);
X509_NAME_oneline(X509_get_subject_name(cert), sn, sizeof sn);
if (!ok)
{
switch (err)
{
/* accept self-signed certs */
case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
ok = 1;
break;
default:
elog(ERROR, "client cert %s: %s", sn,
X509_verify_cert_error_string(err));
}
}
switch (ctx->error)
{
case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
X509_NAME_oneline(X509_get_issuer_name(cert), buf, sizeof buf);
elog(DEBUG, "client cert %s: cannot find issuer %s", sn, buf);
break;
case X509_V_ERR_CERT_NOT_YET_VALID:
case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
bio = BIO_new(BIO_s_mem());
ASN1_TIME_print(bio, X509_get_notBefore(cert));
BIO_flush(bio);
n = BIO_read(bio, buf, sizeof buf - 1);
buf[n] = '\0';
elog(DEBUG, "client cert %s: not valid until %s", sn, buf);
break;
case X509_V_ERR_CERT_HAS_EXPIRED:
case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
bio = BIO_new(BIO_s_mem());
ASN1_TIME_print(bio, X509_get_notAfter(cert));
BIO_flush(bio);
n = BIO_read(bio, buf, sizeof buf - 1);
buf[n] = '\0';
elog(DEBUG, "client cert %s: not valid after %s", sn, buf);
break;
}
return ok;
Quote:
}
/*
* Load the DH parameters file, if it exists.
*/
static DH *load_dh_param(const char *filename)
{
FILE *fp;
DH *dh = NULL;
if ((fp = fopen(filename, "r")) == NULL)
return NULL;
flock(fileno(fp), LOCK_SH);
dh = PEM_read_DHparams(fp, NULL, NULL, NULL);
flock(fileno(fp), LOCK_UN);
fclose(fp);
return dh;
Quote:
}
/*
* Load the DH parameters buffer.
*/
static DH *load_dh_param_buffer(const char *buffer, size_t len)
{
BIO *bio;
DH *dh = NULL;
bio = BIO_new_mem_buf((char *) buffer, len);
if (bio == NULL)
return NULL;
dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
BIO_free(bio);
return dh;
Quote:
}
/*
* Generate an empheral DH key. Because this can take a long
* time to compute, we can use precomputed parameters of the
* common key sizes. If the files do not exist, we fall back
* to the values provided by the OpenSSL project.
*/
static DH *tmp_dh_cb (SSL *s, int is_export, int keylength)
{
char fnbuf[2048];
static DH *dh512 = NULL;
static DH *dh1024 = NULL;
static DH *dh2048 = NULL;
static DH *dh4096 = NULL;
static DH *dh = NULL;
static DH *r = NULL;
switch (keylength)
{
case 512:
if (dh512 == NULL)
{
snprintf(fnbuf, sizeof fnbuf, "%s/dh512.pem", DataDir);
dh512 = load_dh_param(fnbuf);
}
if (dh512 == NULL)
dh512 = load_dh_param_buffer(file_dh512, sizeof file_dh512);
r = dh512;
break;
case 1024:
if (dh1024 == NULL)
{
snprintf(fnbuf, sizeof fnbuf, "%s/dh1024.pem", DataDir);
dh1024 = load_dh_param(fnbuf);
}
if (dh1024 == NULL)
dh1024 = load_dh_param_buffer(file_dh1024, sizeof file_dh1024);
r = dh1024;
break;
case 2048:
if (dh2048 == NULL)
{
snprintf(fnbuf, sizeof fnbuf, "%s/dh2048.pem", DataDir);
dh2048 = load_dh_param(fnbuf);
}
if (dh2048 == NULL)
dh2048 = load_dh_param_buffer(file_dh2048, sizeof file_dh2048);
r = dh2048;
break;
case 4096:
if (dh4096 == NULL)
{
snprintf(fnbuf, sizeof fnbuf, "%s/dh4096.pem", DataDir);
dh4096 = load_dh_param(fnbuf);
}
if (dh4096 == NULL)
dh4096 = load_dh_param_buffer(file_dh4096, sizeof file_dh4096);
r = dh4096;
break;
default:
if (dh == NULL)
{
dh = DH_generate_parameters(keylength, 2, NULL, NULL);
r = dh;
}
}
return r;
Quote:
}
/*
* Initialize global SSL context.
*/
int
initialize_ctx (const char *password, void (*err)(const char *fmt,...))
{
SSL_METHOD *meth = NULL;
char fnbuf[2048];
struct stat buf;
int verify_mode = SSL_VERIFY_PEER;
if (!ctx) {
SSL_library_init();
SSL_load_error_strings();
// meth = SSLv23_method();
meth = TLSv1_method();
ctx = SSL_CTX_new(meth);
if (!ctx) {
err("failed to create SSL context: %s", SSLerrmessage());
return -1;
}
}
/* load our keys and certificate */
snprintf(fnbuf, sizeof fnbuf, "%s/%s", DataDir, "server.crt");
if (!SSL_CTX_use_certificate_file(ctx, fnbuf, SSL_FILETYPE_PEM))
{
err("failed to load server certificate (%s): %s",
fnbuf, SSLerrmessage());
return -1;
}
SSL_CTX_set_default_passwd_cb(ctx, password_cb);
SSL_CTX_set_default_passwd_cb_userdata(ctx, (void *) password);
snprintf(fnbuf, sizeof fnbuf, "%s/%s", DataDir, "server.key");
if (!SSL_CTX_use_PrivateKey_file(ctx, fnbuf, SSL_FILETYPE_PEM))
{
err("failed to private key file (%s): %s",
fnbuf, SSLerrmessage());
return -1;
}
if (!SSL_CTX_check_private_key(ctx))
{
err("check of private key failed: %s",
SSLerrmessage());
return -1;
}
/* load the CAs we trust */
#if defined(CA_LIST) || defined (CA_PATH)
if (!SSL_CTX_load_verify_locations(ctx, CA_LIST, CA_PATH))
{
err("failed to load verfication paths (%s, %s): %s\n",
CA_LIST, CA_PATH, SSLerrmessage());
return -1;
}
#endif
#if defined(CA_LIST)
if (CA_LIST != NULL)
SSL_CTX_set_client_CA_list(ctx, SSL_load_client_CA_file(CA_LIST));
#endif
SSL_CTX_set_verify(ctx, verify_mode, verify_cb);
/* load randomness */
#ifdef RANDOM
if (!RAND_load_file(RANDOM, 1024 * 1024))
{
err("failed to read randomness (%s): %s\n",
RANDOM, SSLerrmessage());
return -1;
}
#else /* RANDOM */
if (lstat("/dev/urandom", &buf) == 0 && S_ISCHR(buf.st_mode))
{
if (!RAND_load_file("/dev/urandom", 16 * 1024))
{
err("failed to read randomness (%s): %s\n",
"/dev/urandom", SSLerrmessage());
return -1;
}
}
#endif /* RANDOM */
/*
* Set up SSL context to use empheral DH keys
*/
SSL_CTX_set_tmp_dh_callback(ctx, tmp_dh_cb);
SSL_CTX_set_options(ctx, SSL_OP_SINGLE_DH_USE);
/*
* Set up SSL context to support sessions
*/
SSL_CTX_set_session_id_context(ctx,
(void *) &postmaster_session_id_context,
sizeof postmaster_session_id_context);
/*
* Set up debugging messages
*/
if (DebugLvl >= 2)
SSL_CTX_set_info_callback(ctx, info_cb);
return 0;
Quote:
}
/*
* Destroy the global SSL context.
*/
void destroy_ctx (void)
{
SSL_CTX_free(ctx);
ctx = NULL;
Quote:
}
/*
* Open SSL connection.
*/
int open_SSL_server (Port *port)
{
const char *cipher = NULL;
char buffer[256];
if (!(port->ssl = SSL_new(ctx)) ||
!SSL_set_fd(port->ssl, port->sock) ||
SSL_accept(port->ssl) <= 0)
{
elog(DEBUG, "failed to initialize SSL connection: %s (%m)",
SSLerrmessage());
return STATUS_ERROR;
}
/* if (context)
* {
* SSL_set_session_id_context(port->ssl, context,
* strlen((char *) context);
* }
*/
/* SSL_clear(port->ssl); */
/* SSL_set_accept_state(port->ssl); */
port->peer = SSL_get_peer_certificate(port->ssl);
if (port->peer == NULL)
strncpy(buffer, "(anonymous)", sizeof buffer);
else
{
X509_NAME_oneline(X509_get_subject_name(port->peer),
buffer, sizeof buffer);
}
cipher = SSL_CIPHER_get_name(SSL_get_current_cipher(port->ssl));
elog(DEBUG, "SSL connection from %s with cipher %s", buffer,
cipher != NULL ? cipher : "(NONE)");
if (SSL_ctrl(port->ssl, SSL_CTRL_GET_FLAGS, 0, NULL) &
TLS1_FLAGS_TLS_PADDING_BUG)
{
elog(ERROR, "Peer has incorrect TLSv1 block padding");
}
return STATUS_OK;
Quote:
}
/*
* Close SSL connection.
*/
void close_SSL (Port *port)
{
if (port->ssl)
{
SSL_shutdown(port->ssl);
SSL_free(port->ssl);
port->ssl = NULL;
}
Quote:
}
/*
* Obtain reason string for last SSL error
*
* Some caution is needed here since ERR_reason_error_string will
* return NULL if it doesn't recognize the error code. We don't
* want to return NULL ever.
*/
static const char *
SSLerrmessage(void)
{
unsigned long errcode;
const char *errreason;
static char errbuf[32];
errcode = ERR_get_error();
if (errcode == 0)
return "No SSL error reported";
errreason = ERR_reason_error_string(errcode);
if (errreason != NULL)
return errreason;
snprintf(errbuf, sizeof(errbuf), "SSL error code %lu", errcode);
return errbuf;
Quote:
}
#endif /* USE_SSL */
--ELM724701686-5621-0_
Content-Type: text/plain; charset=US-ASCII
Content-Disposition: attachment; filename=fe-ssl.c
Content-Description: /tmp/fe-ssl.c
Content-Transfer-Encoding: 7bit
#include "postgres_fe.h"
#include <sys/types.h>
#include <signal.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/stat.h>
#include <pwd.h>
#include "libpq-fe.h"
#include "libpq-int.h"
#include "fe-auth.h"
#include "pqsignal.h"
#ifdef WIN32
#include "win32.h"
#else
#include <sys/socket.h>
#include <unistd.h>
#include <netdb.h>
#include <netinet/in.h>
#ifdef HAVE_NETINET_TCP_H
#include <netinet/tcp.h>
#endif
#include <arpa/inet.h>
#endif
#ifdef USE_SSL
#include <openssl/ssl.h>
#include <openssl/rand.h>
#include <openssl/e_os.h>
int initialize_ctx(const char *, void (*err)(const char *fmt,...), PGconn *);
void destroy_ctx(void);
int open_SSL_client(PGconn *);
void close_SSL(PGconn *);
SSL PGgetssl(PGconn *);
static int clientCert_cb(SSL *ssl, X509 **x509, EVP_PKEY **pkey);
static int verify_cb(int, X509_STORE_CTX *);
static void info_cb(SSL *ssl, int type, int args);
static const char *SSLerrmessage(void);
#endif
ssize_t read_SSL(PGconn *, void *, size_t);
ssize_t write_SSL(PGconn *, const void *, size_t);
/* #define CA_LIST "root.pem" */
#ifdef USE_SSL
static SSL_CTX *ctx = NULL;
#endif
#define PING() fprintf(stderr,"%s, line %d, %s\n", __FILE__, __LINE__, __func__)
/*
* Read data from network.
*/
ssize_t read_SSL (PGconn *conn, void *ptr, size_t len)
{
ssize_t n;
#ifdef USE_SSL
if (conn->ssl)
{
n = SSL_read(conn->ssl, ptr, len);
switch (SSL_get_error(conn->ssl, n))
{
case SSL_ERROR_NONE:
break;
case SSL_ERROR_WANT_READ:
break;
case SSL_ERROR_SYSCALL:
SOCK_ERRNO = get_last_socket_error();
break;
case SSL_ERROR_SSL:
// log error...
SOCK_ERRNO = ECONNRESET;
break;
case SSL_ERROR_ZERO_RETURN:
SOCK_ERRNO = ECONNRESET;
break;
}
}
else
#endif /* USE_SSL */
n = recv(conn->sock, ptr, len, 0);
return n;
Quote:
}
/*
* Write data to network.
*/
ssize_t write_SSL (PGconn *conn, const void *ptr, size_t len)
{
ssize_t n;
/* prevent being SIGPIPEd if backend has closed the connection. */
#ifndef WIN32
pqsigfunc oldsighandler = pqsignal(SIGPIPE, SIG_IGN);
#endif
#ifdef USE_SSL
if (conn->ssl)
{
n = SSL_write(conn->ssl, ptr, len);
switch (SSL_get_error(conn->ssl, n))
{
case SSL_ERROR_NONE:
break;
case SSL_ERROR_WANT_WRITE:
break;
case SSL_ERROR_SYSCALL:
SOCK_ERRNO = get_last_socket_error();
break;
case SSL_ERROR_SSL:
fprintf(stderr, "ssl error\n");
// log error...
SOCK_ERRNO = ECONNRESET;
break;
case SSL_ERROR_ZERO_RETURN:
fprintf(stderr, "zero bytes\n");
SOCK_ERRNO = ECONNRESET;
break;
}
}
else
#endif
n = send(conn->sock, ptr, len, 0);
#ifndef WIN32
pqsignal(SIGPIPE, oldsighandler);
#endif
return n;
Quote:
}
#ifdef USE_SSL
/*
* Null authentication callback
*/
static int
verify_cb (int ok, X509_STORE_CTX *ctx)
{
char sn[256], buf[256];
X509 *cert;
int err, depth, n;
BIO *bio;
cert = X509_STORE_CTX_get_current_cert(ctx);
err = X509_STORE_CTX_get_error(ctx);
depth= X509_STORE_CTX_get_error_depth(ctx);
X509_NAME_oneline(X509_get_subject_name(cert), sn, sizeof sn);
if (!ok)
{
switch (err)
{
/* accept self-signed certs */
case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
ok = 1;
break;
default:
fprintf(stderr, "client cert %s: %s", sn,
X509_verify_cert_error_string(err));
}
}
switch (ctx->error)
{
case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
X509_NAME_oneline(X509_get_issuer_name(cert), buf, sizeof buf);
fprintf(stderr, "client cert %s: cannot find issuer %s", sn, buf);
break;
case X509_V_ERR_CERT_NOT_YET_VALID:
case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
bio = BIO_new(BIO_s_mem());
ASN1_TIME_print(bio, X509_get_notBefore(cert));
BIO_flush(bio);
n = BIO_read(bio, buf, sizeof buf - 1);
buf[n] = '\0';
fprintf(stderr, "client cert %s: not valid until %s", sn, buf);
break;
case X509_V_ERR_CERT_HAS_EXPIRED:
case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
bio = BIO_new(BIO_s_mem());
ASN1_TIME_print(bio, X509_get_notAfter(cert));
BIO_flush(bio);
n = BIO_read(bio, buf, sizeof buf - 1);
buf[n] = '\0';
fprintf(stderr, "client cert %s: not valid after %s\n", sn, buf);
break;
}
return ok;
Quote:
}
/*
* Callback used by SSL to provide information messages.
*/
static void
info_cb (SSL *ssl, int type, int args)
{
PGconn *conn = NULL;
conn = (PGconn *) SSL_get_app_data(ssl);
if (conn == NULL || conn->Pfdebug == NULL)
return;
switch (type)
{
case SSL_CB_HANDSHAKE_START:
fprintf(conn->Pfdebug, "Handshake start\n");
break;
case SSL_CB_HANDSHAKE_DONE:
fprintf(conn->Pfdebug, "Handshake done\n");
break;
case SSL_CB_ACCEPT_LOOP:
fprintf(conn->Pfdebug, "Accept loop...\n");
break;
case SSL_CB_ACCEPT_EXIT:
fprintf(conn->Pfdebug, "Accept exit (%d)\n", args);
break;
case SSL_CB_CONNECT_LOOP:
fprintf(conn->Pfdebug, "Connect loop...\n");
break;
case SSL_CB_CONNECT_EXIT:
fprintf(conn->Pfdebug, "Connect exit (%d)\n", args);
break;
case SSL_CB_READ_ALERT:
fprintf(conn->Pfdebug, "Read alert (0x%04x)\n", args);
break;
case SSL_CB_WRITE_ALERT:
fprintf(conn->Pfdebug, "Write alert (0x%04x)\n", args);
break;
}
Quote:
}
/*
* Callback used by SSL to load client cert and key.
* At the current time we require the cert and key to be
* located in the .postgresql directory under the user's
* home directory, and the files must be named 'postgresql.crt'
* and 'postgresql.key' respectively.
*
* returns 1 on success, 0 on no data, -1 on error.
*/
static int
clientCert_cb (SSL *ssl, X509 **x509, EVP_PKEY **pkey)
{
uid_t uid;
struct passwd *pwd;
char fnbuf[2048];
struct stat buf, buf1;
FILE *fp;
int (*cb)() = NULL;
if ((uid = getuid()) == -1)
{
fprintf(stderr, "can't get current uid\n");
return -1;
}
if ((pwd = getpwuid(uid)) == NULL || !pwd->pw_dir)
{
fprintf(stderr, "can't get passwd entry\n");
return -1;
}
/*
* if $HOME/.postgresql does not exist, 'no data' case.
* otherwise, it must be a directory, owned by current user,
* and not group- or world-accessible.
*/
snprintf(fnbuf, sizeof fnbuf, "%s/.postgresql", pwd->pw_dir);
if (lstat(fnbuf, &buf) == -1)
return 0;
if (!S_ISDIR(buf.st_mode) || buf.st_uid != uid ||
(buf.st_mode & (S_IRWXG | S_IRWXO)) != 0)
{
fprintf(stderr,
"$HOME/.postgresql directory has wrong ownership or permissions\n");
return -1;
}
/*
* make sure $HOME/.postgresql/postgresql.crt file exists,
* is regular file and owned by current user.
*/
snprintf(fnbuf, sizeof fnbuf, "%s/.postgresql/postgresql.crt",
pwd->pw_dir);
if (lstat(fnbuf, &buf) == -1)
return 0;
if (!S_ISREG(buf.st_mode) || buf.st_uid != uid)
{
fprintf(stderr,
"certificate file has wrong ownership or permissions\n");
return -1;
}
if ((fp = fopen(fnbuf, "r")) == NULL)
{
fprintf(stderr, "can't open certificate file (%s)\n", strerror(errno));
return -1;
}
if (PEM_read_X509(fp, x509, NULL, NULL) == NULL)
{
fprintf(stderr, "can't read certificate %s\n", SSLerrmessage());
fclose(fp);
return -1;
}
fclose(fp);
/*
* make sure $HOME/.postgresql/postgresql.key file exists,
* is regular file, owned by current user, and not group-
* or world-accessable.
*/
snprintf(fnbuf, sizeof fnbuf, "%s/.postgresql/postgresql.key",
pwd->pw_dir);
if (lstat(fnbuf, &buf) == -1)
{
fprintf(stderr, "certificate file exists, but no private key\n");
SSL_use_certificate(ssl, NULL);
return -1;
}
if (!S_ISREG(buf.st_mode) || buf.st_uid != uid ||
(buf.st_mode & (S_IRWXG | S_IRWXO)) != 0)
{
fprintf(stderr,
"private key file has wrong ownership or permissions\n");
SSL_use_certificate(ssl, NULL);
return -1;
}
if ((fp = fopen(fnbuf, "r")) == NULL)
{
fprintf(stderr, "error opening private key file: %s\n",
strerror(errno));
SSL_use_certificate(ssl, NULL);
return -1;
}
if (fstat(fileno(fp),&buf1) == -1 ||
buf.st_dev != buf1.st_dev || buf.st_ino != buf1.st_ino)
{
fprintf(stderr, "private key changed under us!\n");
fclose(fp);
SSL_use_certificate(ssl, NULL);
return -1;
}
if (PEM_read_PrivateKey(fp, pkey, cb, NULL) == NULL)
{
fprintf(stderr, "can't read private key %s\n", SSLerrmessage());
fclose(fp);
SSL_use_certificate(ssl, NULL);
return -1;
}
fclose(fp);
return 1;
Quote:
}
/*
* Initialize global SSL context.
*
* We want to use 'err' for errors, same as the corresponding
* function on the server, but for now we use legacy error handler
* in PGconn.
*/
int
initialize_ctx (const char *password,
void (*err)(const char * fmt,...), PGconn *conn)
{
SSL_METHOD *meth = NULL;
struct stat buf;
if (!ctx)
{
SSL_library_init();
SSL_load_error_strings();
// meth = SSLv23_method();
meth = TLSv1_method();
ctx = SSL_CTX_new(meth);
if (!ctx) {
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not create SSL context: %s\n"),
SSLerrmessage());
return -1;
}
}
/* load the CAs we trust */
#ifdef CA_LIST
if (!SSL_CTX_load_verify_locations(ctx, CA_LIST, 0))
{
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not read CA list (%s): %s\n"),
CA_LIST, SSLerrmessage());
return -1;
}
#endif
/* load randomness */
#ifdef RANDOM
if (!RAND_load_file(RANDOM, 1024 * 1024))
{
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not load randomness (%s): %s\n"),
RANDOM, SSLerrmessage());
return -1;
}
#else /* RANDOM */
if (lstat("/dev/urandom", &buf) == 0 && S_ISCHR(buf.st_mode))
{
if (!RAND_load_file("/dev/urandom", 16 * 1024))
{
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not load randomness (%s): %s\n"),
"/dev/urandom", SSLerrmessage());
return -1;
}
}
#endif /* RANDOM */
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, verify_cb);
SSL_CTX_set_verify_depth(ctx, 1);
SSL_CTX_set_info_callback(ctx, info_cb);
SSL_CTX_set_client_cert_cb(ctx, clientCert_cb);
return 0;
Quote:
}
/*
* Destroy the global SSL context.
*/
void destroy_ctx (void)
{
SSL_CTX_free(ctx);
ctx = NULL;
Quote:
}
/*
* Open a SSL connection.
*/
int
open_SSL_client (PGconn *conn)
{
char peerName[256];
const char *reason;
int r;
if (!(conn->ssl = SSL_new(ctx)) ||
!SSL_set_app_data(conn->ssl, conn) ||
!SSL_set_fd(conn->ssl, conn->sock) ||
SSL_connect(conn->ssl) <= 0)
{
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not establish SSL connection: %s\n"),
SSLerrmessage());
return -1;
}
/* check the certificate chain */
/* for now, we allow self-signed server certs */
r = SSL_get_verify_result(conn->ssl);
if (r != X509_V_OK && r != X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT)
{
switch (r)
{
case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
reason = "unable to get issuer cert";
break;
case X509_V_ERR_UNABLE_TO_GET_CRL:
reason = "unable to get CRL";
break;
case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE:
reason = "unable to decrypt cert signature";
break;
case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE:
reason = "unable to decrypt CRL signature";
break;
case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY:
reason = "unable to decode issuer public key";
break;
case X509_V_ERR_CERT_SIGNATURE_FAILURE:
reason = "cert signature failure";
break;
case X509_V_ERR_CRL_SIGNATURE_FAILURE:
reason = "CRL signature failure";
break;
case X509_V_ERR_CERT_NOT_YET_VALID:
reason = "cert is not yet valid";
break;
case X509_V_ERR_CERT_HAS_EXPIRED:
reason = "cert has expired";
break;
case X509_V_ERR_CRL_NOT_YET_VALID:
reason = "CRL not yet valid";
break;
case X509_V_ERR_CRL_HAS_EXPIRED:
reason = "CRL has expired";
break;
case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
reason = "error in cert notBefore field";
break;
case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
reason = "error in cert notAfter field";
break;
case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD:
reason = "error in CRL last update field";
break;
case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD:
reason = "error in CRL next update field";
break;
case X509_V_ERR_OUT_OF_MEM:
reason = "out of memory";
break;
case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
reason = "depth zero self-signed cert";
break;
case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
reason = "self-signed cert in chain";
break;
case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
reason = "unable to get issuer cert locally";
break;
case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
reason = "unable to verify leaf signature";
break;
case X509_V_ERR_CERT_CHAIN_TOO_LONG:
reason = "cert chain too long";
break;
case X509_V_ERR_CERT_REVOKED:
reason = "cert revoked";
break;
case X509_V_ERR_INVALID_CA:
reason = "invalid CA";
break;
case X509_V_ERR_PATH_LENGTH_EXCEEDED:
reason = "path length exceeded";
break;
case X509_V_ERR_INVALID_PURPOSE:
reason = "invalid purpose";
break;
case X509_V_ERR_CERT_UNTRUSTED:
reason = "cert untrusted";
break;
case X509_V_ERR_CERT_REJECTED:
reason = "cert rejected";
break;
/* These are 'informational' when looking for issuer cert */
case X509_V_ERR_SUBJECT_ISSUER_MISMATCH:
reason = "cert issuer/issuer subject mismatch";
break;
case X509_V_ERR_AKID_SKID_MISMATCH:
reason = "cert akid/issuer skid mismatch";
break;
case X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH:
reason = "cert akid/issuer serial mismatch";
break;
case X509_V_ERR_KEYUSAGE_NO_CERTSIGN:
reason = "keyusage no certsign";
break;
/* The application is not happy */
case X509_V_ERR_APPLICATION_VERIFICATION:
reason = "application-specific verification error";
break;
default:
reason = "unknown reason";
}
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("certificate could not be verified: %s (%d)\n"),
reason, r);
return -1;
}
/* check the common name */
conn->peer = SSL_get_peer_certificate(conn->ssl);
X509_NAME_get_text_by_NID(X509_get_subject_name(conn->peer),
NID_commonName, peerName, sizeof peerName);
if (strcasecmp(peerName, conn->pghost) != 0) {
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("certificate name does not match hostname\n"));
return -1;
}
return 0;
Quote:
}
/*
* Close a SSL connection.
*/
void
close_SSL (PGconn *conn)
{
if (conn->ssl)
{
SSL_shutdown(conn->ssl);
SSL_free(conn->ssl);
conn->ssl = NULL;
}
Quote:
}
/*
* Accessor function that retrieves SSL connection pointer.
*/
SSL *
PQgetssl (PGconn *conn)
{
if (!conn)
return NULL;
return conn->ssl;
Quote:
}
/*
* Obtain reason string for last SSL error
*
* Some caution is needed here since ERR_reason_error_string will
* return NULL if it doesn't recognize the error code. We don't
* want to return NULL ever.
*/
static const char *
SSLerrmessage(void)
{
unsigned long errcode;
const char *errreason;
static char errbuf[32];
errcode = ERR_get_error();
if (errcode == 0)
return "No SSL error reported";
errreason = ERR_reason_error_string(errcode);
if (errreason != NULL)
return errreason;
snprintf(errbuf, sizeof(errbuf), "SSL error code %lu", errcode);
return errbuf;
Quote:
}
#endif /* USE_SSL */
--ELM724701686-5621-0_
Content-Type: text/plain
Content-Disposition: inline
Content-Transfer-Encoding: 8bit
MIME-Version: 1.0
---------------------------(end of broadcast)---------------------------
TIP 2: you can get off all lists at once with the unregister command
--ELM724701686-5621-0_--