Forgot the patch...

On Tue, May 30, 2006 at 01:01:38PM +0200, Martijn van Oosterhout wrote:

<snip>
-- 
Martijn van Oosterhout   <kleptog@svana.org>   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);

Attachment: signature.asc
Description: Digital signature

Reply via email to