From 1cc55168d7867c4c4771c7d80361256abe22b8ea Mon Sep 17 00:00:00 2001
From: Barry Lind <barry@xythos.com>
Date: Thu, 27 Feb 2003 05:45:44 +0000
Subject: [PATCH] Added support for SSL in the jdbc driver

 Modified Files:
 	jdbc/build.xml jdbc/org/postgresql/Driver.java.in
 	jdbc/org/postgresql/PG_Stream.java
 	jdbc/org/postgresql/errors.properties
 	jdbc/org/postgresql/jdbc1/AbstractJdbc1Connection.java
 	jdbc/org/postgresql/util/PSQLException.java
---
 src/interfaces/jdbc/build.xml                 | 23 ++++-
 .../jdbc/org/postgresql/Driver.java.in        | 89 +++++++++++++------
 .../jdbc/org/postgresql/PG_Stream.java        | 14 +--
 .../jdbc/org/postgresql/errors.properties     |  2 +
 .../jdbc1/AbstractJdbc1Connection.java        | 69 +++++++++++++-
 .../org/postgresql/util/PSQLException.java    |  2 +-
 6 files changed, 159 insertions(+), 40 deletions(-)

diff --git a/src/interfaces/jdbc/build.xml b/src/interfaces/jdbc/build.xml
index 1e104a1bd55..bcb29f44290 100644
--- a/src/interfaces/jdbc/build.xml
+++ b/src/interfaces/jdbc/build.xml
@@ -6,7 +6,7 @@
 
   This file now requires Ant 1.4.1.  2002-04-18
 
-  $Header: /cvsroot/pgsql/src/interfaces/jdbc/Attic/build.xml,v 1.31 2002/12/11 12:27:47 davec Exp $
+  $Header: /cvsroot/pgsql/src/interfaces/jdbc/Attic/build.xml,v 1.32 2003/02/27 05:45:43 barry Exp $
 
 -->
 
@@ -22,6 +22,7 @@
   <property name="builddir" value="build" />
   <property name="package" value="org/postgresql" />
   <property name="debug" value="on" />
+  <property name="ssl" value="false" />
 
   <property file="build.properties"/>
 
@@ -47,6 +48,7 @@
         <equals arg1="${ant.java.version}" arg2="1.4"/>
     </condition>
     <available property="datasource" classname="javax.sql.DataSource"/>
+    <available property="ssl" classname="javax.net.ssl.SSLSocketFactory"/>
     <available property="junit" classname="junit.framework.Test" />
     <condition property="jdbc2tests">
       <and>
@@ -160,12 +162,27 @@
         <equals arg1="${jdbc3}" arg2="true"/>
     </condition>
 
+    <!-- determine the ssl status -->
+    <condition property="ssl_config" value="">
+        <equals arg1="${ssl}" arg2="true"/>
+    </condition>
+    <condition property="ssl_config" value="//">
+        <equals arg1="${ssl}" arg2="false"/>
+    </condition>
+    <condition property="ssl_edition" value="SSL">
+        <equals arg1="${ssl}" arg2="true"/>
+    </condition>
+    <condition property="ssl_edition" value="NO SSL">
+        <equals arg1="${ssl}" arg2="false"/>
+    </condition>
+
     <!-- Some defaults -->
     <filter token="MAJORVERSION" value="${major}" />
     <filter token="MINORVERSION" value="${minor}" />
-    <filter token="VERSION" value="PostgreSQL ${fullversion} ${edition}" />
+    <filter token="VERSION" value="PostgreSQL ${fullversion} ${edition} with ${ssl_edition}" />
     <filter token="JDBCCONNECTCLASS" value="${connectclass}" />
     <filter token="DEF_PGPORT" value="${def_pgport}" />
+    <filter token="SSL" value="${ssl_config}" />
 
      <fail unless="major" message="'major' undefined. Please follow the directions in README."/>
      <fail unless="minor" message="'minor' undefined. Please follow the directions in README."/>
@@ -181,7 +198,7 @@
           tofile="${package}/Driver.java"
           filtering="yes" />
 
-    <echo message="Configured build for the ${edition} edition driver" />
+    <echo message="Configured build for the ${edition} edition driver with ${ssl_edition}" />
   </target>
 
 
diff --git a/src/interfaces/jdbc/org/postgresql/Driver.java.in b/src/interfaces/jdbc/org/postgresql/Driver.java.in
index 80b47199358..465e300b887 100644
--- a/src/interfaces/jdbc/org/postgresql/Driver.java.in
+++ b/src/interfaces/jdbc/org/postgresql/Driver.java.in
@@ -1,5 +1,6 @@
 package org.postgresql;
 
