Thread: A solution to the SSL customizing problem
I have read previous posts and the proposal that the user should supply a SSLSocketFactory. Besides the obvious question - how - I see an additional problem in that approach: It's anything but user friendly. All you want is a secure connection to postgres and you end up digging deeply into the JSSE spec. Even if all you want is turn validation off, you will have to have some knowledge of the spec. The really hard bit comes when you want to retrieve the certificate from the server. You are using SSL because you are concerned about security, so you probably want checking. Now you will have to give your own TrustManager implementation that saves the certificate received for checking. Furthermore, you will have to save that certificate somehow and you'll end up handling keystores and stuff. What an effort just for this feature! ********** I propose a different solution. There are three modes I can think of when opening a connection: 1. Disabling validation. Not interesting for a user with security concerns - you loose half of SSL's functionality. 2. Enabling validation (standard) The problem is that you need to get your hands on the server's certificate. If you distribute an applet for a single server, like I do, you can ship the server's certificate with your applet. One thing you certainly don't want is to tell people how to insert it with a command line tool (keytool) and so a nice solution is to provide it in a keystore and point jdbc to it. Furthermore you are likely not to have write permissions on the standard keystore (if you want to update it in your applet) because it is in a subdir of java_home. 3. Trust and save (disable validation and save received key) Same as with openssl and known_hosts, you accept the host the first time you connect, save its certificate and use the standard mode from then on. Implementation of this feature is easier within the driver because you have access to the SSL connection and can retrieve the certificate(s). As a user you can't just pull it off the postgres port because postgres doesn't start off in SSL mode. You will have to implement a TrustManager. ************** What I did for the driver is the following: Anologously to javax.net.ssl.trustStore and javax.net.ssl.trustStorePassword I introduced org.postgresql.trustStore and org.postgresql.trustStorePassword Now you can specify your own trustStore for pgjdbc connections. If it doesn't exist, it will be created for you. Additionally I introduced the trust-and-save mode. Currently I look for a property org.postgresql.ssl_trustandsave but this would better be handled in a URL parameter like "ssl_trustandsave". When the driver is in trust-and-save mode, it will trust any server you connect to and save its certificate in the trustStore. In an application, you would first run in normal mode. If the connection fails, you ask the user if he wants to trust the unknown key and if he agrees, you connect again with ssl_trustandsave mode activated and the key is saved. In subsequent connections, you turn ssl_trustandsave off. ********** Implementation: I introduced a class org.postgresql.util.PGSSLSocketFactory and changed one line in org.postgresql.Driver and method makeSSL from javax.net.ssl.SSLSocketFactory factory = (javax.net.ssl.SSLSocketFactory) javax.net.ssl.SSLSocketFactory.getDefault(); to org.postgresql.util.PGSSLSocketFactory factory = org.postgresql.util.PGSSLSocketFactory.getInstance(); . What do you think? Kind Regards, Uli
Attachment
Ulrich Meis <kenobi@halifax.rwth-aachen.de> writes: > I propose a different solution. One small question --- have you checked that this behaves reasonably with both a CVS-tip postmaster and prior releases? CVS tip does do certificate presentation and checking, whereas that stuff was mistakenly disabled in 7.4. (I think all the relevant changes are present in 8.0beta3, but not earlier.) regards, tom lane
Ulrich Meis wrote: > I propose a different solution. [...] This seems like a subset of the other solutions suggested. If you can configure the SSLSocketFactory used, you can use a class just like the one you provided without requiring that everyone uses it. I'd rather see a flexible solution that can be customized to the needs of a particular application. For example, the code that you provided assumes that there is one global truststore and that it is a Java-format keystore that is accessible locally. Neither of those are necessarily true. -O
On Monday 11 October 2004 18:40, Tom Lane wrote: > Ulrich Meis <kenobi@halifax.rwth-aachen.de> writes: > > I propose a different solution. > > One small question --- have you checked that this behaves reasonably > with both a CVS-tip postmaster and prior releases? CVS tip does do > certificate presentation and checking, whereas that stuff was > mistakenly disabled in 7.4. (I think all the relevant changes are > present in 8.0beta3, but not earlier.) No, I only tested on 7.4.5 (plz see notes below). But it does present its certificate, I saved it a hundred times ;-) I don't think Java would accept a SSL connection without presentation of a certificate. Using a different trustStore and trust policy by itself doesn't change the behavior towards postmaster.The custom SSLContext and the resulting SSLContextFactory use the defaults in all other cases, so in effect there shouldn't be any difference in behavior. In other words if the postmaster works with the current driver, it will work with the customization, too. If you are concerned about getting errors in connections that don't use one of the introduced features, please take a look at the first three lines of createSocket ********* public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException { if ((System.getProperty("org.postgresql.Driver.trustStore") == null) && (System.getProperty("org.postgresql.ssl_trustandsave") == null)) return ((SSLSocketFactory) SSLSocketFactory.getDefault()).createSocket(socket, host, port, autoClose); ********* If no custom behavior is specified, the class uses the same line of code as you can find in the current Driver.java. That way bugs in the new class are only exposed if you use a feature. Uli
On Monday 11 October 2004 22:59, you wrote: > Ulrich Meis wrote: > > I propose a different solution. [...] > > This seems like a subset of the other solutions suggested. If you can > configure the SSLSocketFactory used, you can use a class just like the > one you provided without requiring that everyone uses it. Viewpoint one: configurability. I agree, someone could write that same class. There is just one thing that will take a few more lines of code: retrieving the certificate from the server. In an application you don't get your hands on the actual socket that the driver uses. The only way I see to get the server's certificate is to implement your own TrustManager. Surely, providing your own SSLSocketFactory is more configurable. Why not offer providing a SSLSocketFactory, too? Viewpoint two: usability. I think all most people want is to have an SSL connection running and don't care too much about the details. The proposed features allow them to handle the problem that everyone runs into when using SSL: how to get the server's certificate into your trustStore? This is a big problem for applet programmers because applets should just load&work, you can't tell people about keytool. With this implementation you can either ship the certificate with the applet or you can use trustandsave on new servers. > I'd rather see a flexible solution that can be customized to the needs > of a particular application. For example, the code that you provided > assumes that there is one global truststore and that it is a Java-format > keystore that is accessible locally. Neither of those are necessarily true. For these cases it would be nice if you could provide your own KeyStore object...the question is just how. ************ Ok...here comes another solution. You let the user provide a handler and introduce an id in the URL in case the user needs per connection handling. In the Driver class you offer a method(make it empty if compiled without SSL): setPGSSLHandler(PGSSLHandler handler) { pgsslhandler=handler;} and a URL parameter conid=123 and abstract class PGSSLHandler { public static final int STANDARD=0; public static final int CUSTOMFACTORY=1; public static final int CUSTOMSTORE=2; abstract public int getHandleType(int conid); abstract public boolean getTrustAndSave(int conid); abstract public KeyStore getKeyStore(int conid); abstract public SSLSocketFactory getSSLSocketFactory(int conid); } This is really flexible, you don't need to do any classloading and you don't need to manage multiple user objects in the driver. How's that? Uli
On Mon, 11 Oct 2004, Tom Lane wrote: > Ulrich Meis <kenobi@halifax.rwth-aachen.de> writes: > > I propose a different solution. > > One small question --- have you checked that this behaves reasonably > with both a CVS-tip postmaster and prior releases? CVS tip does do > certificate presentation and checking, whereas that stuff was > mistakenly disabled in 7.4. (I think all the relevant changes are > present in 8.0beta3, but not earlier.) > The problem he's talking about is Java's default verification of the server certificate by the client, not presentation of a client certificate to be checked by the server. Currently the JDBC driver does not handle client certificates at all. Kris Jurka
Ulrich Meis wrote: > On Monday 11 October 2004 22:59, you wrote: > >>Ulrich Meis wrote: >> >>>I propose a different solution. [...] >> >>This seems like a subset of the other solutions suggested. If you can >>configure the SSLSocketFactory used, you can use a class just like the >>one you provided without requiring that everyone uses it. > > > Viewpoint one: configurability. > > I agree, someone could write that same class. [...] We could provide such a helper class that implements some policy along the lines of what your patch implements as a convenience to users, but I don't think that behaviour should be hardwired into the driver. It's not the place of the driver to make that sort of policy decision. It needs to be configurable, and the obvious place to do the configuration is to allow the user to provide their own SSLSocketFactory level, since that gives you complete freedom to customize whichever bits of the SSL handshake you want to. So I suggest you look at solving the "how do I give the driver an appropriate SSLSocketFactory" problem first. Once that is solved, the particular configurable behaviour you want can be easily implemented. -O
On Mon, 11 Oct 2004, Ulrich Meis wrote: > 1. Disabling validation. > > Not interesting for a user with security concerns - you loose half of SSL's > functionality. There is certainly a demand for this. While you do lose security it is the default behavior for other pg clients (notably libpq). > 2. Enabling validation (standard) > The problem is that you need to get your hands on the server's certificate. > If you distribute an applet for a single server, like I do, you can ship the > server's certificate with your applet. One thing you certainly don't want is > to tell people how to insert it with a command line tool (keytool) and so a > nice solution is to provide it in a keystore and point jdbc to it. > Furthermore you are likely not to have write permissions on the standard > keystore (if you want to update it in your applet) because it is in a subdir > of java_home. I guess the question is what about keystores that are not plain files. Providing a general means of allowing the client to provide the certs from any source they desire. Could you elaborate more on how you package the keystore with your applet? I wasn't aware you could do that. > 3. Trust and save (disable validation and save received key) > Same as with openssl and known_hosts, you accept the host the first time you > connect, save its certificate and use the standard mode from then on. > Implementation of this feature is easier within the driver because you have > access to the SSL connection and can retrieve the certificate(s). As a user > you can't just pull it off the postgres port because postgres doesn't start > off in SSL mode. You will have to implement a TrustManager. I don't understand what the purpose of this is. Why save the cert if you aren't going to validate? Don't you need some kind of confirm callback to allow the user to do some kind of verification of the cert? Kris Jurka
On Tuesday 12 October 2004 04:57, Oliver Jowett wrote: > So I suggest you look at solving the "how do I give the driver an > appropriate SSLSocketFactory" problem first. Once that is solved, the > particular configurable behaviour you want can be easily implemented. How about my suggestions with the abstract handler class? I know it was a long post ;-) Uli
Ulrich Meis wrote: > On Tuesday 12 October 2004 04:57, Oliver Jowett wrote: > > >>So I suggest you look at solving the "how do I give the driver an >>appropriate SSLSocketFactory" problem first. Once that is solved, the >>particular configurable behaviour you want can be easily implemented. > > > How about my suggestions with the abstract handler class? > I know it was a long post ;-) Namely: > In the Driver class you offer a method(make it empty if compiled without SSL): > > setPGSSLHandler(PGSSLHandler handler) { > pgsslhandler=handler;} > > abstract class PGSSLHandler { > > public static final int STANDARD=0; > public static final int CUSTOMFACTORY=1; > public static final int CUSTOMSTORE=2; > > abstract public int getHandleType(int conid); > abstract public boolean getTrustAndSave(int conid); > > abstract public KeyStore getKeyStore(int conid); > abstract public SSLSocketFactory getSSLSocketFactory(int conid); > > } How do you arrange for setPGSSLHandler to be called if you are in a managed environment that does not know anything about the postgresql driver beyond the standard JDBC interfaces? The "conid" stuff looks really grotty. How do you coordinate the URL-level configuration with the PGSSLHandler implementation? Why is anything but getSSLSocketFactory() needed? You can implement whatever keystore/truststore policy you want via a SSLSocketFactory. The only new thing there is really the conid stuff, and I'd rather deal with classloader issues (specify the class by name, loaded by the driver) than have to deal with managing magic opaque unique configuration keys and a postgresql-specific interface. -O
On Monday 11 October 2004 22:07, Kris Jurka wrote: > On Mon, 11 Oct 2004, Ulrich Meis wrote: > > 1. Disabling validation. > > > > Not interesting for a user with security concerns - you loose half of > > SSL's functionality. > > There is certainly a demand for this. While you do lose security it > is the default behavior for other pg clients (notably libpq). > I would guess that if you care enough about security to begin with, you would probably want to take the whole package - assuming it's not too much work. > > 2. Enabling validation (standard) > > The problem is that you need to get your hands on the server's > > certificate. If you distribute an applet for a single server, like I do, > > you can ship the server's certificate with your applet. One thing you > > certainly don't want is to tell people how to insert it with a command > > line tool (keytool) and so a nice solution is to provide it in a keystore > > and point jdbc to it. Furthermore you are likely not to have write > > permissions on the standard keystore (if you want to update it in your > > applet) because it is in a subdir of java_home. > > I guess the question is what about keystores that are not plain files. > Providing a general means of allowing the client to provide the certs from > any source they desire. > I suggested this version because it seemed to be a problem to offer an interface to the user. If you can offer one it's a nice feature to be able to supply a keystore. I made a suggestion in another post of this thread where the user can supply either a SSLContextFactory or a KeyStore. I am curious what you'll think about that. > Could you elaborate more on how you package the keystore with your applet? > I wasn't aware you could do that. Well, without my proposal you can't ;-) With it, you can put a keystore file with the certificate in the same dir where the applet is and read that file via http(s). If you could provide pgjdbc with a keystore object, you would only have to load it with the file. Here, you have to save the keystore to some temp directory, set org.postgresql.trustStore to the temp file, make the connection and finally delete the temp file. Needs a signed applet. > > > 3. Trust and save (disable validation and save received key) > > Same as with openssl and known_hosts, you accept the host the first time > > you connect, save its certificate and use the standard mode from then on. > > Implementation of this feature is easier within the driver because you > > have access to the SSL connection and can retrieve the certificate(s). As > > a user you can't just pull it off the postgres port because postgres > > doesn't start off in SSL mode. You will have to implement a TrustManager. > > I don't understand what the purpose of this is. Why save the cert if you > aren't going to validate? Don't you need some kind of confirm callback to > allow the user to do some kind of verification of the cert? After saving the cert and in subsequent connections you would switch back to normal mode with validation. Now that the certificate is in your store, everything goes fine. Looks like this: 1. Applet tries "jdbc://foo/bar" and fails with no certificate exception. 2. Applet connects to "jdbc://foo/bar?ssl_trustandsave" and immediately afterwards closes the connection 3. Applet loads keystore, identifies the new certificate and tells user "Warning, can't verify identity of DB server, continue?". If the answer is no, it will delete the entry from the store. (if confirmed)5. Applet connects to "jdbc://foo/bar" and will succeed. The shorter version (if you don't care about cert details) 2. Applet asks user "Can't verify identity of DB server, continue?" 3. Applet connects this once with trustandsave Uli
On Tuesday 12 October 2004 06:34, Oliver Jowett wrote: > Ulrich Meis wrote: > > On Tuesday 12 October 2004 04:57, Oliver Jowett wrote: > >>So I suggest you look at solving the "how do I give the driver an > >>appropriate SSLSocketFactory" problem first. Once that is solved, the > >>particular configurable behaviour you want can be easily implemented. > > > > How about my suggestions with the abstract handler class? > > I know it was a long post ;-) > > Namely: > > In the Driver class you offer a method(make it empty if compiled without > > SSL): > > > > setPGSSLHandler(PGSSLHandler handler) { > > pgsslhandler=handler;} > > > > > > abstract class PGSSLHandler { > > > > public static final int STANDARD=0; > > public static final int CUSTOMFACTORY=1; > > public static final int CUSTOMSTORE=2; > > > > abstract public int getHandleType(int conid); > > abstract public boolean getTrustAndSave(int conid); > > > > abstract public KeyStore getKeyStore(int conid); > > abstract public SSLSocketFactory getSSLSocketFactory(int conid); > > > > } > > How do you arrange for setPGSSLHandler to be called if you are in a > managed environment that does not know anything about the postgresql > driver beyond the standard JDBC interfaces? How about making it a static method? You could only provide one handler per JVM but that will probably do in most cases. > > The "conid" stuff looks really grotty. How do you coordinate the > URL-level configuration with the PGSSLHandler implementation? That coordination is up to the user. He provides the conid in the URL and the driver just forwards it to the handler method. I don't see a problem here. > Why is anything but getSSLSocketFactory() needed? You can implement > whatever keystore/truststore policy you want via a SSLSocketFactory. I just thought it would be a nice feature because it spares people a lot of work, nothing else ;-) > The only new thing there is really the conid stuff, and I'd rather deal > with classloader issues (specify the class by name, loaded by the > driver) than have to deal with managing magic opaque unique > configuration keys and a postgresql-specific interface. The way I thought about it was that the connection just forwards the conid to makeSSL. Then there is no need for the driver to manage it. I thought - after reading previous posts about this - loading a user class would not be an option because some environments might not allow the driver to do so? Uli
Ulrich Meis wrote: > On Tuesday 12 October 2004 06:34, Oliver Jowett wrote: > >>How do you arrange for setPGSSLHandler to be called if you are in a >>managed environment that does not know anything about the postgresql >>driver beyond the standard JDBC interfaces? > > > How about making it a static method? > You could only provide one handler per JVM but that will probably do in most > cases. That doesn't help.. The situation I am thinking of is when you are configuring a DataSource for use in a managed environment, e.g. appserver. The appserver knows nothing about the driver it is using beyond the standard JDBC interfaces (and perhaps not even that, if it's using a JCA wrapper). This breaks as soon as you have a magic method that needs to be called on the driver to configure SSL properties. There is no way to teach the appserver how to do this without hardwiring dependencies on the PG way of doing things.. which does not make for a very portable appserver! >>The "conid" stuff looks really grotty. How do you coordinate the >>URL-level configuration with the PGSSLHandler implementation? > > That coordination is up to the user. > He provides the conid in the URL and the driver just forwards it to the > handler method. I don't see a problem here. It's a maintenance nightmare. The configuration used by the handler method has to somehow be kept in sync with the user configuration (the URL). However, the handler method's configuration is not managed by the same mechanisms that the user configuration is (namely "edit the JDBC URL"). >>Why is anything but getSSLSocketFactory() needed? You can implement >>whatever keystore/truststore policy you want via a SSLSocketFactory. > > I just thought it would be a nice feature because it spares people a lot of > work, nothing else ;-) If you want to provide a default policy, you can do it as a separate class. I don't see any reason for the extra methods in the driver-level interface. >>The only new thing there is really the conid stuff, and I'd rather deal >>with classloader issues (specify the class by name, loaded by the >>driver) than have to deal with managing magic opaque unique >>configuration keys and a postgresql-specific interface. > > > The way I thought about it was that the connection just forwards the conid to > makeSSL. Then there is no need for the driver to manage it. > > I thought - after reading previous posts about this - loading a user class > would not be an option because some environments might not allow the driver > to do so? The problem arises when the user code is in a disjoint classloader to the driver's classloader. The driver cannot necessary load classes that the user code can see. This is an issue if the handler is doing something application-specific & is therefore part of the user code. There is a related problem to do with passing around classnames in that you cannot do multiple configurations just by instantiating new handlers with different details, you either have to have lots of wrapper implementations or something like the 'conid' scheme above along with all the associated baggage. ... Thinking about this a bit more, it seems to me that the use case that hits the classloader issue (user code wanting feedback from the driver via a nonstandard callback in a managed environment) is actually a hard problem to solve anyway. It seems like if you need to do that you're placing too high a burden on the appserver to magically know about the behaviour of the nonstandard callback. The SSL configuration is really a configuration property of the connection/connection pool, not something that should be managed by user code in a managed environment. So maybe we should forget about that case for the moment. Anyway.. I'd be OK with something like: - a URL parameter that specifies name of a subclass of SSLSocketFactory to use, and - a method on the PG DataSource implementations that allows setting a SSLSocketFactory *instance*. This instance is passed via a PG-private API to the driver when creating connections from the DataSource. This overrides use of the URL parameter. i.e. you have: if (SSLSocketFactory instance provided via DataSource) use provided instance else if (SSLSocketFactory classname provided via URL) create new instance by name and use it else use default SSLSocketFactory DataSource users configure the environment to instantiate a SSLSocketFactory, configure it via reflection + javabean accessors, set it via reflection on the DataSource. I think this is within the reach of generic configuration mechanisms, they only need to know how to manipulate the configuration objects via javabean accessors and don't need to know anything specific to PG. -O
Hi, Oliver, On Tue, 12 Oct 2004 21:23:58 +1300 Oliver Jowett <oliver@opencloud.com> wrote: > The situation I am thinking of is when you are configuring a DataSource > for use in a managed environment, e.g. appserver. The appserver knows > nothing about the driver it is using beyond the standard JDBC interfaces > (and perhaps not even that, if it's using a JCA wrapper). This breaks as > soon as you have a magic method that needs to be called on the driver to > configure SSL properties. There is no way to teach the appserver how to > do this without hardwiring dependencies on the PG way of doing things.. > which does not make for a very portable appserver! That's true. We were very happy to learn that the JBoss connection pooling uses wrappers that have a method to get the underlying original connection, so we could call addDataType() to add the postGIS classes. This left us with ugly code like: public static PGConnection unWrapPGConn(Connection conn) { PGConnection result = null; if (conn == null) { log.error("trying to unWrapPGConn(null)!"); return null; } if (conn instanceof PGConnection) { result = (PGConnection) conn; } else if (conn instanceof WrappedConnection) { WrappedConnection w = (WrappedConnection) conn; try { result = unWrapPGConn(w.getUnderlyingConnection()); } catch (SQLException e) { log.error("unWrapPGConn: Could not get underyling SQL connection!"); log.error(e.toString()); } } if (result == null) { log.warn("unWrapPGConn called with unknown connection type."); log.warn("given Connection Class is: " + conn.getClass().getPackage() + "." + conn.getClass().getName()); } return result; } So, you can see that this is a real world problem. Imagine we would need to have our App portable to other app servers - this would not only complicate the source above. We also would need to provide the appropriate classes in the other environments, so the classloader can find them for the instanceof call - this could imply strange licence issues. Additionally, we currently have different apps with different needs running in the same app server - so fine grained configurability is a must. We cannot provide a single static method because the apps are developed and deployed independently. FIN, Markus -- markus schaber | dipl. informatiker logi-track ag | rennweg 14-16 | ch 8001 zürich phone +41-43-888 62 52 | fax +41-43-888 62 53 mailto:schabios@logi-track.com | www.logi-track.com
On Tuesday 12 October 2004 10:23, Oliver Jowett wrote: > >>The "conid" stuff looks really grotty. How do you coordinate the > >>URL-level configuration with the PGSSLHandler implementation? > > > > That coordination is up to the user. > > He provides the conid in the URL and the driver just forwards it to the > > handler method. I don't see a problem here. > > It's a maintenance nightmare. The configuration used by the handler > method has to somehow be kept in sync with the user configuration (the > URL). However, the handler method's configuration is not managed by the > same mechanisms that the user configuration is (namely "edit the JDBC > URL"). In an app server environment I would think that either the user obtains a datasource object and provides handler and connection id as in datasource.setjdbcurl("jdbc://server1/db1?handlerclass=mysslhandler&conid=1");con1=... datasource.setjdbcurl("jdbc://server1/db1?handlerclass=mysslhandlerconid=2");con2=... or uses a configuration file like <datasource1> <jdbcurl>.......conid=1</jdbcurl> <datasource2> <jdbcurl>......conid=2</jdbcurl> where in this case the connection id identifies a database and not a particular connection. Maybe it would better be called dbid. > There is a related problem to do with passing around classnames in that > you cannot do multiple configurations just by instantiating new handlers > with different details, you either have to have lots of wrapper > implementations or something like the 'conid' scheme above along with > all the associated baggage. If you can forward a class name from the URL to makeSSL, you can forward an id as well and avoid the wrapper classes. Associated baggage stays the same. > > ... > > Thinking about this a bit more, it seems to me that the use case that > hits the classloader issue (user code wanting feedback from the driver > via a nonstandard callback in a managed environment) is actually a hard > problem to solve anyway. It seems like if you need to do that you're > placing too high a burden on the appserver to magically know about the > behaviour of the nonstandard callback. The SSL configuration is really a > configuration property of the connection/connection pool, not something > that should be managed by user code in a managed environment. So maybe > we should forget about that case for the moment. Anyway.. I agree that if you want to customize SSL settings and use multiple configurations, you probably don't want to use the app server's connection pool anyway. > > I'd be OK with something like: > > - a URL parameter that specifies name of a subclass of SSLSocketFactory > to use, and > - a method on the PG DataSource implementations that allows setting a > SSLSocketFactory *instance*. This instance is passed via a PG-private > API to the driver when creating connections from the DataSource. This > overrides use of the URL parameter. Sounds good to me. I would suggest an additional URL parameter and method on PG DataSource for the connection/database id. Another completely different idea that I haven't tested and/or thought through yet: How about passing a JNDI name in the URL that users and/or app servers bind their custom SSLSocketFactory to? something like context.bind("org/somewhere/sslfactory_companyA",new mysslfactory("companyA")); jdbcurl = "jdbc:postgresql://companyA/workdb?sslsocketfactory=org/somewhere/sslfactory_companyA"; con=... context.bind("org/somewhere/sslfactory_companyB",new mysslfactory("companyB"); jdbcurl = "jdbc:postgresql://companyB/workdb?sslsocketfactory=org/somewhere/sslfactory_companyB"; con=... Then you could forget about ids and the second class loading option. JNDI is included since jdk1.3 and there's a jndi.jar for jdk1.2. Uli
Ulrich Meis wrote: > On Tuesday 12 October 2004 10:23, Oliver Jowett wrote: > > >>>>The "conid" stuff looks really grotty. How do you coordinate the >>>>URL-level configuration with the PGSSLHandler implementation? >>> >>>That coordination is up to the user. >>>He provides the conid in the URL and the driver just forwards it to the >>>handler method. I don't see a problem here. >> >>It's a maintenance nightmare. The configuration used by the handler >>method has to somehow be kept in sync with the user configuration (the >>URL). However, the handler method's configuration is not managed by the >>same mechanisms that the user configuration is (namely "edit the JDBC >>URL"). > > > In an app server environment I would think that either the user obtains a > datasource object and provides handler and connection id as in > > datasource.setjdbcurl("jdbc://server1/db1?handlerclass=mysslhandler&conid=1");con1=... > datasource.setjdbcurl("jdbc://server1/db1?handlerclass=mysslhandlerconid=2");con2=... This is unlikely, the datasources are usually created and configured by the appserver. That's one of the reasons for having a managed environment in the first place.. > or uses a configuration file like > > <datasource1> > <jdbcurl>.......conid=1</jdbcurl> > > <datasource2> > <jdbcurl>......conid=2</jdbcurl> A configuration file like this is the usual approach. > where in this case the connection id identifies a database and not a > particular connection. Maybe it would better be called dbid. OK: so an administrator wants to add a new database that needs to use SSL. They reconfigure the appserver with details of the driver & URL to use, perhaps by editing a config file and restarting, perhaps by talking to the running server via JMX, web interface, etc. The URL includes a new 'conid' value. To pick this value the administrator has to check all the other connections by hand and find a new unique value. Fun! Now, how does the administrator configure 'mysslhandler' to handle the new 'conid' or 'dbid' they have just configured? Keep in mind that the only mechanism they have access to for configuring things on the machine may be whatever is provided by the appserver (JMX etc).. so "edit this local file" or "change these system properties" are not useful answers. >>I'd be OK with something like: >> >>- a URL parameter that specifies name of a subclass of SSLSocketFactory >>to use, and >>- a method on the PG DataSource implementations that allows setting a >>SSLSocketFactory *instance*. This instance is passed via a PG-private >>API to the driver when creating connections from the DataSource. This >>overrides use of the URL parameter. > > > Sounds good to me. > I would suggest an additional URL parameter and method on PG DataSource for > the connection/database id. See above :) If you want to pass configuration information to the instantiated class (and that is really what you're trying to do here, right?), a better way seems to be to include all of that information directly in the URL rather than adding more indirection. i.e. something like: jdbc:postgresql://server:port/dbname?sslfactory=my.factory&sslfactoryargs=truststore=foo,behaviour=bar Or even just provide the whole URL-derived Properties object to the instantiated class and let it pick out whichever properties it wants. Though I don't like passing all Properties as it makes the set of valid properties for a connection unpredictable. Among other things this means you can't correctly implement Driver.getPropertyInfo(). > Another completely different idea that I haven't tested and/or thought through > yet: > > How about passing a JNDI name in the URL that users and/or app servers bind > their custom SSLSocketFactory to? You have no guarantees that there is a JNDI provider available or that it works as you expect from the driver's context or that you even have permission to bind into it. So this seems like a really bad idea. As a concrete example, if you try to do this in our appserver things are going to blow up horribly -- there is a separate JNDI tree per application, applications have a readonly view of their tree, and non-application code such as JDBC drivers has no guarantees about which tree (if any) is visible when they are executing. > JNDI is included since jdk1.3 and there's a jndi.jar for jdk1.2. The infrastructure is there, but the default providers leave a lot to be desired. There's no purely in-memory provider, for example. -O
On Tuesday 12 October 2004 23:12, Oliver Jowett wrote: > Ulrich Meis wrote: > > On Tuesday 12 October 2004 10:23, Oliver Jowett wrote: > [...] > > Sounds good to me. > > I would suggest an additional URL parameter and method on PG DataSource > > for the connection/database id. > > See above :) Alright, finally I got your point ;-) > If you want to pass configuration information to the instantiated class > (and that is really what you're trying to do here, right?), a better way > seems to be to include all of that information directly in the URL > rather than adding more indirection. i.e. something like: > > jdbc:postgresql://server:port/dbname?sslfactory=my.factory&sslfactoryargs=t >ruststore=foo,behaviour=bar I totally agree. > Or even just provide the whole URL-derived Properties object to the > instantiated class and let it pick out whichever properties it wants. > Though I don't like passing all Properties as it makes the set of valid > properties for a connection unpredictable. Among other things this means > you can't correctly implement Driver.getPropertyInfo(). How about passing 1. the URL without parameters so the factory knows which server&database it's dealing with 2. a sslfactoryargs parameter as you described? That way the factory can easily identify which certificate belongs to which server. > > Another completely different idea that I haven't tested and/or thought > > through yet: > > > > How about passing a JNDI name in the URL that users and/or app servers > > bind their custom SSLSocketFactory to? > > You have no guarantees that there is a JNDI provider available or that > it works as you expect from the driver's context or that you even have > permission to bind into it. So this seems like a really bad idea. Again, agreed. I wanted to come around the problem that the user cannot provide his own factory in a managed environment, but seeing that this functionality isn't desired anyway, the problem vanishes. > As a concrete example, if you try to do this in our appserver things are > going to blow up horribly -- there is a separate JNDI tree per > application, applications have a readonly view of their tree, and > non-application code such as JDBC drivers has no guarantees about which > tree (if any) is visible when they are executing. > > > JNDI is included since jdk1.3 and there's a jndi.jar for jdk1.2. > > The infrastructure is there, but the default providers leave a lot to be > desired. There's no purely in-memory provider, for example. My thoughts were upon simple-jndi from osjava for a stand alone application. Uli
Now what I got from the discussion: 1. New URL parameters: sslfactory=a.class.name sslfactoryargs=driver=does,not=care These are forwarded to 2. makeSSL which does something like if (sslfactory!=null) { //sslfactory url parameter factory = instantiated class, handle exceptions } else if (DSfactory!=null) { // factory set via DataSource factory = dsfactory; } else factory = SSLSocketFactory.getDefault(); if (factory instanceof PGSSLHandler) { ((PGSSLHandler)factory).setPGURL(...); ((PGSSLHandler)factory).setPGargs(...); } java.net.Socket newConnection = factory.createsocket(....); .... 3. a new abstract class abstract PGSSLHandler extends SSLSocketFactory { public void setPGURL(String url_no_parameters); public void setPGargs(String args); } 4. in Driver protected setSSLSocketFactory(SSLSocketfactory factory) { DSfactory=factory; } 5. in BaseDataSource public void setPG_SSLSocketFactory(SSLSocketFactory factory) { ((org.postgresql.Driver)DriverManager.getDriver("jdbc:postgresql://server/db")).setSSLSocketFactory(factory); } How does that sound? I would offer a concrete implementation unless someone else wants to do it and if I knew you're interested. Uli
Ulrich Meis wrote: > 3. a new abstract class > > abstract PGSSLHandler extends SSLSocketFactory { > > public void setPGURL(String url_no_parameters); > public void setPGargs(String args); > > } You don't need this class. Just require that the named factory class implements SSLSocketFactory and has a ctor that takes two Strings. Otherwise it looks fine. I wouldn't worry about the DataSource piece for the moment.. if you have the ability to pass args to the constructed class via a URL or properties file it is less of an issue. -O
On Thursday 14 October 2004 02:43, Oliver Jowett wrote: >... > Otherwise it looks fine. I wouldn't worry about the DataSource piece for > the moment.. if you have the ability to pass args to the constructed > class via a URL or properties file it is less of an issue. How does that look like? Since I found the same code in v2 and v3, I copied the new method as well. Uli
Attachment
Ulrich Meis wrote: > On Thursday 14 October 2004 02:43, Oliver Jowett wrote: > >>... >>Otherwise it looks fine. I wouldn't worry about the DataSource piece for >>the moment.. if you have the ability to pass args to the constructed >>class via a URL or properties file it is less of an issue. > > > How does that look like? > Since I found the same code in v2 and v3, I copied the new method as well. getSSLSocketFactory() doesn't need to be duplicated. Just pass the connection properties to makeSSL and construct the factory from there. Use of SSL-specific code needs to be conditionally compiled or the driver will not build if JSSE is not present. This is easy to do if you move getSSLSocketFactory() into Driver.java.in. This doesn't look right, what is '&urlServer&'? > + Object[] args = { info.getProperty("&urlServer&"), info.getProperty("sslfactoryargs") }; Otherwise, seems ok. -O
On Friday 15 October 2004 05:35, Oliver Jowett wrote: >... > getSSLSocketFactory() doesn't need to be duplicated. Just pass the > connection properties to makeSSL and construct the factory from there. done > Use of SSL-specific code needs to be conditionally compiled or the > driver will not build if JSSE is not present. This is easy to do if you > move getSSLSocketFactory() into Driver.java.in. I see...it's all in makeSSL now and prefixed by @SSL@ > This doesn't look right, what is '&urlServer&'? > > > + Object[] args = { info.getProperty("&urlServer&"), > > info.getProperty("sslfactoryargs") }; In parseURL I added a line to set that property to the connection url string without parameters. It's the easiest way I am aware of to get the exact url down to makeSSL. Since parseURL simply splits parameters upon & and doesn't convert escaped &s, I used the &s to make absolutely sure that this property never clashes with a real url parameter - also that wouldn't actually matter. If nothing else, it makes it obvious that this is internal. An alternative would be to reconstruct the url via the parameters. I didn't choose that approach for two reasons: 1. A lot more lines of code 2. For implementors of a SSLSocketFactory, it would break the approach of simply comparing the received string with the original connection url provided because it might slightly differ(in case for instance). Uli
Attachment
Ulrich Meis wrote: > On Friday 15 October 2004 05:35, Oliver Jowett wrote: >>This doesn't look right, what is '&urlServer&'? >> >>>+ Object[] args = { info.getProperty("&urlServer&"), >>>info.getProperty("sslfactoryargs") }; > > In parseURL I added a line to set that property to the connection url string > without parameters. It's the easiest way I am aware of to get the exact url > down to makeSSL. Since parseURL simply splits parameters upon & and doesn't > convert escaped &s, I used the &s to make absolutely sure that this property > never clashes with a real url parameter - also that wouldn't actually matter. > If nothing else, it makes it obvious that this is internal. Ouch, that's a bit nasty. Why exactly would a SSLSocketFactory implementation need to know the original URL? -O
Hi, Oliver, On Fri, 15 Oct 2004 18:28:02 +1300 Oliver Jowett <oliver@opencloud.com> wrote: > Why exactly would a SSLSocketFactory implementation need to know the > original URL? At least, it should know the original hostname for comparison to the one in the certificate, to avoid host spoofing. Markus -- markus schaber | dipl. informatiker logi-track ag | rennweg 14-16 | ch 8001 zürich phone +41-43-888 62 52 | fax +41-43-888 62 53 mailto:schabios@logi-track.com | www.logi-track.com
On Friday 15 October 2004 07:28, Oliver Jowett wrote: > Ulrich Meis wrote: > > On Friday 15 October 2004 05:35, Oliver Jowett wrote: > >>This doesn't look right, what is '&urlServer&'? > >> > >>>+ Object[] args = { info.getProperty("&urlServer&"), > >>>info.getProperty("sslfactoryargs") }; > > > > In parseURL I added a line to set that property to the connection url > > string without parameters. It's the easiest way I am aware of to get the > > exact url down to makeSSL. Since parseURL simply splits parameters upon & > > and doesn't convert escaped &s, I used the &s to make absolutely sure > > that this property never clashes with a real url parameter - also that > > wouldn't actually matter. If nothing else, it makes it obvious that this > > is internal. > > Ouch, that's a bit nasty. > > Why exactly would a SSLSocketFactory implementation need to know the > original URL? That makes it easy to associate a particular getConnection call with a presented certificate. But to make the code less nasty, hostname and port might actually do the trick because that suffices to identify the server that presented the certificate (since a server only has one cert). The attached changes provide as first parameter "hostname:port" to the factory as given in the Properties object, so I removed the &urlServer& thing. Uli
Attachment
Ulrich Meis wrote: > The attached changes provide as first parameter "hostname:port" to the factory > as given in the Properties object, so I removed the &urlServer& thing. The host and port are already given to the SSLSocketFactory when the socket is created: > @SSL@ java.net.Socket newConnection = factory.createSocket(p_stream.getSocket(), p_stream.getHost(), p_stream.getPort(),true); Why do we need to also pass them separately to the factory's ctor? -O
Markus Schaber wrote: > Hi, Oliver, > > On Fri, 15 Oct 2004 18:28:02 +1300 > Oliver Jowett <oliver@opencloud.com> wrote: > > >>Why exactly would a SSLSocketFactory implementation need to know the >>original URL? > > > At least, it should know the original hostname for comparison to the one in the > certificate, to avoid host spoofing. We already preserve the hostname & port and give them to SSLSocketFactory.createSocket(). -O
On Friday 15 October 2004 21:44, Oliver Jowett wrote: > Ulrich Meis wrote: > > The attached changes provide as first parameter "hostname:port" to the > > factory as given in the Properties object, so I removed the &urlServer& > > thing. > > The host and port are already given to the SSLSocketFactory when the > > socket is created: > > @SSL@ java.net.Socket newConnection = > > factory.createSocket(p_stream.getSocket(), p_stream.getHost(), > > p_stream.getPort(), true); Right! I forgot. > > Why do we need to also pass them separately to the factory's ctor? We don't ;-) I removed that. See next try attached ;-) Uli
Attachment
On Sat, 16 Oct 2004, Ulrich Meis wrote: > [here's a patch to customize ssl.] > I've applied a modified version of this patch. I moved the Driver.makeSSL implementation into it's own class to not use @SSL@ everywhere. I renamed the sslfactoryargs to sslfactorarg because it really is one argument. If the user chooses to encode multiple arguments into it, that's really his own business. I put a NonValidatingFactory class in to demonstrate how this can work and provide the most requested functionality. Questions: In the non-validating factory I have a SSLContext.getInstance("TLS"), but I've also seen it use "SSL". Is either preferred or does it matter for pg? I've been building the jdbc2ee jar files against the actual j2sdkee1.2.1, not just the jdbc optional package. This includes support for javax.net.ssl.SSLSocketFactory, but not javax.net.ssl.SSLContext and associated classes which are in com.sun.net.ssl instead. So at the moment I've modified the ssl requirements to not build ssl into this particular jar. What do we want to do about this: - nothing, the ee really meant datasource and was never meant to include ssl - make the existing ssl support work by not building the NonValidingFactory class - make everything work by building the NonValidatingFactory by importing from com.sun.net.ssl instead. Kris Jurka
Kris Jurka wrote: > In the non-validating factory I have a SSLContext.getInstance("TLS"), but > I've also seen it use "SSL". Is either preferred or does it matter for > pg? http://java.sun.com/j2se/1.4.2/docs/guide/security/jsse/JSSERefGuide.html#AppA has a list of 'standard' protocol names. "TLS" supports the largest set of protocols, I believe. > I've been building the jdbc2ee jar files against the actual j2sdkee1.2.1, > not just the jdbc optional package. This includes support for > javax.net.ssl.SSLSocketFactory, but not javax.net.ssl.SSLContext and > associated classes which are in com.sun.net.ssl instead. So at the moment > I've modified the ssl requirements to not build ssl into this > particular jar. What do we want to do about this: > > - nothing, the ee really meant datasource and was never meant to > include ssl > - make the existing ssl support work by not building the > NonValidingFactory class Either of these look OK to me. > - make everything work by building the NonValidatingFactory by importing > from com.sun.net.ssl instead. I think this is a bad idea, it'd break the build on non-Sun JDKs. -O
On Sunday 17 October 2004 14:07, Kris Jurka wrote: > On Sat, 16 Oct 2004, Ulrich Meis wrote: > > [here's a patch to customize ssl.] > > I've applied a modified version of this patch. I moved the Driver.makeSSL > implementation into it's own class to not use @SSL@ everywhere. I renamed > the sslfactoryargs to sslfactorarg because it really is one argument. If > the user chooses to encode multiple arguments into it, that's really his > own business. I put a NonValidatingFactory class in to demonstrate > how this can work and provide the most requested functionality. Thanks a lot! > Questions: > > In the non-validating factory I have a SSLContext.getInstance("TLS"), but > I've also seen it use "SSL". Is either preferred or does it matter for > pg? The JSSE docs say : --- Like other JCA provider-based "engine" classes, SSLContext objects are created using the getInstance factory methods of the SSLContext class. These static methods each return an instance that implements at least the requested secure socket protocol. --- My guess is that the minimum requirement for pg is ssl (tls being its successor). Hence, I would use getInstance("SSL") but I suppose most java versions will support both anyway. Uli