From 902ca3e2255411d709a1ffa28dea9eda92425f77 Mon Sep 17 00:00:00 2001
From: Bruce Momjian <bruce@momjian.us>
Date: Wed, 6 Oct 2004 09:35:23 +0000
Subject: [PATCH] Here is a patch to fix win32 ssl builds. Summary of changes:

* Links with -leay32 and -lssleay32 instead of crypto and ssl. On win32,
"crypto and ssl" is only used for static linking.

* Initializes SSL in the backend and not just in the postmaster. We
cannot pass the SSL context from the postmaster through the parameter
file, because it contains function pointers.

* Split one error check in be-secure.c. Previously we could not tell
which of three calls actually failed. The previous code also returned
incorrect error messages if SSL_accept() failed - that function needs to
use SSL_get_error() on the return value, can't just use the error queue.

* Since the win32 implementation uses non-blocking sockets "behind the
scenes" in order to deliver signals correctly, implements a version of
SSL_accept() that can handle this. Also, add a wait function in case
SSL_read or SSL_write() needs more data.

Magnus Hagander
---
 configure                           | 141 ++++++++++++++++++++++++++++
 configure.in                        |  11 ++-
 src/backend/libpq/be-secure.c       |  68 +++++++++++++-
 src/backend/port/win32/socket.c     |   4 +-
 src/backend/postmaster/postmaster.c |  12 ++-
 src/include/pg_config.h.in          |   6 ++
 src/include/port/win32.h            |   3 +-
 7 files changed, 234 insertions(+), 11 deletions(-)

diff --git a/configure b/configure
index f82192ae567..59327b959d2 100755
--- a/configure
+++ b/configure
@@ -6558,6 +6558,7 @@ fi
 fi
 
 if test "$with_openssl" = yes ; then
+    if test "$PORTNAME" != "win32"; then
 
 echo "$as_me:$LINENO: checking for CRYPTO_new_ex_data in -lcrypto" >&5
 echo $ECHO_N "checking for CRYPTO_new_ex_data in -lcrypto... $ECHO_C" >&6
@@ -6696,6 +6697,146 @@ echo "$as_me: error: library 'ssl' is required for OpenSSL" >&2;}
    { (exit 1); exit 1; }; }
 fi
 