+import java.io.*;
 import java.sql.*;
 import java.util.*;
 
@@ -66,15 +67,17 @@ public class Driver implements java.sql.Driver
 	 *
 	 * user - (optional) The user to connect as
 	 * password - (optional) The password for the user
+	 * ssl - (optional) Use SSL when connecting to the server
 	 * charSet - (optional) The character set to be used for converting
 	 *	 to/from the database to unicode.  If multibyte is enabled on the
 	 *	 server then the character set of the database is used as the default,
 	 *	 otherwise the jvm character encoding is used as the default.
-			* loglevel - (optional) Enable logging of messages from the driver.
-			*		The value is an integer from 1 to 2 where:
-			*		  INFO = 1, DEBUG = 2
-			*		The output is sent to DriverManager.getPrintWriter() if set,
-			*		otherwise it is sent to System.out.
+	 *   This value is only used when connecting to a 7.2 or older server.
+	 * loglevel - (optional) Enable logging of messages from the driver.
+	 *		The value is an integer from 1 to 2 where:
+	 *		  INFO = 1, DEBUG = 2
+	 *		The output is sent to DriverManager.getPrintWriter() if set,
+	 *		otherwise it is sent to System.out.
 	 * compatible - (optional) This is used to toggle
 	 *	 between different functionality as it changes across different releases
 	 *	 of the jdbc driver code.  The values here are versions of the jdbc
@@ -136,8 +139,9 @@ public class Driver implements java.sql.Driver
 		}
 		catch (Exception ex2)
 		{
-			if (Driver.logDebug)
+			if (Driver.logDebug) {
 				Driver.debug("error", ex2);
+			}
 			throw new PSQLException("postgresql.unusual", ex2);
 		}
 	}
@@ -211,7 +215,7 @@ public class Driver implements java.sql.Driver
 	 */
 	public static String getVersion()
 	{
-		return "@VERSION@ jdbc driver build " + m_buildNumber;
+		return "@VERSION@ (build " + m_buildNumber + ")";
 	}
 
 	/*
@@ -248,7 +252,17 @@ public class Driver implements java.sql.Driver
 		String key = "";
 		String value = "";
 
-		StringTokenizer st = new StringTokenizer(url, ":/;=&?", true);
+		String l_urlServer = url;
+		String l_urlArgs = "";
+
+		int l_qPos = url.indexOf('?');
+		if (l_qPos != -1) {
+			l_urlServer = url.substring(0,l_qPos);
+			l_urlArgs = url.substring(l_qPos+1);
+		}
+
+		//parse the server part of the url
+		StringTokenizer st = new StringTokenizer(l_urlServer, ":/", true);
 		for (int count = 0; (st.hasMoreTokens()); count++)
 		{
 			String token = st.nextToken();
@@ -318,25 +332,19 @@ public class Driver implements java.sql.Driver
 					urlProps.put("PGDBNAME", token);
 					state = -2;
 				}
-				else if (state <= -2 && (count % 2) == 1)
-				{
-					// PM Aug 2 1997 - added tests for ? and &
-					if (token.equals(";") || token.equals("?") || token.equals("&") )
-						state = -3;
-					else if (token.equals("="))
-						state = -5;
-				}
-				else if (state <= -2 && (count % 2) == 0)
-				{
-					if (state == -3)
-						key = token;
-					else if (state == -5)
-					{
-						value = token;
-						urlProps.put(key, value);
-						state = -2;
-					}
-				}
+			}
+		}
+
+		//parse the args part of the url
+		StringTokenizer qst = new StringTokenizer(l_urlArgs, "&");
+		for (int count = 0; (qst.hasMoreTokens()); count++)
+		{
+			String token = qst.nextToken();
+			int l_pos = token.indexOf('=');
+			if (l_pos == -1) {
+				urlProps.put(token, "");
+			} else {
+				urlProps.put(token.substring(0,l_pos), token.substring(l_pos+1));
 			}
 		}
 
@@ -419,7 +427,10 @@ public class Driver implements java.sql.Driver
 	{
 		if (logDebug)
 		{
-			DriverManager.println(msg + ex != null ? ex.getMessage() : "null Exception");
+			DriverManager.println(msg);
+			if(ex != null) {
+				DriverManager.println(ex.toString());
+			}
 		}
 	}
 	/*
@@ -441,10 +452,30 @@ public class Driver implements java.sql.Driver
 	{
 		if (logInfo)
 		{
-			DriverManager.println(msg + ex != null ? ex.getMessage() : "null Exception");
+			DriverManager.println(msg);
+			if(ex != null) {
+				DriverManager.println(ex.toString());
+			}
 		}
 	}
 
+
+	public static void makeSSL(PG_Stream p_stream) throws IOException {
+@SSL@		if (logDebug)
+@SSL@			debug("converting regular socket connection to ssl");
+@SSL@		javax.net.ssl.SSLSocketFactory factory = (javax.net.ssl.SSLSocketFactory) javax.net.ssl.SSLSocketFactory.getDefault();
+@SSL@		p_stream.connection = (javax.net.ssl.SSLSocket) factory.createSocket(p_stream.connection,p_stream.host,p_stream.port,true);
+@SSL@		p_stream.pg_input = new BufferedInputStream(p_stream.connection.getInputStream(), 8192);
+@SSL@		p_stream.pg_output = new BufferedOutputStream(p_stream.connection.getOutputStream(), 8192);
+	}
+
+	public static boolean sslEnabled() {
+		boolean l_return = false;
+@SSL@		l_return = true;
+		return l_return;
+	}
+
+
 	//The build number should be incremented for every new build
 	private static int m_buildNumber = 201;
 
diff --git a/src/interfaces/jdbc/org/postgresql/PG_Stream.java b/src/interfaces/jdbc/org/postgresql/PG_Stream.java
index 11781dba969..0c92bff9449 100644
--- a/src/interfaces/jdbc/org/postgresql/PG_Stream.java
+++ b/src/interfaces/jdbc/org/postgresql/PG_Stream.java
@@ -10,7 +10,7 @@ import org.postgresql.core.*;
 import org.postgresql.util.*;
 
 /*
- * $Id: PG_Stream.java,v 1.17 2002/08/20 04:26:02 barry Exp $
+ * $Id: PG_Stream.java,v 1.18 2003/02/27 05:45:44 barry Exp $
  *
  * This class is used by Connection & PGlobj for communicating with the
  * backend.
@@ -20,9 +20,11 @@ import org.postgresql.util.*;
 //	This class handles all the Streamed I/O for a org.postgresql connection
 public class PG_Stream
 {
-	private Socket connection;
-	private InputStream pg_input;
-	private BufferedOutputStream pg_output;
+	public String host;
+	public int port;
+	public Socket connection;
+	public InputStream pg_input;
+	public BufferedOutputStream pg_output;
 	private byte[] byte_buf = new byte[8*1024];
 
 	/*
@@ -33,8 +35,10 @@ public class PG_Stream
 	 * @param port the port number that the postmaster is sitting on
 	 * @exception IOException if an IOException occurs below it.
 	 */
