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);
signature.asc
Description: Digital signature