revised SSL patches 
Author Message
 revised SSL patches

--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_--



Sat, 06 Nov 2004 08:50:40 GMT
 
 [ 1 post ] 

 Relevant Pages 

1. revised SSL patches submitted

2. Revised Patch for JDBC timestamp problems

3. revised patch for PL/PgSQL table functions

4. patch for SSL cleanup, client certificates

5. SSL Connections [doc PATCH]

6. SSL Connections [doc PATCH]

7. SSL (patch 2)

8. SSL (patch 3)

9. SSL Connections [doc PATCH]

10. [PATCH] Win32 native fixes after SSL updates (+more)

11. Refuse SSL patch

12. Refuse SSL patch


 
Powered by phpBB® Forum Software