-	public PG_Stream(String host, int port) throws IOException
+	public PG_Stream(String p_host, int p_port) throws IOException
 	{
+		host = p_host;
+		port = p_port;
 		connection = new Socket(host, port);
 
 		// Submitted by Jason Venner <jason@idiom.com> adds a 10x speed
diff --git a/src/interfaces/jdbc/org/postgresql/errors.properties b/src/interfaces/jdbc/org/postgresql/errors.properties
index 589baf704bf..cb6cdc38421 100644
--- a/src/interfaces/jdbc/org/postgresql/errors.properties
+++ b/src/interfaces/jdbc/org/postgresql/errors.properties
@@ -19,6 +19,8 @@ postgresql.con.multres:Cannot handle multiple result groups.
 postgresql.con.pass:The password property is missing. It is mandatory.
 postgresql.con.refused:Connection refused. Check that the hostname and port are correct and that the postmaster is accepting TCP/IP connections.
 postgresql.con.setup:Protocol error. Session setup failed.
+postgresql.con.sslfail:An error occured while getting setting up the SSL connection.
+postgresql.con.sslnotsupported:The server does not support SSL
 postgresql.con.strobj:The object could not be stored. Check that any tables required have already been created in the database.
 postgresql.con.strobjex:Failed to store object - {0}
 postgresql.con.toolong:The SQL Statement is too long - {0}
diff --git a/src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1Connection.java b/src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1Connection.java
index 7191f26a0e9..328a5371eba 100644
--- a/src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1Connection.java
+++ b/src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1Connection.java
@@ -14,7 +14,7 @@ import org.postgresql.largeobject.LargeObjectManager;
 import org.postgresql.util.*;
 
 
-/* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc1/Attic/AbstractJdbc1Connection.java,v 1.15 2003/02/05 11:12:39 davec Exp $
+/* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc1/Attic/AbstractJdbc1Connection.java,v 1.16 2003/02/27 05:45:44 barry Exp $
  * This class defines methods of the jdbc1 specification.  This class is
  * extended by org.postgresql.jdbc2.AbstractJdbc2Connection which adds the jdbc2
  * methods.  The real Connection class (for jdbc1) is org.postgresql.jdbc1.Jdbc1Connection
@@ -34,6 +34,7 @@ public abstract class AbstractJdbc1Connection implements org.postgresql.PGConnec
 	protected String PG_DATABASE;
 	protected boolean PG_STATUS;
 	protected String compatible;
+	protected boolean useSSL;
 
 	// The PID an cancellation key we get from the backend process
 	protected int pid;
@@ -100,7 +101,7 @@ public abstract class AbstractJdbc1Connection implements org.postgresql.PGConnec
 	 * @exception SQLException if a database access error occurs
 	 */
 	public void openConnection(String host, int port, Properties info, String database, String url, org.postgresql.Driver d) throws SQLException
-	{
+	  {
 		firstWarning = null;
 
 		// Throw an exception if the user or password properties are missing
@@ -121,6 +122,15 @@ public abstract class AbstractJdbc1Connection implements org.postgresql.PGConnec
 		PG_HOST = host;
 		PG_STATUS = CONNECTION_BAD;
 
+		if (info.getProperty("ssl") != null && this_driver.sslEnabled())
+		{
+			useSSL = true;
+		}
+		else
+		{
+			useSSL = false;
+		}
+
 		if (info.getProperty("compatible") == null)
 		{
 			compatible = d.getMajorVersion() + "." + d.getMinorVersion();
@@ -156,6 +166,11 @@ public abstract class AbstractJdbc1Connection implements org.postgresql.PGConnec
 		//Print out the driver version number
 		if (org.postgresql.Driver.logInfo)
 			org.postgresql.Driver.info(org.postgresql.Driver.getVersion());
+		if (org.postgresql.Driver.logDebug) {
+			org.postgresql.Driver.debug("    ssl = " + useSSL);
+			org.postgresql.Driver.debug("    compatible = " + compatible);
+			org.postgresql.Driver.debug("    loglevel = " + l_logLevel);
+		}
 
 		// Now make the initial connection
 		try
@@ -174,6 +189,56 @@ public abstract class AbstractJdbc1Connection implements org.postgresql.PGConnec
 			throw new PSQLException ("postgresql.con.failed", e);
 		}
 
+		// Now we need to construct and send an ssl startup packet
+		try
+		{
+			if (useSSL) {
+				if (org.postgresql.Driver.logDebug)
+					org.postgresql.Driver.debug("Asking server if it supports ssl");
+				pg_stream.SendInteger(8,4);
+				pg_stream.SendInteger(80877103,4);
+
+				// now flush the ssl packets to the backend
+				pg_stream.flush();
+
+				// Now get the response from the backend, either an error message
+				// or an authentication request
+				int beresp = pg_stream.ReceiveChar();
+				if (org.postgresql.Driver.logDebug)
+					org.postgresql.Driver.debug("Server response was (S=Yes,N=No): "+(char)beresp);
+				switch (beresp)
+					{
+					case 'E':
+						// An error occured, so pass the error message to the
+						// user.
+						//
+						// The most common one to be thrown here is:
+						// "User authentication failed"
+						//
+						throw new PSQLException("postgresql.con.misc", pg_stream.ReceiveString(encoding));
+						
+					case 'N':
+						// Server does not support ssl
+						throw new PSQLException("postgresql.con.sslnotsupported");
+						
+					case 'S':
+						// Server supports ssl
+						if (org.postgresql.Driver.logDebug)
+							org.postgresql.Driver.debug("server does support ssl");
+						org.postgresql.Driver.makeSSL(pg_stream);
+						break;
+
+					default:
+						throw new PSQLException("postgresql.con.sslfail");
+					}
+			}
+		}
+		catch (IOException e)
+		{
+			throw new PSQLException("postgresql.con.failed", e);
+		}
+
+
 		// Now we need to construct and send a startup packet
 		try
 		{
diff --git a/src/interfaces/jdbc/org/postgresql/util/PSQLException.java b/src/interfaces/jdbc/org/postgresql/util/PSQLException.java
index 46c71fab11f..d8fabd904a5 100644
--- a/src/interfaces/jdbc/org/postgresql/util/PSQLException.java
+++ b/src/interfaces/jdbc/org/postgresql/util/PSQLException.java
@@ -27,7 +27,7 @@ public class PSQLException extends SQLException
 	 */
 	public PSQLException(String error, Object[] args)
 	{
-		//super();
+		super();
 		translate(error, args);
 	}
 
-- 
GitLab