Back out SSL changes. Newer patch available.

This commit is contained in:
Bruce Momjian 2002-06-14 04:09:37 +00:00
parent a9bd17616e
commit eb43af3210
10 changed files with 208 additions and 886 deletions

View File

@ -4,7 +4,7 @@
# Makefile for libpq subsystem (backend half of libpq interface)
#
# IDENTIFICATION
# $Header: /cvsroot/pgsql/src/backend/libpq/Makefile,v 1.31 2002/06/14 03:56:46 momjian Exp $
# $Header: /cvsroot/pgsql/src/backend/libpq/Makefile,v 1.32 2002/06/14 04:09:36 momjian Exp $
#
#-------------------------------------------------------------------------
@ -14,8 +14,7 @@ include $(top_builddir)/src/Makefile.global
# be-fsstubs is here for historical reasons, probably belongs elsewhere
OBJS = be-fsstubs.o be-ssl.o auth.o crypt.o hba.o md5.o pqcomm.o \
pqformat.o pqsignal.o
OBJS = be-fsstubs.o auth.o crypt.o hba.o md5.o pqcomm.o pqformat.o pqsignal.o
all: SUBSYS.o

View File

@ -29,7 +29,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: pqcomm.c,v 1.134 2002/06/14 03:56:46 momjian Exp $
* $Id: pqcomm.c,v 1.135 2002/06/14 04:09:36 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -81,14 +81,6 @@
#include "miscadmin.h"
#include "storage/ipc.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);
@ -146,9 +138,6 @@ 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 */
MyProcPort->sock = -1;
@ -427,7 +416,6 @@ StreamConnection(int server_fd, Port *port)
void
StreamClose(int sock)
{
/* FIXME - what about closing SSL connections? */
close(sock);
}
@ -469,8 +457,14 @@ pq_recvbuf(void)
{
int r;
r = read_SSL(MyProcPort, PqRecvBuffer + PqRecvLength,
PQ_BUFFER_SIZE - PqRecvLength);
#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);
if (r < 0)
{
@ -486,11 +480,7 @@ pq_recvbuf(void)
elog(COMMERROR, "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 */
elog(COMMERROR, "pq_recvbuf: unexpected EOF on client connection");
@ -661,13 +651,14 @@ pq_flush(void)
{
int r;
r = write_SSL(MyProcPort, bufptr, bufend - bufptr);
#ifdef USE_SSL
if (r < 0 || (r == 0 && !MyProcPort->ssl))
#else /* USE_SSL */
if (MyProcPort->ssl)
r = SSL_write(MyProcPort->ssl, bufptr, bufend - bufptr);
else
#endif
r = send(MyProcPort->sock, bufptr, bufend - bufptr, 0);
if (r <= 0)
#endif /* USE_SSL */
{
if (errno == EINTR)
continue; /* Ok if we were interrupted */
@ -712,9 +703,8 @@ int
pq_eof(void)
{
char x;
int res = 1;
int res;
#ifndef USE_SSL /* not a good solution, but better than nothing */
res = recv(MyProcPort->sock, &x, 1, MSG_PEEK);
if (res < 0)
@ -723,8 +713,6 @@ pq_eof(void)
elog(COMMERROR, "pq_eof: recv() failed: %m");
return EOF;
}
#endif /* USE_SSL */
if (res == 0)
return EOF;
else

View File

@ -37,7 +37,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.277 2002/06/14 03:56:47 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.278 2002/06/14 04:09:36 momjian Exp $
*
* NOTES
*
@ -165,6 +165,10 @@ static int ServerSock_INET = INVALID_SOCK; /* stream socket server */
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
*/
@ -270,10 +274,8 @@ __attribute__((format(printf, 1, 2)));
#define ShutdownDataBase() SSDataBase(BS_XLOG_SHUTDOWN)
#ifdef USE_SSL
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 *);
static void InitSSL(void);
static const char *SSLerrmessage(void);
#endif
@ -607,10 +609,7 @@ PostmasterMain(int argc, char *argv[])
ExitPostmaster(1);
}
if (EnableSSL)
{
if (initialize_ctx(NULL, postmaster_error) == -1)
ExitPostmaster(1);
}
InitSSL();
#endif
/*
@ -1115,9 +1114,13 @@ ProcessStartupPacket(Port *port, bool SSLdone)
#ifdef USE_SSL
if (SSLok == 'S')
{
if (open_SSL_server(port) != STATUS_OK)
{
if (!(port->ssl = SSL_new(SSL_context)) ||
!SSL_set_fd(port->ssl, port->sock) ||
SSL_accept(port->ssl) <= 0)
{
elog(LOG, "failed to initialize SSL connection: %s (%m)",
SSLerrmessage());
return STATUS_ERROR;
}
}
@ -1319,10 +1322,9 @@ static void
ConnFree(Port *conn)
{
#ifdef USE_SSL
close_SSL(conn);
if (conn->ssl)
SSL_free(conn->ssl);
#endif
if (conn->sock != -1)
close(conn->sock);
free(conn);
}
@ -2422,6 +2424,72 @@ CountChildren(void)
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.

View File

@ -3,7 +3,7 @@
*
* Copyright 2000 by PostgreSQL Global Development Group
*
* $Header: /cvsroot/pgsql/src/bin/psql/startup.c,v 1.58 2002/06/14 03:56:47 momjian Exp $
* $Header: /cvsroot/pgsql/src/bin/psql/startup.c,v 1.59 2002/06/14 04:09:36 momjian Exp $
*/
#include "postgres_fe.h"
@ -678,33 +678,14 @@ printSSLInfo(void)
{
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("(protocol: %s)\n"), SSL_get_version(ssl)),
printf(gettext("(cipher: %s, bits: %i)\n"),
printf(gettext("SSL connection (cipher: %s, bits: %i)\n\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

View File

@ -11,7 +11,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: libpq-be.h,v 1.28 2002/06/14 03:56:47 momjian Exp $
* $Id: libpq-be.h,v 1.29 2002/06/14 04:09:37 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -70,7 +70,6 @@ typedef struct Port
*/
#ifdef USE_SSL
SSL *ssl;
X509 *peer;
#endif
} Port;

View File

@ -4,7 +4,7 @@
#
# Copyright (c) 1994, Regents of the University of California
#
# $Header: /cvsroot/pgsql/src/interfaces/libpq/Makefile,v 1.60 2002/06/14 03:56:47 momjian Exp $
# $Header: /cvsroot/pgsql/src/interfaces/libpq/Makefile,v 1.61 2002/06/14 04:09:37 momjian Exp $
#
#-------------------------------------------------------------------------
@ -20,7 +20,7 @@ SO_MINOR_VERSION= 2
override CPPFLAGS := -I$(srcdir) $(CPPFLAGS) -DFRONTEND -DSYSCONFDIR='"$(sysconfdir)"'
OBJS= fe-auth.o fe-connect.o fe-exec.o fe-misc.o fe-print.o fe-lobj.o \
pqexpbuffer.o dllist.o md5.o pqsignal.o fe-ssl.o \
pqexpbuffer.o dllist.o md5.o pqsignal.o \
$(INET_ATON) $(SNPRINTF) $(STRERROR)
ifdef MULTIBYTE

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.184 2002/06/14 03:56:47 momjian Exp $
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.185 2002/06/14 04:09:37 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -62,6 +62,10 @@ inet_aton(const char *cp, struct in_addr * inp)
#endif
#ifdef USE_SSL
static SSL_CTX *SSL_context = NULL;
#endif
#define NOTIFYLIST_INITIAL_SIZE 10
#define NOTIFYLIST_GROWBY 10
@ -182,13 +186,8 @@ static char *conninfo_getval(PQconninfoOption *connOptions,
static void defaultNoticeProcessor(void *arg, const char *message);
static int parseServiceInfo(PQconninfoOption *options,
PQExpBuffer errorMessage);
#ifdef USE_SSL
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 *);
static const char *SSLerrmessage(void);
#endif
@ -970,10 +969,28 @@ retry2:
}
if (SSLok == 'S')
{
if (initialize_ctx(NULL, NULL, conn) == -1)
goto connect_errReturn;
if (open_SSL_client(conn) == -1)
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());
goto connect_errReturn;
}
/* SSL connection finished. Continue to send startup packet */
}
else if (SSLok == 'E')
@ -998,7 +1015,7 @@ retry2:
goto connect_errReturn;
}
}
if (conn->require_ssl && !PQgetssl(conn))
if (conn->require_ssl && !conn->ssl)
{
/* Require SSL, but server does not support/want it */
printfPQExpBuffer(&conn->errorMessage,
@ -1897,7 +1914,8 @@ freePGconn(PGconn *conn)
return;
pqClearAsyncResult(conn); /* deallocate result and curTuple */
#ifdef USE_SSL
close_SSL(conn);
if (conn->ssl)
SSL_free(conn->ssl);
#endif
if (conn->sock >= 0)
{
@ -2623,6 +2641,35 @@ PQconninfoFree(PQconninfoOption *connOptions)
}
#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 *
PQdb(const PGconn *conn)
@ -2767,6 +2814,16 @@ PQsetClientEncoding(PGconn *conn, const char *encoding)
}
#endif
#ifdef USE_SSL
SSL *
PQgetssl(PGconn *conn)
{
if (!conn)
return NULL;
return conn->ssl;
}
#endif
void
PQtrace(PGconn *conn, FILE *debug_port)
{

View File

@ -25,7 +25,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-misc.c,v 1.71 2002/06/14 03:56:47 momjian Exp $
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-misc.c,v 1.72 2002/06/14 04:09:37 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -55,13 +55,6 @@
#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) \
((*(conn)->noticeHook) ((conn)->noticeArg, (message)))
@ -484,8 +477,14 @@ pqReadData(PGconn *conn)
/* OK, try to read some data */
retry3:
nread = read_SSL(conn, conn->inBuffer + conn->inEnd,
conn->inBufSize - conn->inEnd);
#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);
if (nread < 0)
{
if (SOCK_ERRNO == EINTR)
@ -564,8 +563,14 @@ retry3:
* arrived.
*/
retry4:
nread = read_SSL(conn, conn->inBuffer + conn->inEnd,
conn->inBufSize - conn->inEnd);
#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);
if (nread < 0)
{
if (SOCK_ERRNO == EINTR)
@ -606,9 +611,6 @@ definitelyFailed:
"\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);
#else
@ -648,9 +650,23 @@ pqSendSome(PGconn *conn)
/* 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;
sent = write_SSL(conn, ptr, len);
#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
if (sent < 0)
{
@ -716,7 +732,7 @@ pqSendSome(PGconn *conn)
*/
#ifdef USE_SSL
/* can't do anything for our SSL users yet */
if (PQgetssl(conn) == NULL)
if (conn->ssl == NULL)
{
#endif
if (pqIsnonblocking(conn))

View File

@ -1,785 +0,0 @@
#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 void load_hardcoded_certs(void);
static X509 * load_cert_buffer(const char *buf, size_t len);
static const char *SSLerrmessage(void);
#endif
ssize_t read_SSL(PGconn *, void *, size_t);
ssize_t write_SSL(PGconn *, const void *, size_t);
extern int h_error;
#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;
}
/*
* 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;
}
#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;
}
/*
* 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;
}
}
/*
* 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;
}
/*
* Load a root cert from a buffer. This allows us to avoid
* needing to copy the root cert to deployed systems.
*/
static X509 *
load_cert_buffer(const char *buf, size_t len)
{
BIO *bio;
X509 *x;
bio = BIO_new_mem_buf((char *) buf, len);
x = PEM_read_bio_X509(bio, NULL, NULL, NULL);
BIO_free(bio);
return x;
}
/*
* 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;
struct passwd *pwd;
char fnbuf[2048];
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 any hard-coded root cert */
load_hardcoded_certs();
/* load the CAs we trust */
if ((pwd = getpwuid(getuid())) != NULL)
{
snprintf(fnbuf, sizeof fnbuf, "%s/.postgresql/root.crt", pwd->pw_dir);
if (stat(fnbuf, &buf) != -1)
{
if (!SSL_CTX_load_verify_locations(ctx, fnbuf, 0))
{
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not read CA list (%s): %s\n"),
fnbuf, SSLerrmessage());
return -1;
}
}
}
/* 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;
}
/*
* Destroy the global SSL context.
*/
void destroy_ctx (void)
{
SSL_CTX_free(ctx);
ctx = NULL;
}
/*
* Open a SSL connection.
*/
int
open_SSL_client (PGconn *conn)
{
char peerName[256];
struct sockaddr addr;
struct sockaddr_in *sin1, *sin2;
socklen_t len;
struct hostent *h;
const char *reason;
char **s;
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)
if (r != X509_V_OK)
{
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;
}
/* do a reverse lookup on the server */
len = sizeof(addr);
if (getpeername(conn->sock, &addr, &len) == -1)
{
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("error querying socket: %s\n"), strerror(errno));
return -1;
}
if (addr.sa_family != AF_INET)
{
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("not on IPv4 socket\n"));
return -1;
}
/* check the cert 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 ((h = gethostbyname2(peerName, addr.sa_family)) == NULL)
{
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("error looking up address %s: %s\n"),
peerName, hstrerror(h_errno));
return -1;
}
/* check for a match on actual socket address */
sin1 = (struct sockaddr_in *) &addr;
for (s = h->h_addr_list; *s != NULL; s++)
{
sin2 = (struct sockaddr_in *) *s;
if (sin1->sin_addr.s_addr == sin2->sin_addr.s_addr)
break;
}
/* if that failed, check for a match on alias */
if (*s == NULL)
{
if (strcasecmp(peerName, conn->pghost) == 0)
;
else
{
for (s = h->h_aliases; *s != NULL; s++)
{
if (strcasecmp(peerName, *s) == 0)
break;
}
if (*s == NULL)
{
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext(
"certificate name (%s) does not match peer address\n"),
peerName);
return -1;
}
}
}
return 0;
}
/*
* Close a SSL connection.
*/
void
close_SSL (PGconn *conn)
{
if (conn->ssl)
{
SSL_shutdown(conn->ssl);
SSL_free(conn->ssl);
conn->ssl = NULL;
}
}
/*
* Accessor function that retrieves SSL connection pointer.
*/
SSL *
PQgetssl (PGconn *conn)
{
if (!conn)
return NULL;
return conn->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;
}
/*
* The following conditional block shows how to embedded
* one or more root certs into the libpq library. This
* eliminates any need to copy the file to the clients, but
* obviously must be done on a per-site basis.
*/
#if 0
/*
* The cert file, in PEM format, copied into a string buffer.
*/
static const char root1[] =
"-----BEGIN CERTIFICATE-----\n\
MIIEqDCCBGagAwIBAgIBADALBgcqhkjOOAQDBQAwgYwxEzARBgoJkiaJk/IsZAEZ\n\
EwNjb20xGjAYBgoJkiaJk/IsZAEZEwpjb3lvdGVzb25nMRIwEAYDVQQKEwlTbmFr\n\
ZSBPaWwxHTAbBgNVBAMTFFBvc3RncmVTUUwgUm9vdCBDZXJ0MSYwJAYJKoZIhvcN\n\
AQkBFhdwb3N0Z3Jlc0Bjb3lvdGVzb25nLmNvbTAeFw0wMjA1MjEwMDE4MDZaFw0w\n\
MjA2MjAwMDE4MDZaMIGMMRMwEQYKCZImiZPyLGQBGRMDY29tMRowGAYKCZImiZPy\n\
LGQBGRMKY295b3Rlc29uZzESMBAGA1UEChMJU25ha2UgT2lsMR0wGwYDVQQDExRQ\n\
b3N0Z3JlU1FMIFJvb3QgQ2VydDEmMCQGCSqGSIb3DQEJARYXcG9zdGdyZXNAY295\n\
b3Rlc29uZy5jb20wggG2MIIBKwYHKoZIzjgEATCCAR4CgYEAxgmwTdzv7eSqUjcS\n\
8fdT/3lm+On8LmHL+CkmF7IlvZKm2kwIiQqjcrG6JqgXBdBTIzeqSZV8cGrc0/f5\n\
zMh6rDVxuSrEwCh8DtAC9LdwWyHp7Tw79z9khkZNTAlBonwOLvm0BJaroH5FLK9S\n\
PvAHmjmLA1zd/2K8o+CqFFJasTkCFQDXfI1tnskPUtPXz/W88wRg5y5zpQKBgGwk\n\
3a+tfWmw2mMDXh2sSHoGwVlzwqKZnDfk97I7Tz/zmGOLEGdA7s+2YqKKfW7F0S8p\n\
Ho/cYDNE2lyaGqaxl2pscqdIhEmKYjJtjgaOOkQwfaYXs5GY0zkiSaxxtvJTj0WK\n\
OQ+J/0iunsyyukYc3+TiosHENz4Y2ZgaGseJTMz0A4GEAAKBgFG5WK5/64gjuJ7D\n\
D4RQ7QZtZ+wxP4s3oEqphz4hPGpGOPYlHdo2PhHMEAVrgMnX44yqUAnwmG5LT1RI\n\
5KPCDwgyxBQVq2FDJrYoRb/AVbqMQ8cyJZ1etd7J1ies31b3fHp+uYSFHuCmLfFp\n\
RO8wLplYM6XmJ5X5BF8zlclDxIj/o4IBVTCCAVEwHQYDVR0OBBYEFMO7rhIEVsrn\n\
6k/gxKR5bCdEo8jZMIG5BgNVHSMEgbEwga6AFMO7rhIEVsrn6k/gxKR5bCdEo8jZ\n\
oYGSpIGPMIGMMRMwEQYKCZImiZPyLGQBGRMDY29tMRowGAYKCZImiZPyLGQBGRMK\n\
Y295b3Rlc29uZzESMBAGA1UEChMJU25ha2UgT2lsMR0wGwYDVQQDExRQb3N0Z3Jl\n\
U1FMIFJvb3QgQ2VydDEmMCQGCSqGSIb3DQEJARYXcG9zdGdyZXNAY295b3Rlc29u\n\
Zy5jb22CAQAwDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAQYwEQYJYIZIAYb4QgEB\n\
BAQDAgEGMCIGA1UdEQQbMBmBF3Bvc3RncmVzQGNveW90ZXNvbmcuY29tMCIGA1Ud\n\
EgQbMBmBF3Bvc3RncmVzQGNveW90ZXNvbmcuY29tMAsGByqGSM44BAMFAAMvADAs\n\
AhUAhcafaeM39bK2z2tgRD8OLbrr3fICEwdVqUy9ykb9Hc7SjcKB51lUJ9s=\n\
-----END CERTIFICATE-----\n";
static void
load_hardcoded_certs(void)
{
X509_STORE *store;
X509 *x;
store = SSL_CTX_get_cert_store(ctx);
if (store != NULL)
{
x = load_cert_buffer(root1, sizeof (root1));
X509_STORE_add_cert(store, x);
X509_free(x);
/* repeat as necessary.... */
}
}
#else
static void
load_hardcoded_certs(void)
{
}
#endif
#endif /* USE_SSL */

View File

@ -12,7 +12,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: libpq-int.h,v 1.47 2002/06/14 03:56:47 momjian Exp $
* $Id: libpq-int.h,v 1.48 2002/06/14 04:09:37 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -270,7 +270,6 @@ struct pg_conn
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 */