+  else
+
+echo "$as_me:$LINENO: checking for CRYPTO_new_ex_data in -leay32" >&5
+echo $ECHO_N "checking for CRYPTO_new_ex_data in -leay32... $ECHO_C" >&6
+if test "${ac_cv_lib_eay32_CRYPTO_new_ex_data+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-leay32  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+#line $LINENO "configure"
+#include "confdefs.h"
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char CRYPTO_new_ex_data ();
+#ifdef F77_DUMMY_MAIN
+#  ifdef __cplusplus
+     extern "C"
+#  endif
+   int F77_DUMMY_MAIN() { return 1; }
+#endif
+int
+main ()
+{
+CRYPTO_new_ex_data ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+         { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_lib_eay32_CRYPTO_new_ex_data=yes
+else
+  echo "$as_me: failed program was:" >&5
+cat conftest.$ac_ext >&5
+ac_cv_lib_eay32_CRYPTO_new_ex_data=no
+fi
+rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_eay32_CRYPTO_new_ex_data" >&5
+echo "${ECHO_T}$ac_cv_lib_eay32_CRYPTO_new_ex_data" >&6
+if test $ac_cv_lib_eay32_CRYPTO_new_ex_data = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBEAY32 1
+_ACEOF
+
+  LIBS="-leay32 $LIBS"
+
+else
+  { { echo "$as_me:$LINENO: error: library 'eay32' is required for OpenSSL" >&5
+echo "$as_me: error: library 'eay32' is required for OpenSSL" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+
+echo "$as_me:$LINENO: checking for SSL_library_init in -lssleay32" >&5
+echo $ECHO_N "checking for SSL_library_init in -lssleay32... $ECHO_C" >&6
+if test "${ac_cv_lib_ssleay32_SSL_library_init+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lssleay32  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+#line $LINENO "configure"
+#include "confdefs.h"
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char SSL_library_init ();
+#ifdef F77_DUMMY_MAIN
+#  ifdef __cplusplus
+     extern "C"
+#  endif
+   int F77_DUMMY_MAIN() { return 1; }
+#endif
+int
+main ()
+{
+SSL_library_init ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+         { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_lib_ssleay32_SSL_library_init=yes
+else
+  echo "$as_me: failed program was:" >&5
+cat conftest.$ac_ext >&5
+ac_cv_lib_ssleay32_SSL_library_init=no
+fi
+rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_ssleay32_SSL_library_init" >&5
+echo "${ECHO_T}$ac_cv_lib_ssleay32_SSL_library_init" >&6
+if test $ac_cv_lib_ssleay32_SSL_library_init = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBSSLEAY32 1
+_ACEOF
+
+  LIBS="-lssleay32 $LIBS"
+
+else
+  { { echo "$as_me:$LINENO: error: library 'ssleay32' is required for OpenSSL" >&5
+echo "$as_me: error: library 'ssleay32' is required for OpenSSL" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+  fi
 fi
 
 if test "$with_pam" = yes ; then
diff --git a/configure.in b/configure.in
index 56119d74d88..bc4976f52d8 100644
--- a/configure.in
+++ b/configure.in
@@ -1,5 +1,5 @@
 dnl Process this file with autoconf to produce a configure script.
-dnl $PostgreSQL: pgsql/configure.in,v 1.379 2004/10/01 02:00:41 neilc Exp $
+dnl $PostgreSQL: pgsql/configure.in,v 1.380 2004/10/06 09:35:19 momjian Exp $
 dnl
 dnl Developers, please strive to achieve this order:
 dnl
@@ -672,8 +672,13 @@ fi
 
 if test "$with_openssl" = yes ; then
   dnl Order matters!
-  AC_CHECK_LIB(crypto, CRYPTO_new_ex_data, [], [AC_MSG_ERROR([library 'crypto' is required for OpenSSL])])
-  AC_CHECK_LIB(ssl,    SSL_library_init, [], [AC_MSG_ERROR([library 'ssl' is required for OpenSSL])])
+  if test "$PORTNAME" != "win32"; then
+     AC_CHECK_LIB(crypto, CRYPTO_new_ex_data, [], [AC_MSG_ERROR([library 'crypto' is required for OpenSSL])])
+     AC_CHECK_LIB(ssl,    SSL_library_init, [], [AC_MSG_ERROR([library 'ssl' is required for OpenSSL])])
+  else
+     AC_CHECK_LIB(eay32, CRYPTO_new_ex_data, [], [AC_MSG_ERROR([library 'eay32' is required for OpenSSL])])
+     AC_CHECK_LIB(ssleay32,    SSL_library_init, [], [AC_MSG_ERROR([library 'ssleay32' is required for OpenSSL])])
+  fi
 fi
 
 if test "$with_pam" = yes ; then
diff --git a/src/backend/libpq/be-secure.c b/src/backend/libpq/be-secure.c
index eee9ad28367..efe7d7c1180 100644
--- a/src/backend/libpq/be-secure.c
+++ b/src/backend/libpq/be-secure.c
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/libpq/be-secure.c,v 1.51 2004/09/26 22:51:49 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/libpq/be-secure.c,v 1.52 2004/10/06 09:35:20 momjian Exp $
  *
  *	  Since the server static private key ($DataDir/server.key)
  *	  will normally be stored unencrypted so that the database
@@ -268,6 +268,11 @@ rloop:
 				break;
 			case SSL_ERROR_WANT_READ:
 			case SSL_ERROR_WANT_WRITE:
+#ifdef WIN32
+				pgwin32_waitforsinglesocket(SSL_get_fd(port->ssl),
+					(err==SSL_ERROR_WANT_READ) ?
+						FD_READ|FD_CLOSE : FD_WRITE|FD_CLOSE);
+#endif
 				goto rloop;
 			case SSL_ERROR_SYSCALL:
 				if (n == -1)
@@ -356,6 +361,11 @@ wloop:
 				break;
 			case SSL_ERROR_WANT_READ:
 			case SSL_ERROR_WANT_WRITE:
+#ifdef WIN32
+				pgwin32_waitforsinglesocket(SSL_get_fd(port->ssl),
+					(err==SSL_ERROR_WANT_READ) ?
+						FD_READ|FD_CLOSE : FD_WRITE|FD_CLOSE);
+#endif
 				goto wloop;
 			case SSL_ERROR_SYSCALL:
 				if (n == -1)
@@ -717,6 +727,38 @@ initialize_SSL(void)
 	return 0;
 }
 
+#ifdef WIN32
+/*
+ *	Win32 socket code uses nonblocking sockets. We ned to deal with that
+ *	by waiting on the socket if the SSL accept operation didn't complete
+ *	right away.
+ */
+static int pgwin32_SSL_accept(SSL *ssl)
+{
+	int r;
+
+	while (1)
+	{
+		int rc;
+		int waitfor;
+
+		printf("uhh\n");fflush(stdout);
+		r = SSL_accept(ssl);
+		if (r == 1)
+			return 1;
+
+		rc = SSL_get_error(ssl, r);
+		if (rc != SSL_ERROR_WANT_READ && rc != SSL_ERROR_WANT_WRITE)
+			return r;
+
+		waitfor = (rc == SSL_ERROR_WANT_READ)?FD_READ|FD_CLOSE|FD_ACCEPT:FD_WRITE|FD_CLOSE;
+		if (pgwin32_waitforsinglesocket(SSL_get_fd(ssl), waitfor) == 0)
+			return -1;
+	}
+}
+#define SSL_accept(ssl) pgwin32_SSL_accept(ssl)
+#endif
+
 /*
  *	Destroy global SSL context.
  */
@@ -736,12 +778,11 @@ destroy_SSL(void)
 static int
 open_server_SSL(Port *port)
 {
+	int r;
 	Assert(!port->ssl);
 	Assert(!port->peer);
 
-	if (!(port->ssl = SSL_new(SSL_context)) ||
-		!SSL_set_fd(port->ssl, port->sock) ||
-		SSL_accept(port->ssl) <= 0)
+	if (!(port->ssl = SSL_new(SSL_context)))
 	{
 		ereport(COMMERROR,
 				(errcode(ERRCODE_PROTOCOL_VIOLATION),
@@ -750,6 +791,25 @@ open_server_SSL(Port *port)
 		close_SSL(port);
 		return -1;
 	}
+	if (!SSL_set_fd(port->ssl, port->sock))
+	{
+		ereport(COMMERROR,
+				(errcode(ERRCODE_PROTOCOL_VIOLATION),
+				 errmsg("could not set SSL socket: %s",
+						SSLerrmessage())));
+		close_SSL(port);
+		return -1;
+	}
+	if ((r=SSL_accept(port->ssl)) <= 0)
+	{
+		ereport(COMMERROR,
+				(errcode(ERRCODE_PROTOCOL_VIOLATION),
+				 errmsg("could not accept SSL connection: %i",
+						SSL_get_error(port->ssl,r))));
+		close_SSL(port);
+		return -1;
+	}
+
 	port->count = 0;
 
 	/* get client certificate, if available. */
diff --git a/src/backend/port/win32/socket.c b/src/backend/port/win32/socket.c
index 9be5a7ae134..de8376201fa 100644
--- a/src/backend/port/win32/socket.c
+++ b/src/backend/port/win32/socket.c
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/port/win32/socket.c,v 1.6 2004/09/07 14:31:42 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/port/win32/socket.c,v 1.7 2004/10/06 09:35:20 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -102,7 +102,7 @@ pgwin32_poll_signals(void)
 	return 0;
 }
 
-static int
+int
 pgwin32_waitforsinglesocket(SOCKET s, int what)
 {
 	static HANDLE waitevent = INVALID_HANDLE_VALUE;
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index c329c12d25d..34d71483ef5 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -37,7 +37,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.425 2004/09/09 00:59:33 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.426 2004/10/06 09:35:21 momjian Exp $
  *
  * NOTES
  *
@@ -2981,6 +2981,16 @@ SubPostmasterMain(int argc, char *argv[])
 		/* Attach process to shared segments */
 		CreateSharedMemoryAndSemaphores(false, MaxBackends, 0);
 
+#ifdef USE_SSL
+		/*
+		 *	Need to reinitialize the SSL library in the backend,
+		 *	since the context structures contain function pointers
+		 *	and cannot be passed through the parameter file.
+		 */
+		if (EnableSSL)
+			secure_initialize();
+#endif
+
 		Assert(argc == 3);		/* shouldn't be any more args */
 		proc_exit(BackendRun(&port));
 	}
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index 2fb4adf8fb2..600f4229f0a 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -227,6 +227,9 @@
 /* Define to 1 if you have the `dld' library (-ldld). */
 #undef HAVE_LIBDLD
 
+/* Define to 1 if you have the `eay32' library (-leay32). */
+#undef HAVE_LIBEAY32
+
 /* Define to 1 if you have the `gen' library (-lgen). */
 #undef HAVE_LIBGEN
 
@@ -266,6 +269,9 @@
 /* Define to 1 if you have the `ssl' library (-lssl). */
 #undef HAVE_LIBSSL
 
+/* Define to 1 if you have the `ssleay32' library (-lssleay32). */
+#undef HAVE_LIBSSLEAY32
+
 /* Define to 1 if you have the `unix' library (-lunix). */
 #undef HAVE_LIBUNIX
 
diff --git a/src/include/port/win32.h b/src/include/port/win32.h
index 1b79271719a..d3ca1f20d8b 100644
--- a/src/include/port/win32.h
+++ b/src/include/port/win32.h
@@ -1,4 +1,4 @@
-/* $PostgreSQL: pgsql/src/include/port/win32.h,v 1.36 2004/10/05 14:27:07 momjian Exp $ */
+/* $PostgreSQL: pgsql/src/include/port/win32.h,v 1.37 2004/10/06 09:35:23 momjian Exp $ */
 
 /* undefine and redefine after #include */
 #undef mkdir
@@ -141,6 +141,7 @@ int			pgwin32_recv(SOCKET s, char *buf, int len, int flags);
 int			pgwin32_send(SOCKET s, char *buf, int len, int flags);
 
 const char *pgwin32_socket_strerror(int err);
+int pgwin32_waitforsinglesocket(SOCKET s, int what);
 
 /* in backend/port/win32/security.c */
 extern int	pgwin32_is_admin(void);
-- 
GitLab