From eb43af321067e743eafb81692c05575e0624ca6a Mon Sep 17 00:00:00 2001 From: Bruce Momjian Date: Fri, 14 Jun 2002 04:09:37 +0000 Subject: [PATCH] Back out SSL changes. Newer patch available. --- src/backend/libpq/Makefile | 5 +- src/backend/libpq/pqcomm.c | 44 +- src/backend/postmaster/postmaster.c | 96 +++- src/bin/psql/startup.c | 23 +- src/include/libpq/libpq-be.h | 3 +- src/interfaces/libpq/Makefile | 4 +- src/interfaces/libpq/fe-connect.c | 81 ++- src/interfaces/libpq/fe-misc.c | 50 +- src/interfaces/libpq/fe-ssl.c | 785 ---------------------------- src/interfaces/libpq/libpq-int.h | 3 +- 10 files changed, 208 insertions(+), 886 deletions(-) delete mode 100644 src/interfaces/libpq/fe-ssl.c diff --git a/src/backend/libpq/Makefile b/src/backend/libpq/Makefile index 55d2b8c421..87f00e68ce 100644 --- a/src/backend/libpq/Makefile +++ b/src/backend/libpq/Makefile @@ -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 diff --git a/src/backend/libpq/pqcomm.c b/src/backend/libpq/pqcomm.c index 5ece972b56..4a71eb1615 100644 --- a/src/backend/libpq/pqcomm.c +++ b/src/backend/libpq/pqcomm.c @@ -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 diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c index 5b56ab37ca..12b0941181 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -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. diff --git a/src/bin/psql/startup.c b/src/bin/psql/startup.c index 1d8e3aa965..da8f5c01c3 100644 --- a/src/bin/psql/startup.c +++ b/src/bin/psql/startup.c @@ -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 diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h index d0e6a15162..7343e2ac69 100644 --- a/src/include/libpq/libpq-be.h +++ b/src/include/libpq/libpq-be.h @@ -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; diff --git a/src/interfaces/libpq/Makefile b/src/interfaces/libpq/Makefile index a9679e631d..e98be1b8a6 100644 --- a/src/interfaces/libpq/Makefile +++ b/src/interfaces/libpq/Makefile @@ -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 diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c index ac4a42052a..02164778b7 100644 --- a/src/interfaces/libpq/fe-connect.c +++ b/src/interfaces/libpq/fe-connect.c @@ -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) { diff --git a/src/interfaces/libpq/fe-misc.c b/src/interfaces/libpq/fe-misc.c index 64a05c8dbe..a8af4d893a 100644 --- a/src/interfaces/libpq/fe-misc.c +++ b/src/interfaces/libpq/fe-misc.c @@ -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)) diff --git a/src/interfaces/libpq/fe-ssl.c b/src/interfaces/libpq/fe-ssl.c deleted file mode 100644 index dc0590357d..0000000000 --- a/src/interfaces/libpq/fe-ssl.c +++ /dev/null @@ -1,785 +0,0 @@ -#include "postgres_fe.h" - -#include -#include -#include -#include -#include -#include - -#include "libpq-fe.h" -#include "libpq-int.h" -#include "fe-auth.h" -#include "pqsignal.h" - -#ifdef WIN32 -#include "win32.h" -#else -#include -#include -#include -#include -#ifdef HAVE_NETINET_TCP_H -#include -#endif -#include -#endif - -#ifdef USE_SSL -#include -#include -#include - -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 */ diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h index a874d6bfda..ec8deb0083 100644 --- a/src/interfaces/libpq/libpq-int.h +++ b/src/interfaces/libpq/libpq-int.h @@ -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 */