Forgot the patch... On Tue, May 30, 2006 at 01:01:38PM +0200, Martijn van Oosterhout wrote:
<snip> -- Martijn van Oosterhout <[email protected]> http://svana.org/kleptog/ > From each according to his ability. To each according to his ability to > litigate.
Index: configure
Index: configure.in
===================================================================
RCS file: /projects/cvsroot/pgsql/configure.in,v
retrieving revision 1.464
diff -c -r1.464 configure.in
*** configure.in 29 Apr 2006 20:47:29 -0000 1.464
--- configure.in 30 May 2006 10:51:30 -0000
***************
*** 489,498 ****
#
AC_MSG_CHECKING([whether to build with OpenSSL support])
PGAC_ARG_BOOL(with, openssl, no, [ --with-openssl build with
OpenSSL support],
! [AC_DEFINE([USE_SSL], 1, [Define to build with (Open)SSL
support. (--with-openssl)])])
AC_MSG_RESULT([$with_openssl])
AC_SUBST(with_openssl)
#
# Prefer libedit
--- 489,515 ----
#
AC_MSG_CHECKING([whether to build with OpenSSL support])
PGAC_ARG_BOOL(with, openssl, no, [ --with-openssl build with
OpenSSL support],
! [AC_DEFINE([USE_SSL_OPENSSL], 1, [Define to build with
(Open)SSL support. (--with-openssl)])])
AC_MSG_RESULT([$with_openssl])
AC_SUBST(with_openssl)
+ #
+ # GnuTLS
+ #
+ AC_MSG_CHECKING([whether to build with GnuTLS support])
+ PGAC_ARG_BOOL(with, gnutls, no, [ --with-gnutls build with GnuTLS
support],
+ [AC_DEFINE([USE_SSL_GNUTLS], 1, [Define to build with GnuTLS
(SSL) support. (--with-gnutls)])])
+ AC_MSG_RESULT([$with_gnutls])
+ AC_SUBST(with_gnutls)
+
+ if test "$with_openssl" = yes -o "$with_gnutls" = yes ; then
+ AC_DEFINE([USE_SSL], 1, [Define to build with SSL support (select
either OpenSSL or GnuTLS)])
+ fi
+
+ if test "$with_openssl" = yes -a "$with_gnutls" = yes ; then
+ AC_MSG_ERROR([
+ *** Cannot select both OpenSSL and GnuTLS.])
+ fi
#
# Prefer libedit
***************
*** 692,697 ****
--- 709,720 ----
fi
fi
+ if test "$with_gnutls" = yes ; then
+ AC_CHECK_LIB(gnutls, gnutls_init, [], [AC_MSG_ERROR([library 'gnutls' is
required for GnuTLS])])
+ # Technically we only need this with --enable-thread-safety
+ AC_CHECK_LIB(gcrypt, gcry_control, [], [AC_MSG_ERROR([library 'gcrypt'
is required for GnuTLS])])
+ fi
+
if test "$with_pam" = yes ; then
AC_CHECK_LIB(pam, pam_start, [], [AC_MSG_ERROR([library 'pam' is
required for PAM])])
fi
***************
*** 774,779 ****
--- 797,807 ----
AC_CHECK_HEADER(openssl/err.h, [], [AC_MSG_ERROR([header file
<openssl/err.h> is required for OpenSSL])])
fi
+ if test "$with_gnutls" = yes ; then
+ AC_CHECK_HEADER(gnutls/gnutls.h, [], [AC_MSG_ERROR([header file
<gnutls/gnutls.h> is required for GnuTLS])])
+ AC_CHECK_HEADER(gcrypt.h, [], [AC_MSG_ERROR([header file <gcrypt.h>
is required for GnuTLS])])
+ fi
+
if test "$with_pam" = yes ; then
AC_CHECK_HEADERS(security/pam_appl.h, [],
[AC_CHECK_HEADERS(pam/pam_appl.h, [],
Index: src/backend/libpq/be-secure.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/libpq/be-secure.c,v
retrieving revision 1.70
diff -c -r1.70 be-secure.c
*** src/backend/libpq/be-secure.c 12 May 2006 22:44:36 -0000 1.70
--- src/backend/libpq/be-secure.c 30 May 2006 10:51:32 -0000
***************
*** 89,99 ****
#include <arpa/inet.h>
#endif
! #ifdef USE_SSL
#include <openssl/ssl.h>
#include <openssl/dh.h>
#endif
#include "libpq/libpq.h"
#include "miscadmin.h"
#include "tcop/tcopprot.h"
--- 89,108 ----
#include <arpa/inet.h>
#endif
! #ifdef USE_SSL_OPENSSL
#include <openssl/ssl.h>
#include <openssl/dh.h>
#endif
+ #ifdef USE_SSL_GNUTLS
+ #include <gnutls/gnutls.h>
+ #include <gnutls/x509.h>
+
+ /* If thread safety ever becomes an issue for the backend, we will need to
+ * include this. See the frontend code in libpq. */
+ /* #include <gcrypt.h> */
+ #endif
+
#include "libpq/libpq.h"
#include "miscadmin.h"
#include "tcop/tcopprot.h"
***************
*** 106,132 ****
#define SERVER_CERT_FILE "server.crt"
#define SERVER_PRIVATE_KEY_FILE "server.key"
- static DH *load_dh_file(int keylength);
- static DH *load_dh_buffer(const char *, size_t);
- static DH *tmp_dh_cb(SSL *s, int is_export, int keylength);
- static int verify_cb(int, X509_STORE_CTX *);
- static void info_cb(const SSL *ssl, int type, int args);
static void initialize_SSL(void);
static void destroy_SSL(void);
static int open_server_SSL(Port *);
static void close_SSL(Port *);
- static const char *SSLerrmessage(void);
- #endif
-
- #ifdef USE_SSL
/*
* How much data can be sent across a secure connection
* (total in both directions) before we require renegotiation.
*/
#define RENEGOTIATION_LIMIT (512 * 1024 * 1024)
static SSL_CTX *SSL_context = NULL;
#endif
/* ------------------------------------------------------------ */
/* Hardcoded values
*/
--- 115,148 ----
#define SERVER_CERT_FILE "server.crt"
#define SERVER_PRIVATE_KEY_FILE "server.key"
static void initialize_SSL(void);
static void destroy_SSL(void);
static int open_server_SSL(Port *);
static void close_SSL(Port *);
/*
* How much data can be sent across a secure connection
* (total in both directions) before we require renegotiation.
*/
#define RENEGOTIATION_LIMIT (512 * 1024 * 1024)
+ #endif
+ #ifdef USE_SSL_OPENSSL
+ static DH *load_dh_file(int keylength);
+ static DH *load_dh_buffer(const char *, size_t);
+ static DH *tmp_dh_cb(SSL *s, int is_export, int keylength);
+ static int verify_cb(int, X509_STORE_CTX *);
+ static void info_cb(const SSL *ssl, int type, int args);
+ static const char *SSLerrmessage(void);
static SSL_CTX *SSL_context = NULL;
#endif
+ #ifdef USE_SSL_GNUTLS
+ static const char *SSLerrmessage(int);
+
+ static bool SSL_verify_client;
+ static gnutls_certificate_credentials SSL_cred; /* Credentials for
connecting */
+
+ #define DH_BITS 1024
+ #endif
/* ------------------------------------------------------------ */
/* Hardcoded values
*/
***************
*** 153,160 ****
* for fun and profit, review "Assigned Number for SKIP
* Protocols" (http://www.skip-vpn.org/spec/numbers.html)
* for suggestions.
*/
! #ifdef USE_SSL
static const char file_dh512[] =
"-----BEGIN DH PARAMETERS-----\n\
--- 169,179 ----
* for fun and profit, review "Assigned Number for SKIP
* Protocols" (http://www.skip-vpn.org/spec/numbers.html)
* for suggestions.
+ *
+ * Note: GnuTLS does not use this. Instead it generates a random set
+ * each server restart.
*/
! #ifdef USE_SSL_OPENSSL
static const char file_dh512[] =
"-----BEGIN DH PARAMETERS-----\n\
***************
*** 258,264 ****
{
ssize_t n;
! #ifdef USE_SSL
if (port->ssl)
{
int err;
--- 277,283 ----
{
ssize_t n;
! #ifdef USE_SSL_OPENSSL
if (port->ssl)
{
int err;
***************
*** 312,317 ****
--- 331,366 ----
}
}
else
+ #elif defined(USE_SSL_GNUTLS)
+ if (port->ssl)
+ {
+ rloop:
+ n = gnutls_record_recv(port->ssl, ptr, len);
+ if( n < 0 )
+ {
+ if( n == GNUTLS_E_INTERRUPTED ||
+ n == GNUTLS_E_AGAIN )
+ {
+ #ifdef WIN32
+
pgwin32_waitforsinglesocket(gnutls_transport_get_ptr(port->ssl),
+
(gnutls_record_get_direction(port->ssl) == 0) ?
+ FD_READ |
FD_CLOSE : FD_WRITE | FD_CLOSE);
+ #endif
+ goto rloop;
+ }
+ else
+ {
+ ereport(COMMERROR,
+
(errcode(ERRCODE_PROTOCOL_VIOLATION),
+ errmsg("SSL error: %s",
SSLerrmessage(n))));
+ errno = ECONNRESET;
+ n = -1;
+ }
+ }
+ else
+ port->count += n;
+ }
+ else
#endif
{
prepare_for_client_read();
***************
*** 332,338 ****
{
ssize_t n;
! #ifdef USE_SSL
if (port->ssl)
{
int err;
--- 381,387 ----
{
ssize_t n;
! #ifdef USE_SSL_OPENSSL
if (port->ssl)
{
int err;
***************
*** 411,416 ****
--- 460,530 ----
}
}
else
+ #elif defined(USE_SSL_GNUTLS)
+ if( port->ssl )
+ {
+ int err;
+
+ wloop:
+ n = gnutls_record_send(port->ssl, ptr, len);
+ if( n < 0 )
+ {
+ if( n == GNUTLS_E_INTERRUPTED ||
+ n == GNUTLS_E_AGAIN )
+ {
+ #ifdef WIN32
+ pgwin32_waitforsinglesocket(port->sock),
+
(gnutls_record_get_direction(port->ssl) == 0) ?
+ FD_READ |
FD_CLOSE : FD_WRITE | FD_CLOSE);
+ #endif
+ goto wloop;
+ }
+ else
+ {
+ ereport(COMMERROR,
+
(errcode(ERRCODE_PROTOCOL_VIOLATION),
+ errmsg("SSL error:
%s", SSLerrmessage(n))));
+ errno = ECONNRESET;
+ n = -1;
+ }
+ return n;
+ }
+ port->count += n;
+
+ if (port->count > RENEGOTIATION_LIMIT)
+ {
+ /* Note: we don't allow the user to ignore the
renegotiation request. We could... */
+ do {
+ err = gnutls_rehandshake(port->ssl);
+ } while( err == GNUTLS_E_INTERRUPTED || err ==
GNUTLS_E_AGAIN );
+
+ if (err < 0)
+ {
+ ereport(COMMERROR,
+
(errcode(ERRCODE_PROTOCOL_VIOLATION),
+ errmsg("SSL renegotiation
failure: %s",SSLerrmessage(err))));
+ errno = ECONNRESET;
+ return -1;
+ }
+
+ do {
+ err = gnutls_handshake(port->ssl);
+ } while( err == GNUTLS_E_INTERRUPTED || err ==
GNUTLS_E_AGAIN );
+
+ if (err < 0)
+ {
+ ereport(COMMERROR,
+
(errcode(ERRCODE_PROTOCOL_VIOLATION),
+ errmsg("SSL renegotiation
failure: %s", SSLerrmessage(err))));
+
+ errno = ECONNRESET;
+ return -1;
+ }
+
+ port->count = 0;
+ }
+ }
+ else
#endif
n = send(port->sock, ptr, len, 0);
***************
*** 418,426 ****
}
/* ------------------------------------------------------------ */
! /* SSL specific code
*/
/* ------------------------------------------------------------ */
! #ifdef USE_SSL
/*
* Private substitute BIO: this wraps the SSL library's standard socket BIO
--- 532,540 ----
}
/* ------------------------------------------------------------ */
! /* OpenSSL specific code
*/
/* ------------------------------------------------------------ */
! #ifdef USE_SSL_OPENSSL
/*
* Private substitute BIO: this wraps the SSL library's standard socket BIO
***************
*** 995,998 ****
return errbuf;
}
! #endif /* USE_SSL */
--- 1109,1453 ----
return errbuf;
}
! #endif /* USE_SSL_OPENSSL */
! #ifdef USE_SSL_GNUTLS
! /*
! * Private substitute BIO: this wraps the SSL library's standard socket BIO
! * so that we can enable and disable interrupts just while calling recv().
! * We cannot have interrupts occurring while the bulk of openssl runs,
! * because it uses malloc() and possibly other non-reentrant libc facilities.
! */
!
! static int
! gnutls_raw_read(gnutls_transport_ptr fd, void *buf, size_t size)
! {
! int res;
!
! prepare_for_client_read();
!
! res = read((int)fd, buf, size);
!
! client_read_ended();
!
! return res;
! }
!
! /*
! * Certificate verification callback
! *
! * This callback allows us to log intermediate problems during
! * verification, but for now we'll see if the final error message
! * contains enough information.
! *
! * This callback also allows us to override the default acceptance
! * criteria (e.g., accepting self-signed or expired certs), but
! * for now we accept the default checks.
! */
! #ifdef NOT_USED
! static int
! verify_cb(int ok, X509_STORE_CTX *ctx)
! {
! return ok;
! }
! #endif
!
! /*
! * This callback is used to copy SSL information messages
! * into the PostgreSQL log.
! */
! #ifdef NOT_USED
! static void
! info_cb(const SSL *ssl, int type, int args)
! {
! }
! #endif
!
! /*
! * Initialize global SSL context. This will be called once, by the
postmaster during startup
! */
! static void
! initialize_SSL(void)
! {
! struct stat buf;
! gnutls_dh_params dh_params;
! int error;
!
! #ifdef ENABLE_THREAD_SAFETY
! /* GnuTLS is thread-safe by design, but gcrypt may not be. Here we
assume that the lib is pthread */
! /* Thread safety not an issue for server */
! /* gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread); */
! #endif
! /* Do gnutls_check_version(LIBGNUTLS_VERSION) ? */
! error = gnutls_global_init(); /* Check for error */
! if( error < 0 )
! ereport(FATAL,
! (errmsg("could not initialize GnuTLS: %s",
! SSLerrmessage(error))));
!
! gnutls_certificate_allocate_credentials (&SSL_cred);
!
! if (stat(SERVER_PRIVATE_KEY_FILE, &buf) == -1)
! ereport(FATAL,
! (errcode_for_file_access(),
! errmsg("could not access private key file
\"%s\": %m",
! SERVER_PRIVATE_KEY_FILE)));
!
! /*
! * Require no public access to key file.
! *
! * XXX temporarily suppress check when on Windows, because there may
! * not be proper support for Unix-y file permissions. Need to think
! * of a reasonable check to apply on Windows. (See also the data
! * directory permission check in postmaster.c)
! */
! #if !defined(WIN32) && !defined(__CYGWIN__)
! if (!S_ISREG(buf.st_mode) || (buf.st_mode & (S_IRWXG | S_IRWXO)) ||
! buf.st_uid != geteuid())
! ereport(FATAL,
! (errcode(ERRCODE_CONFIG_FILE_ERROR),
! errmsg("unsafe permissions on private key file
\"%s\"",
! SERVER_PRIVATE_KEY_FILE),
! errdetail("File must be owned by the database
user and must have no permissions for \"group\" or \"other\".")));
! #endif
!
! if( (error = gnutls_certificate_set_x509_key_file( SSL_cred,
SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE, GNUTLS_X509_FMT_PEM )) < 0 )
! ereport(FATAL,
! (errmsg("could not load private key/certificate
file \"%s\", \"%s\": %s",
! SERVER_PRIVATE_KEY_FILE,
SERVER_CERT_FILE, SSLerrmessage(error))));
!
!
! /* set up empheral DH keys */
! #if 0
! gnutls_certificate_set_params_function(SSL_cred, tmp_dh_cb);
! #else
! gnutls_dh_params_init (&dh_params);
! gnutls_dh_params_generate2 (dh_params, DH_BITS);
! gnutls_certificate_set_dh_params (SSL_cred, dh_params);
! #endif
!
! /* Cannot setup the allowed cipher list, must do that at connection
start */
!
! /*
! * Require and check client certificates only if we have a root.crt
file.
! */
! error = gnutls_certificate_set_x509_trust_file (SSL_cred,
ROOT_CERT_FILE, GNUTLS_X509_FMT_PEM);
! if ( error < 0 )
! {
! /* Not fatal - we do not require client certificates */
! ereport(LOG,
! (errmsg("could not load root certificate file
\"%s\": %s",
! ROOT_CERT_FILE,
SSLerrmessage(error)),
! errdetail("Will not verify client
certificates.")));
! }
! else
! {
! SSL_verify_client = TRUE;
! error = gnutls_certificate_set_x509_crl_file (SSL_cred,
ROOT_CRL_FILE, GNUTLS_X509_FMT_PEM);
! if ( error < 0 )
! {
! /* Not fatal - we do not require a CRL file */
! ereport(LOG,
! (errmsg("SSL Certificate Revocation
List (CRL) file \"%s\" not found, skipping: %s",
! ROOT_CRL_FILE,
SSLerrmessage(error)),
! errdetail("Will not check certificates
against CRL.")));
! }
! }
!
! #ifdef NOT_USED
! /* We don't use a verify function, but if we did, here's where
we'd set it */
! SSL_CTX_set_verify(SSL_context,
! (SSL_VERIFY_PEER |
!
SSL_VERIFY_FAIL_IF_NO_PEER_CERT |
! SSL_VERIFY_CLIENT_ONCE),
! verify_cb);
! #endif
! }
!
! /*
! * Destroy global SSL context.
! */
! static void
! destroy_SSL(void)
! {
! gnutls_certificate_free_credentials(SSL_cred);
! SSL_cred = NULL;
! gnutls_global_deinit();
! }
!
! /*
! * Attempt to negotiate SSL connection.
! */
! static int
! open_server_SSL(Port *port)
! {
! int err;
! int certcount;
!
! const int kx_prio[] = { GNUTLS_KX_DHE_RSA, GNUTLS_KX_DHE_DSS, 0 };
! /* Only do X509 certificates */
! const int cert_type_priority[] = { GNUTLS_CRT_X509, 0 };
! const int cipher_type_priority[] = { GNUTLS_CIPHER_AES_256_CBC,
GNUTLS_CIPHER_AES_128_CBC, GNUTLS_CIPHER_ARCFOUR_128, 0 };
! const int mac_type_priority[] = { GNUTLS_MAC_SHA, GNUTLS_MAC_RMD160, 0
};
!
! Assert(!port->ssl);
! Assert(!port->peer);
!
! err = gnutls_init (&port->ssl, GNUTLS_SERVER);
! if( err < 0 )
! {
! ereport(COMMERROR,
! (errcode(ERRCODE_PROTOCOL_VIOLATION),
! errmsg("could not initialize SSL connection:
%s",
! SSLerrmessage(err))));
! close_SSL(port);
! return -1;
! }
! /* Use default priorities */
! gnutls_set_default_priority (port->ssl);
! /* Server uses: ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH. This means:
! * - No anonymous DH cipher suites.
! * - Low security keys
! * - No export algorithms
! * - No MD5 */
! gnutls_kx_set_priority (port->ssl, kx_prio); /* DH keyexchange only */
! gnutls_certificate_type_set_priority (port->ssl, cert_type_priority);
/* X.509 certs only */
! gnutls_cipher_set_priority( port->ssl, cipher_type_priority );
! gnutls_mac_set_priority( port->ssl, mac_type_priority );
!
! gnutls_transport_set_ptr (port->ssl, (gnutls_transport_ptr) port->sock);
! gnutls_session_set_ptr (port->ssl, port);
!
! /* setup customer read function */
! gnutls_transport_set_pull_function( port->ssl, gnutls_raw_read );
!
! /* set up mechanism to provide client certificate, if available */
! gnutls_credentials_set (port->ssl, GNUTLS_CRD_CERTIFICATE, SSL_cred);
!
! /* Set the number of bits for DH */
! gnutls_dh_set_prime_bits (port->ssl, DH_BITS);
!
! /* Request a certificate, if any */
! gnutls_certificate_server_set_request (port->ssl, GNUTLS_CERT_REQUEST);
!
! aloop:
! err = gnutls_handshake (port->ssl);
! if (err < 0)
! {
! if( err == GNUTLS_E_AGAIN ||
! err == GNUTLS_E_INTERRUPTED )
! {
! #ifdef WIN32
! pgwin32_waitforsinglesocket(port->sock),
!
(gnutls_record_get_direction(port->ssl) == 0) ?
! FD_READ | FD_CLOSE | FD_ACCEPT : FD_WRITE |
FD_CLOSE);
! #endif
! goto aloop;
! }
! ereport(COMMERROR,
! (errcode_for_socket_access(),
! errmsg("could not accept SSL connection: %s",
SSLerrmessage(err))));
! close_SSL(port);
! return -1;
! }
!
! port->count = 0;
!
! /* get server certificate */
! port->peer = gnutls_certificate_get_peers(port->ssl, &certcount);
!
! strncpy(port->peer_dn, "(anonymous)", sizeof(port->peer_dn));
! strncpy(port->peer_cn, "(anonymous)", sizeof(port->peer_cn));
!
! if( port->peer )
! {
! gnutls_x509_crt cert;
!
! err = gnutls_x509_crt_init (&cert);
! if(err == 0)
! err = gnutls_x509_crt_import (cert, &port->peer[0],
GNUTLS_X509_FMT_DER);
! if(err < 0)
! {
! ereport(WARNING,
! (errmsg("gnutls_x509_crt_import
failure: %s\n", SSLerrmessage(err))));
! }
! else
! {
! int size = sizeof(port->peer_dn);
! gnutls_x509_crt_get_dn (cert, port->peer_dn, &size);
! port->peer_dn[sizeof(port->peer_dn) - 1] = '\0';
!
! size = sizeof(port->peer_cn);
! gnutls_x509_crt_get_dn_by_oid(cert,
GNUTLS_OID_X520_COMMON_NAME, 0, 0, port->peer_cn, &size );
! port->peer_cn[sizeof(port->peer_cn) - 1] = '\0';
!
! gnutls_x509_crt_deinit( cert );
! }
! }
! /* If asked to verify server, do so */
! if( SSL_verify_client )
! {
! int status;
! err = gnutls_certificate_verify_peers2 (port->ssl, &status);
! if(err < 0)
! {
! ereport(COMMERROR,
! (errmsg("SSL verification error: %s\n",
SSLerrmessage(err))));
! close_SSL(port);
! return -1;
! }
! if( status )
! {
! ereport(COMMERROR,
! (errmsg("Server cert verification
failure: %d\n", status)));
! close_SSL(port);
! return -1;
! }
! }
! ereport(DEBUG2,
! (errmsg("SSL connection from \"%s\"", port->peer_cn)));
!
! #ifdef NOT_USED
! /* set up debugging/info callback */
! SSL_CTX_set_info_callback(SSL_context, info_cb);
! #endif
!
! return 0;
! }
!
! /*
! * Close SSL connection.
! */
! static void
! close_SSL(Port *port)
! {
! /* Isn't allocated, so don't free */
! if (port->peer)
! port->peer = NULL;
! /* Clear SSL data */
! if (port->ssl)
! {
! gnutls_deinit(port->ssl);
! port->ssl = NULL;
! }
!
! }
!
! /*
! * Obtain reason string for last SSL error
! *
! * gnutls_strerror returns a useful string even if it doesn't recognise the
error code
! */
! static const char *
! SSLerrmessage(int errcode)
! {
! if (errcode == 0)
! return "No SSL error reported";
! /* Error in push/pull function is error in socket send/recv */
! if( errcode == GNUTLS_E_PUSH_ERROR || errcode == GNUTLS_E_PULL_ERROR )
! return strerror(errno);
!
! return gnutls_strerror(errcode);
! }
!
! #endif
!
Index: src/bin/psql/startup.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/bin/psql/startup.c,v
retrieving revision 1.132
diff -c -r1.132 startup.c
*** src/bin/psql/startup.c 27 Apr 2006 02:58:08 -0000 1.132
--- src/bin/psql/startup.c 30 May 2006 10:51:33 -0000
***************
*** 8,14 ****
#include "postgres_fe.h"
#include <sys/types.h>
! #ifdef USE_SSL
#include <openssl/ssl.h>
#endif
--- 8,14 ----
#include "postgres_fe.h"
#include <sys/types.h>
! #ifdef USE_SSL_OPENSSL
#include <openssl/ssl.h>
#endif
***************
*** 731,736 ****
--- 731,737 ----
static void
printSSLInfo(void)
{
+ #ifdef USE_SSL_OPENSSL
int sslbits = -1;
SSL *ssl;
***************
*** 741,746 ****
--- 742,752 ----
SSL_get_cipher_bits(ssl, &sslbits);
printf(_("SSL connection (cipher: %s, bits: %i)\n\n"),
SSL_get_cipher(ssl), sslbits);
+ #elif defined(USE_SSL_GNUTLS)
+ if( !PQgetssl(pset.db) )
+ return;
+ printf(_("SSL connection\n\n"));
+ #endif
}
#endif
Index: src/include/pg_config.h.in
Index: src/include/libpq/libpq-be.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/include/libpq/libpq-be.h,v
retrieving revision 1.55
diff -c -r1.55 libpq-be.h
*** src/include/libpq/libpq-be.h 5 Mar 2006 15:58:56 -0000 1.55
--- src/include/libpq/libpq-be.h 30 May 2006 10:51:33 -0000
***************
*** 21,29 ****
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
! #ifdef USE_SSL
#include <openssl/ssl.h>
#include <openssl/err.h>
#endif
#ifdef HAVE_NETINET_TCP_H
#include <netinet/tcp.h>
--- 21,35 ----
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
! #ifdef USE_SSL_OPENSSL
#include <openssl/ssl.h>
#include <openssl/err.h>
+ typedef SSL *pg_sslcontext;
+ typedef X509 *pg_sslcert;
+ #elif USE_SSL_GNUTLS
+ #include <gnutls/gnutls.h>
+ typedef gnutls_session pg_sslcontext;
+ typedef const gnutls_datum *pg_sslcert;
#endif
#ifdef HAVE_NETINET_TCP_H
#include <netinet/tcp.h>
***************
*** 100,107 ****
* SSL structures
*/
#ifdef USE_SSL
! SSL *ssl;
! X509 *peer;
char peer_dn[128 + 1];
char peer_cn[SM_USER + 1];
unsigned long count;
--- 106,113 ----
* SSL structures
*/
#ifdef USE_SSL
! pg_sslcontext ssl;
! pg_sslcert peer;
char peer_dn[128 + 1];
char peer_cn[SM_USER + 1];
unsigned long count;
Index: src/interfaces/libpq/fe-misc.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/fe-misc.c,v
retrieving revision 1.127
diff -c -r1.127 fe-misc.c
*** src/interfaces/libpq/fe-misc.c 23 May 2006 19:28:45 -0000 1.127
--- src/interfaces/libpq/fe-misc.c 30 May 2006 10:51:33 -0000
***************
*** 970,976 ****
#ifdef USE_SSL
/* Check for SSL library buffering read bytes */
! if (forRead && conn->ssl && SSL_pending(conn->ssl) > 0)
{
/* short-circuit the select */
return 1;
--- 970,976 ----
#ifdef USE_SSL
/* Check for SSL library buffering read bytes */
! if (forRead && conn->ssl && pqsecure_pending(conn) > 0)
{
/* short-circuit the select */
return 1;
Index: src/interfaces/libpq/fe-secure.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/fe-secure.c,v
retrieving revision 1.81
diff -c -r1.81 fe-secure.c
*** src/interfaces/libpq/fe-secure.c 11 May 2006 23:27:35 -0000 1.81
--- src/interfaces/libpq/fe-secure.c 30 May 2006 10:51:33 -0000
***************
*** 114,123 ****
#include "strdup.h"
#endif
! #ifdef USE_SSL
#include <openssl/ssl.h>
! #endif /* USE_SSL */
#ifdef USE_SSL
--- 114,132 ----
#include "strdup.h"
#endif
! #ifdef USE_SSL_OPENSSL
#include <openssl/ssl.h>
! #include <openssl/err.h>
! #endif /* USE_SSL_OPENSSL */
+ #ifdef USE_SSL_GNUTLS
+ #include <gnutls/gnutls.h>
+ #include <gnutls/x509.h>
+
+ #ifdef ENABLE_THREAD_SAFETY
+ #include <gcrypt.h>
+ #endif
+ #endif /* USE_SSL_GNUTLS */
#ifdef USE_SSL
***************
*** 132,158 ****
#define USER_KEY_FILE "postgresql.key"
#define ROOT_CERT_FILE "root.crt"
#define ROOT_CRL_FILE "root.crl"
! #endif
#ifdef NOT_USED
static int verify_peer(PGconn *);
#endif
static int verify_cb(int ok, X509_STORE_CTX *ctx);
static int client_cert_cb(SSL *, X509 **, EVP_PKEY **);
- static int init_ssl_system(PGconn *conn);
- static int initialize_SSL(PGconn *);
- static void destroy_SSL(void);
- static PostgresPollingStatusType open_client_SSL(PGconn *);
- static void close_SSL(PGconn *);
static char *SSLerrmessage(void);
static void SSLerrfree(char *buf);
- #endif
- #ifdef USE_SSL
static bool pq_initssllib = true;
static SSL_CTX *SSL_context = NULL;
#endif
/* ------------------------------------------------------------ */
/* Procedures common to all secure sessions
*/
--- 141,191 ----
#define USER_KEY_FILE "postgresql.key"
#define ROOT_CERT_FILE "root.crt"
#define ROOT_CRL_FILE "root.crl"
! #endif /* WIN32 */
+ static int initialize_SSL(PGconn *);
+ static void destroy_SSL(void);
+ static void close_SSL(PGconn *);
+ static PostgresPollingStatusType open_client_SSL(PGconn *);
+ static int init_ssl_system(PGconn *conn);
+
+ static char ssl_nomem[] = "Out of memory allocating error description";
+
+ #define SSL_ERR_LEN 128
+
+ #endif
+
+ #ifdef USE_SSL_OPENSSL
#ifdef NOT_USED
static int verify_peer(PGconn *);
#endif
static int verify_cb(int ok, X509_STORE_CTX *ctx);
static int client_cert_cb(SSL *, X509 **, EVP_PKEY **);
static char *SSLerrmessage(void);
static void SSLerrfree(char *buf);
static bool pq_initssllib = true;
static SSL_CTX *SSL_context = NULL;
+ #endif /* USE_SSL_OPENSSL */
+
+ #ifdef USE_SSL_GNUTLS
+ /* Define this for extra debugging info */
+ /* #define GNUTLS_DEBUG */
+
+ static char *pq_gnutls_errmessage(int err);
+ static void pq_gnutls_errfree(char *buf);
+
+ static int SSL_verify_server;
+ static int SSL_handshake;
+ static gnutls_certificate_credentials SSL_cred; /* Credentials for
connecting */
+
+ #ifdef ENABLE_THREAD_SAFETY
+ GCRY_THREAD_OPTION_PTHREAD_IMPL;
#endif
+ #endif /* USE_SSL_GNUTLS */
+
+
/* ------------------------------------------------------------ */
/* Procedures common to all secure sessions
*/
***************
*** 166,172 ****
void
PQinitSSL(int do_init)
{
! #ifdef USE_SSL
pq_initssllib = do_init;
#endif
}
--- 199,205 ----
void
PQinitSSL(int do_init)
{
! #ifdef USE_SSL_OPENSSL
pq_initssllib = do_init;
#endif
}
***************
*** 203,209 ****
PostgresPollingStatusType
pqsecure_open_client(PGconn *conn)
{
! #ifdef USE_SSL
/* First time through? */
if (conn->ssl == NULL)
{
--- 236,242 ----
PostgresPollingStatusType
pqsecure_open_client(PGconn *conn)
{
! #if defined(USE_SSL_OPENSSL)
/* First time through? */
if (conn->ssl == NULL)
{
***************
*** 229,234 ****
--- 262,316 ----
}
/* Begin or continue the actual handshake */
return open_client_SSL(conn);
+ #elif defined(USE_SSL_GNUTLS)
+ const int kx_prio[] = { GNUTLS_KX_DHE_RSA, GNUTLS_KX_DHE_DSS, 0 };
+ /* Only do X509 certificates */
+ const int cert_type_priority[] = { GNUTLS_CRT_X509, 0 };
+ const int cipher_type_priority[] = { GNUTLS_CIPHER_AES_256_CBC,
GNUTLS_CIPHER_AES_128_CBC, GNUTLS_CIPHER_ARCFOUR_128, 0 };
+ const int mac_type_priority[] = { GNUTLS_MAC_SHA, GNUTLS_MAC_RMD160, 0
};
+
+ /* First time through? */
+ if (conn->ssl == NULL)
+ {
+ int error = gnutls_init ( (gnutls_session*)(&conn->ssl),
GNUTLS_CLIENT);
+ if (error)
+ {
+ char *err = pq_gnutls_errmessage(error);
+
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("could not
create SSL context: %s\n"),
+ err);
+ pq_gnutls_errfree(err);
+
+ return -1;
+ }
+ // gnutls_credentials_set (conn->ssl, GNUTLS_CRD_ANON,
SSL_anoncred);
+
+ /* Use default priorities */
+ gnutls_set_default_priority (conn->ssl);
+ /* Server uses: ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH. This means:
+ * - No anonymous DH cipher suites.
+ * - Low security keys
+ * - No export algorithms
+ * - No MD5 */
+ gnutls_kx_set_priority (conn->ssl, kx_prio); /* DH keyexchange
only */
+ gnutls_certificate_type_set_priority (conn->ssl,
cert_type_priority); /* X.509 certs only */
+ gnutls_cipher_set_priority( conn->ssl, cipher_type_priority );
+ gnutls_mac_set_priority( conn->ssl, mac_type_priority );
+
+ gnutls_transport_set_ptr (conn->ssl, (gnutls_transport_ptr)
conn->sock);
+ gnutls_session_set_ptr (conn->ssl, conn);
+
+ /* set up mechanism to provide client certificate, if available
*/
+ gnutls_credentials_set (conn->ssl, GNUTLS_CRD_CERTIFICATE,
SSL_cred);
+ /*
+ * Initialize errorMessage to empty. This allows
open_client_SSL() to
+ * detect whether pg_gnutls_client_cert_cb() has stored a
message.
+ */
+ resetPQExpBuffer(&conn->errorMessage);
+ }
+ /* Begin or continue the actual handshake */
+ return open_client_SSL(conn);
#else
/* shouldn't get here */
return PGRES_POLLING_FAILED;
***************
*** 255,261 ****
{
ssize_t n;
! #ifdef USE_SSL
if (conn->ssl)
{
int err;
--- 337,343 ----
{
ssize_t n;
! #if defined(USE_SSL_OPENSSL)
if (conn->ssl)
{
int err;
***************
*** 319,324 ****
--- 401,455 ----
}
}
else
+ #elif defined(USE_SSL_GNUTLS)
+ if( conn->ssl )
+ {
+ rloop:
+ if( SSL_handshake )
+ n = gnutls_handshake(conn->ssl);
+ else
+ n = gnutls_record_recv(conn->ssl, ptr, len);
+ if( n < 0 )
+ {
+ if( n == GNUTLS_E_INTERRUPTED ||
+ n == GNUTLS_E_AGAIN )
+ {
+ /*
+ * Returning 0 here would cause caller to wait
for read-ready,
+ * which is not correct since what SSL wants is
wait for
+ * write-ready. The former could get us stuck
in an infinite
+ * wait, so don't risk it; busy-loop instead.
+ */
+ if( gnutls_record_get_direction(conn->ssl) == 0
) /* read */
+ n = 0;
+ else
+ goto rloop;
+ }
+ else if( n == GNUTLS_E_REHANDSHAKE )
+ {
+ /* Server wants a rehandshake. */
+ SSL_handshake = true;
+ goto rloop;
+ }
+ else
+ {
+ char *err = pq_gnutls_errmessage(n);
+ printfPQExpBuffer(&conn->errorMessage,
+
libpq_gettext("SSL error: %s\n"), err);
+ pq_gnutls_errfree(err);
+ SOCK_ERRNO_SET(ECONNRESET);
+ n = -1;
+ }
+ }
+ /* If we get here, the handshake has completed */
+ else if( SSL_handshake )
+ {
+ SSL_handshake = false;
+ /* Go back and read actual data now */
+ goto rloop;
+ }
+ }
+ else
#endif
n = recv(conn->sock, ptr, len, 0);
***************
*** 347,353 ****
#endif /* ENABLE_THREAD_SAFETY */
#endif /* WIN32 */
! #ifdef USE_SSL
if (conn->ssl)
{
int err;
--- 478,484 ----
#endif /* ENABLE_THREAD_SAFETY */
#endif /* WIN32 */
! #if defined(USE_SSL_OPENSSL)
if (conn->ssl)
{
int err;
***************
*** 415,420 ****
--- 546,593 ----
}
}
else
+ #elif defined(USE_SSL_GNUTLS)
+ if( conn->ssl )
+ {
+ #if defined(ENABLE_THREAD_SAFETY) && !defined(WIN32)
+ got_epipe = false;
+ #endif
+ if( SSL_handshake )
+ n = gnutls_handshake(conn->ssl);
+ else
+ n = gnutls_record_send(conn->ssl, ptr, len);
+ if( n < 0 )
+ {
+ #if defined(ENABLE_THREAD_SAFETY) && !defined(WIN32)
+ if (SOCK_ERRNO == EPIPE)
+ got_epipe = true;
+ #endif
+ if( n == GNUTLS_E_INTERRUPTED ||
+ n == GNUTLS_E_AGAIN )
+ {
+ /*
+ * Returning 0 here causes caller to wait
+ * for write-ready, which is may not really
+ * the right thing, but it's the best we can
+ * do.
+ */
+ n = 0;
+ }
+ else
+ {
+ char *err = pq_gnutls_errmessage(n);
+ printfPQExpBuffer(&conn->errorMessage,
+
libpq_gettext("SSL error: %s\n"), err);
+ pq_gnutls_errfree(err);
+ SOCK_ERRNO_SET(ECONNRESET);
+ n = -1;
+ SSL_handshake = false;
+ }
+ }
+ else
+ SSL_handshake = false;
+ }
+ else
#endif
{
n = send(conn->sock, ptr, len, 0);
***************
*** 436,444 ****
}
/* ------------------------------------------------------------ */
! /* SSL specific code
*/
/* ------------------------------------------------------------ */
! #ifdef USE_SSL
/*
* Certificate verification callback
--- 609,617 ----
}
/* ------------------------------------------------------------ */
! /* OpenSSL specific code
*/
/* ------------------------------------------------------------ */
! #if defined(USE_SSL_OPENSSL)
/*
* Certificate verification callback
***************
*** 932,957 ****
#endif
/* pull out server distinguished and common names */
- conn->peer = SSL_get_peer_certificate(conn->ssl);
- if (conn->peer == NULL)
{
! char *err = SSLerrmessage();
!
! printfPQExpBuffer(&conn->errorMessage,
! libpq_gettext("certificate could not be
obtained: %s\n"),
! err);
! SSLerrfree(err);
! close_SSL(conn);
! return PGRES_POLLING_FAILED;
! }
! X509_NAME_oneline(X509_get_subject_name(conn->peer),
! conn->peer_dn, sizeof(conn->peer_dn));
! conn->peer_dn[sizeof(conn->peer_dn) - 1] = '\0';
! X509_NAME_get_text_by_NID(X509_get_subject_name(conn->peer),
NID_commonName,
conn->peer_cn, SM_USER);
! conn->peer_cn[SM_USER] = '\0';
/* verify that the common name resolves to peer */
--- 1105,1135 ----
#endif
/* pull out server distinguished and common names */
{
! X509 *peer = SSL_get_peer_certificate(conn->ssl);
! if (peer == NULL)
! {
! char *err = SSLerrmessage();
!
! printfPQExpBuffer(&conn->errorMessage,
! libpq_gettext("certificate
could not be obtained: %s\n"),
! err);
! SSLerrfree(err);
! close_SSL(conn);
! return PGRES_POLLING_FAILED;
! }
! X509_NAME_oneline(X509_get_subject_name(peer),
! conn->peer_dn,
sizeof(conn->peer_dn));
! conn->peer_dn[sizeof(conn->peer_dn) - 1] = '\0';
! X509_NAME_get_text_by_NID(X509_get_subject_name(peer),
NID_commonName,
conn->peer_cn, SM_USER);
! conn->peer_cn[SM_USER] = '\0';
!
! X509_free(peer);
! }
!
/* verify that the common name resolves to peer */
***************
*** 986,997 ****
SSL_free(conn->ssl);
conn->ssl = NULL;
}
-
- if (conn->peer)
- {
- X509_free(conn->peer);
- conn->peer = NULL;
- }
}
/*
--- 1164,1169 ----
***************
*** 1001,1010 ****
* return NULL if it doesn't recognize the error code. We don't
* want to return NULL ever.
*/
- static char ssl_nomem[] = "Out of memory allocating error description";
-
- #define SSL_ERR_LEN 128
-
static char *
SSLerrmessage(void)
{
--- 1173,1178 ----
***************
*** 1049,1055 ****
return NULL;
return conn->ssl;
}
! #else /* !USE_SSL */
void *
PQgetssl(PGconn *conn)
--- 1217,1653 ----
return NULL;
return conn->ssl;
}
!
! int
! pqsecure_pending(PGconn *conn)
! {
! return SSL_pending(conn->ssl);
! }
! #elif defined(USE_SSL_GNUTLS)
!
/***********************************************************************************
! * GnuTLS specific code
*
!
***********************************************************************************/
! static void
! pq_gnutls_load_file( PGconn *conn, const char *filename, gnutls_datum *datum )
! {
! int filesize;
! FILE *f;
!
! datum->size = 0;
! datum->data = NULL;
!
! if( (f = fopen( filename, "r" )) == NULL )
! {
! printfPQExpBuffer(&conn->errorMessage,
! libpq_gettext("could not open
file \"%s\": %m\n"),
! filename);
! return;
! }
! if( (filesize = fseek( f, SEEK_END, 0 )) < 0 )
! {
! printfPQExpBuffer(&conn->errorMessage,
! libpq_gettext("could not seek
file \"%s\": %m\n"),
! filename);
! return;
! }
! if( fseek( f, SEEK_SET, 0 ) < 0 )
! {
! printfPQExpBuffer(&conn->errorMessage,
! libpq_gettext("could not seek
file \"%s\": %m\n"),
! filename);
! return;
! }
! datum->data = malloc( filesize );
! if( fread( datum->data, filesize, 1, f ) != 1 )
! {
! printfPQExpBuffer(&conn->errorMessage,
! libpq_gettext("could not read
file \"%s\": %m\n"),
! filename);
! return;
! }
! fclose(f);
! datum->size = filesize;
! return;
! }
! /*
! * Callback used by SSL to load client cert and key.
! * This callback is only called when the server wants a
! * client cert.
! *
! * Must return 0 on success.
! */
! static int
! pg_gnutls_client_cert_cb(gnutls_session session, const gnutls_datum *
req_ca_rdn, int nreqs,
! const gnutls_pk_algorithm * pk_algos,
! int pk_algos_length, gnutls_retr_st *st)
! {
! gnutls_certificate_type type;
! gnutls_x509_crt ssl_clientcrt;
! gnutls_x509_privkey ssl_clientkey;
!
! char homedir[MAXPGPATH];
! struct stat buf;
!
! char crtfile[MAXPGPATH], keyfile[MAXPGPATH];
! gnutls_datum crtdatum, keydatum;
!
! PGconn *conn = (PGconn *) gnutls_session_get_ptr(session);
!
! type = gnutls_certificate_type_get (session);
! if (type != GNUTLS_CRT_X509)
! return -1;
!
! /* Setup these values so we can return 0 at any time to try without
cert */
! st->type = type;
! st->ncerts = 0;
! st->cert.x509 = NULL;
! st->key.x509 = NULL;
! st->deinit_all = 0;
!
! if (!pqGetHomeDirectory(homedir, sizeof(homedir)))
! {
! printfPQExpBuffer(&conn->errorMessage,
! libpq_gettext("could not get
user information\n"));
! return 0;
! }
!
! /* find the user certificate */
! snprintf(crtfile, sizeof(crtfile), "%s/%s", homedir, USER_CERT_FILE);
! if (stat(crtfile, &buf) == -1)
! {
! printfPQExpBuffer(&conn->errorMessage,
! libpq_gettext("could not find
certificate file \"%s\"\n"),
! crtfile);
! return 0;
! }
! /* find the user key */
! snprintf(keyfile, sizeof(keyfile), "%s/%s", homedir, USER_KEY_FILE);
! if (stat(keyfile, &buf) == -1)
! {
! printfPQExpBuffer(&conn->errorMessage,
! libpq_gettext("certificate
present, but not private key file \"%s\"\n"),
! keyfile);
! return 0;
! }
! #ifndef WIN32
! if (!S_ISREG(buf.st_mode) || (buf.st_mode & 0077) ||
! buf.st_uid != geteuid())
! {
! printfPQExpBuffer(&conn->errorMessage,
! libpq_gettext("private key file \"%s\" has wrong
permissions\n"),
! keyfile);
! return 0;
! }
! #endif
! pq_gnutls_load_file( conn, crtfile, &crtdatum );
! pq_gnutls_load_file( conn, keyfile, &keydatum );
!
! if( crtdatum.size && keydatum.size )
! {
! int err;
!
! gnutls_x509_crt_init( &ssl_clientcrt );
! gnutls_x509_privkey_init( &ssl_clientkey );
!
! err = gnutls_x509_crt_import( ssl_clientcrt, &crtdatum,
GNUTLS_X509_FMT_PEM );
! if( err == 0 )
! err = gnutls_x509_privkey_import( ssl_clientkey,
&keydatum, GNUTLS_X509_FMT_PEM );
! if( err == 0 )
! {
! st->ncerts = 1;
! st->key.x509 = ssl_clientkey;
! st->cert.x509 = malloc( sizeof(ssl_clientcrt)); /* No
easy way to clean this up */
! st->cert.x509[0] = ssl_clientcrt;
! st->deinit_all = 1;
! }
! else
! {
! char *error = pq_gnutls_errmessage(err);
!
! printfPQExpBuffer(&conn->errorMessage,
! libpq_gettext("could not parse
key/cert files: %s\n"),
! error);
! pq_gnutls_errfree(error);
! }
! }
! if( crtdatum.data )
! free( crtdatum.data );
! if( keydatum.data )
! free( keydatum.data );
!
!
! return 0;
! }
!
! #ifdef GNUTLS_DEBUG
! static void
! pq_gnutls_log( int level, const char *str )
! {
! fprintf( stderr, "%d: %s\n", level, str );
! }
! #endif
!
! /*
! * Initialize global SSL context.
! */
! static int
! initialize_SSL(PGconn *conn)
! {
! struct stat buf;
! char homedir[MAXPGPATH];
! char fnbuf[MAXPGPATH];
!
! if (init_ssl_system(conn))
! return -1;
!
! gnutls_certificate_allocate_credentials (&SSL_cred);
!
! /* Set up to verify server cert, if root.crt is present */
! if (pqGetHomeDirectory(homedir, sizeof(homedir)))
! {
! snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir,
ROOT_CERT_FILE);
! if (stat(fnbuf, &buf) == 0)
! {
! int error;
! error = gnutls_certificate_set_x509_trust_file
(SSL_cred, fnbuf, GNUTLS_X509_FMT_PEM);
! if( error < 0 )
! {
! char *err = pq_gnutls_errmessage(error);
!
! printfPQExpBuffer(&conn->errorMessage,
!
libpq_gettext("could not read root certificate file \"%s\": %s\n"),
! fnbuf, err);
! pq_gnutls_errfree(err);
! return -1;
! }
! SSL_verify_server = TRUE;
! }
! }
!
! gnutls_certificate_client_set_retrieve_function(SSL_cred,
pg_gnutls_client_cert_cb);
!
! return 0;
! }
!
! /*
! * Destroy global SSL context.
! */
! void
! destroy_SSL(void)
! {
! if (SSL_cred)
! {
! gnutls_certificate_free_credentials (SSL_cred);
! SSL_cred = NULL;
!
! gnutls_global_deinit ();
! }
! }
!
! static int
! init_ssl_system(PGconn *conn)
! {
! int error;
! #ifdef ENABLE_THREAD_SAFETY
! /* GnuTLS is thread-safe by design, but gcrypt may not be. Here we
assume that the lib is pthread */
! gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
! #endif
! /* Do gnutls_check_version(LIBGNUTLS_VERSION) ? */
! error = gnutls_global_init(); /* Check for error */
! if (error)
! {
! char *err = pq_gnutls_errmessage(error);
!
! printfPQExpBuffer(&conn->errorMessage,
! libpq_gettext("could not create SSL
context: %s\n"),
! err);
! pq_gnutls_errfree(err);
! return -1;
! }
! #ifdef GNUTLS_DEBUG
! gnutls_global_set_log_function( pq_gnutls_log );
! gnutls_global_set_log_level (9);
! #endif
! return 0;
! }
!
! /*
! * Attempt to negotiate SSL connection.
! */
! static PostgresPollingStatusType
! open_client_SSL(PGconn *conn)
! {
! int err;
! int cert_list_size, status, size;
! gnutls_x509_crt cert;
!
! err = gnutls_handshake(conn->ssl);
! if (err < 0)
! {
! if( err == GNUTLS_E_AGAIN ||
! err == GNUTLS_E_INTERRUPTED )
! {
! if( gnutls_record_get_direction(conn->ssl) == 0 )
! return PGRES_POLLING_READING;
! else
! return PGRES_POLLING_WRITING;
! }
! if( gnutls_error_is_fatal(err) )
! {
! printfPQExpBuffer(&conn->errorMessage,
! libpq_gettext("SSL error: %s\n"),
! gnutls_strerror(err));
! close_SSL(conn);
! return PGRES_POLLING_FAILED;
! }
! else
! {
! printfPQExpBuffer(&conn->errorMessage,
! libpq_gettext("Unexpected non-fatal SSL
error: %s\n"),
! gnutls_strerror(err));
! close_SSL(conn);
! return PGRES_POLLING_FAILED;
! }
! }
!
! /* If asked to verify server, do so */
! if( SSL_verify_server )
! {
! err = gnutls_certificate_verify_peers2 (conn->ssl, &status);
! if(err < 0)
! {
! printfPQExpBuffer(&conn->errorMessage,
! libpq_gettext("SSL verification error:
%s\n"),
! gnutls_strerror(err));
! close_SSL(conn);
! return PGRES_POLLING_FAILED;
! }
! if( status )
! {
! printfPQExpBuffer(&conn->errorMessage,
! libpq_gettext("Server cert verification
failure: %d\n"),
! status);
! close_SSL(conn);
! return PGRES_POLLING_FAILED;
! }
! }
! /* pull out server distinguished and common names */
! const gnutls_datum *peer = gnutls_certificate_get_peers (conn->ssl,
&cert_list_size);
! if (peer == NULL)
! {
! printfPQExpBuffer(&conn->errorMessage,
! libpq_gettext("certificate could not be
obtained\n"));
! close_SSL(conn);
! return PGRES_POLLING_FAILED;
! }
! err = gnutls_x509_crt_init (&cert);
! if(err < 0)
! {
! printfPQExpBuffer(&conn->errorMessage,
! libpq_gettext("gnutls_x509_crt_init failure:
%s\n"),
! gnutls_strerror(err));
! close_SSL(conn);
! return PGRES_POLLING_FAILED;
! }
!
! err = gnutls_x509_crt_import (cert, &peer[0], GNUTLS_X509_FMT_DER);
! if(err < 0)
! {
! printfPQExpBuffer(&conn->errorMessage,
! libpq_gettext("gnutls_x509_crt_import failure:
%s\n"),
! gnutls_strerror(err));
! close_SSL(conn);
! return PGRES_POLLING_FAILED;
! }
! size = sizeof(conn->peer_dn);
! gnutls_x509_crt_get_dn (cert, conn->peer_dn, &size);
! conn->peer_dn[sizeof(conn->peer_dn) - 1] = '\0';
!
! size = sizeof(conn->peer_cn);
! gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0,
conn->peer_cn, &size );
! conn->peer_cn[sizeof(conn->peer_cn) - 1] = '\0';
!
! gnutls_x509_crt_deinit( cert );
! /* SSL handshake is complete */
! return PGRES_POLLING_OK;
! }
!
! /*
! * Close SSL connection.
! */
! void
! close_SSL(PGconn *conn)
! {
! if (conn->ssl)
! {
! #ifdef NOT_USED
! conn->peer = NULL; /* Is allocated as part of session, no need
to free */
! #endif
! gnutls_bye (conn->ssl, GNUTLS_SHUT_RDWR);
! gnutls_deinit (conn->ssl);
! conn->ssl = NULL;
! }
! }
!
! /*
! * 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 char *
! pq_gnutls_errmessage(int errcode)
! {
! const char *errreason;
! char *errbuf;
!
! errbuf = malloc(SSL_ERR_LEN);
! if (!errbuf)
! return ssl_nomem;
! if (errcode >= 0)
! {
! strcpy(errbuf, "No SSL error reported");
! return errbuf;
! }
! errreason = gnutls_strerror(errcode);
! if (errreason != NULL)
! {
! strncpy(errbuf, errreason, SSL_ERR_LEN - 1);
! errbuf[SSL_ERR_LEN - 1] = '\0';
! return errbuf;
! }
! snprintf(errbuf, SSL_ERR_LEN, "SSL error code %u", errcode);
! return errbuf;
! }
!
! static void
! pq_gnutls_errfree(char *buf)
! {
! if (buf != ssl_nomem)
! free(buf);
! }
!
! /*
! * Return pointer to SSL object.
! *
! * Unfortunatly, this function was in the past defined to return a
! * pointer to an OpenSSL structure, so here we just return NULL.
! *
! */
! void *
! PQgetssl(PGconn *conn)
! {
! return NULL;
! }
!
! int
! pqsecure_pending(PGconn *conn)
! {
! return gnutls_record_check_pending(conn->ssl);
! }
! #else /* !USE_SSL_OPENSSL &&
!USE_SSL_GNUTLS */
void *
PQgetssl(PGconn *conn)
Index: src/interfaces/libpq/libpq-int.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/libpq-int.h,v
retrieving revision 1.113
diff -c -r1.113 libpq-int.h
*** src/interfaces/libpq/libpq-int.h 21 May 2006 20:19:23 -0000 1.113
--- src/interfaces/libpq/libpq-int.h 30 May 2006 10:51:33 -0000
***************
*** 49,59 ****
/* include stuff found in fe only */
#include "pqexpbuffer.h"
- #ifdef USE_SSL
- #include <openssl/ssl.h>
- #include <openssl/err.h>
- #endif
-
/*
* POSTGRES backend dependent Constants.
*/
--- 49,54 ----
***************
*** 338,345 ****
bool allow_ssl_try; /* Allowed to try SSL negotiation */
bool wait_ssl_try; /* Delay SSL negotiation until after
* attempting
normal connection */
! SSL *ssl; /* SSL status, if have
SSL connection */
! X509 *peer; /* X509 cert of server */
char peer_dn[256 + 1]; /* peer distinguished
name */
char peer_cn[SM_USER + 1]; /* peer common name */
#endif
--- 333,339 ----
bool allow_ssl_try; /* Allowed to try SSL negotiation */
bool wait_ssl_try; /* Delay SSL negotiation until after
* attempting
normal connection */
! void *ssl; /* SSL status, if have SSL connection */
char peer_dn[256 + 1]; /* peer distinguished
name */
char peer_cn[SM_USER + 1]; /* peer common name */
#endif
***************
*** 480,485 ****
--- 474,480 ----
extern void pqsecure_close(PGconn *);
extern ssize_t pqsecure_read(PGconn *, void *ptr, size_t len);
extern ssize_t pqsecure_write(PGconn *, const void *ptr, size_t len);
+ extern int pqsecure_pending(PGconn *);
#if defined(ENABLE_THREAD_SAFETY) && !defined(WIN32)
extern int pq_block_sigpipe(sigset_t *osigset, bool *sigpipe_pending);
signature.asc
Description: Digital signature
