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