1 /* JDBCSessionContext.java -- database persistent sessions.
2 Copyright (C) 2006 Free Software Foundation, Inc.
4 This file is a part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or (at
9 your option) any later version.
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
39 package gnu
.javax
.net
.ssl
.provider
;
41 import java
.io
.ByteArrayOutputStream
;
42 import java
.io
.InputStream
;
44 import java
.security
.SecureRandom
;
45 import java
.security
.cert
.Certificate
;
46 import java
.security
.cert
.CertificateFactory
;
48 import java
.sql
.Connection
;
49 import java
.sql
.DriverManager
;
50 import java
.sql
.PreparedStatement
;
51 import java
.sql
.ResultSet
;
52 import java
.sql
.SQLException
;
53 import java
.sql
.Statement
;
54 import java
.sql
.Timestamp
;
55 import java
.sql
.Types
;
57 import java
.util
.ArrayList
;
58 import java
.util
.Iterator
;
59 import java
.util
.Enumeration
;
60 import java
.util
.TreeSet
;
61 import java
.util
.Vector
;
63 import javax
.net
.ssl
.SSLSession
;
66 * The SQL table this class stores sessions in, called <tt>SESSIONS</tt>,
71 * ID VARBINARY(32) PRIMARY KEY UNIQUE NOT NULL,
72 * CREATED TIMESTAMP NOT NULL,
73 * LAST_ACCESSED TIMESTAMP NOT NULL,
74 * PROTOCOL VARCHAR(7) NOT NULL,
75 * SUITE VARCHAR(255) NOT NULL,
76 * PEER_HOST TEXT NOT NULL,
77 * PEER_CERT_TYPE VARCHAR(32),
79 * CERT_TYPE VARCHAR(32),
81 * SECRET VARBINARY(48) NOT NULL
85 * <p>Note that the master secret for sessions is not protected before
86 * being inserted into the database; it is up to the system to protect
87 * the stored data from unauthorized access.
89 class JDBCSessionContext
extends SessionContext
93 // -------------------------------------------------------------------------
95 protected Connection connection
;
96 protected PreparedStatement selectById
;
97 protected PreparedStatement insert
;
98 protected PreparedStatement selectTimestamp
;
99 protected PreparedStatement updateTimestamp
;
100 protected PreparedStatement deleteSession
;
103 // -------------------------------------------------------------------------
105 JDBCSessionContext() throws SQLException
107 String url
= Util
.getSecurityProperty("jessie.SessionContext.jdbc.url");
108 String user
= Util
.getSecurityProperty("jessie.SessionContext.jdbc.user");
109 String passwd
= Util
.getSecurityProperty("jessie.SessionContext.jdbc.password");
112 throw new IllegalArgumentException("no JDBC URL");
114 if (user
== null || passwd
== null)
116 connection
= DriverManager
.getConnection(url
);
120 connection
= DriverManager
.getConnection(url
, user
, passwd
);
123 connection
.prepareStatement("SELECT * FROM SESSIONS WHERE ID = ?");
124 insert
= connection
.prepareStatement("INSERT INTO SESSIONS VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
126 connection
.prepareStatement("SELECT CREATED FROM SESSIONS WHERE ID = ?");
128 connection
.prepareStatement("UPDATE SESSIONS SET LAST_ACCESSED = ? WHERE ID = ?");
130 connection
.prepareStatement("DELETE FROM SESSIONS WHERE ID = ?");
134 // -------------------------------------------------------------------------
136 public synchronized Enumeration
getIds()
138 Vector ids
= new Vector();
141 Statement stmt
= connection
.createStatement();
142 ResultSet rs
= stmt
.executeQuery("SELECT ID FROM SESSIONS");
145 byte[] id
= rs
.getBytes("ID");
149 catch (SQLException sqle
)
152 return ids
.elements();
155 public synchronized SSLSession
getSession(byte[] sessionId
)
157 Session session
= (Session
) super.getSession(sessionId
);
162 selectById
.setBytes(1, sessionId
);
163 ResultSet rs
= selectById
.executeQuery();
166 session
= new Session(rs
.getTimestamp("CREATED").getTime());
167 session
.enabledSuites
= new ArrayList(SSLSocket
.supportedSuites
);
168 session
.enabledProtocols
= new TreeSet(SSLSocket
.supportedProtocols
);
169 session
.random
= new SecureRandom();
170 session
.context
= this;
171 session
.sessionId
= new Session
.ID(rs
.getBytes("ID"));
172 session
.setLastAccessedTime(rs
.getTimestamp("LAST_ACCESSED").getTime());
173 long elapsed
= System
.currentTimeMillis() - session
.getLastAccessedTime();
174 if ((int) (elapsed
/ 1000L) > timeout
)
176 removeSession(session
.sessionId
);
179 session
.peerHost
= rs
.getString("PEER_HOST");
180 String protocol
= rs
.getString("PROTOCOL");
181 if (protocol
.equals("SSLv3"))
183 session
.protocol
= ProtocolVersion
.SSL_3
;
185 else if (protocol
.equals("TLSv1"))
187 session
.protocol
= ProtocolVersion
.TLS_1
;
189 else if (protocol
.equals("TLSv1.1"))
191 session
.protocol
= ProtocolVersion
.TLS_1_1
;
197 session
.cipherSuite
= CipherSuite
.forName(rs
.getString("SUITE"));
198 String type
= rs
.getString("PEER_CERT_TYPE");
199 boolean wasNull
= rs
.wasNull();
200 InputStream certs
= null;
203 certs
= rs
.getBinaryStream("PEER_CERTS");
204 wasNull
= rs
.wasNull();
208 CertificateFactory cf
= CertificateFactory
.getInstance(type
);
209 session
.peerCerts
= (Certificate
[])
210 cf
.generateCertificates(certs
).toArray(new Certificate
[0]);
211 session
.peerVerified
= true;
213 type
= rs
.getString("CERT_TYPE");
214 wasNull
= rs
.wasNull();
217 certs
= rs
.getBinaryStream("CERTS");
218 wasNull
= rs
.wasNull();
222 CertificateFactory cf
= CertificateFactory
.getInstance(type
);
223 session
.localCerts
= (Certificate
[])
224 cf
.generateCertificates(certs
).toArray(new Certificate
[0]);
226 session
.masterSecret
= rs
.getBytes("SECRET");
227 if (cacheSize
== 0 || sessions
.size() < cacheSize
)
229 sessions
.put(session
.sessionId
, session
);
240 synchronized boolean addSession(Session
.ID id
, Session s
)
242 if (containsSessionID(id
))
248 insert
.setBytes(1, id
.getId());
249 insert
.setTimestamp(2, new Timestamp(s
.getCreationTime()));
250 insert
.setTimestamp(3, new Timestamp(s
.getLastAccessedTime()));
251 insert
.setString(4, s
.getProtocol());
252 insert
.setString(5, s
.getCipherSuite());
253 insert
.setString(6, s
.peerHost
);
254 if (s
.peerCerts
!= null && s
.peerCerts
.length
> 0)
256 insert
.setString(7, s
.peerCerts
[0].getType());
257 insert
.setBytes(8, certs(s
.peerCerts
));
261 insert
.setNull(7, Types
.VARCHAR
);
262 insert
.setNull(8, Types
.LONGVARBINARY
);
264 if (s
.localCerts
!= null && s
.localCerts
.length
> 0)
266 insert
.setString(9, s
.localCerts
[0].getType());
267 insert
.setBytes(10, certs(s
.localCerts
));
271 insert
.setNull(9, Types
.VARCHAR
);
272 insert
.setNull(10, Types
.LONGVARBINARY
);
274 insert
.setBytes(11, s
.masterSecret
);
275 insert
.executeUpdate();
276 super.addSession(id
, s
);
278 catch (SQLException sqle
)
285 synchronized boolean containsSessionID(Session
.ID sessionId
)
289 selectTimestamp
.setBytes(1, sessionId
.getId());
290 ResultSet rs
= selectTimestamp
.executeQuery();
295 Timestamp ts
= rs
.getTimestamp("CREATED");
300 long elapsed
= System
.currentTimeMillis() - ts
.getTime();
301 if ((int) (elapsed
/ 1000) > timeout
)
303 removeSession(sessionId
);
308 catch (SQLException sqle
)
314 protected boolean removeSession(Session
.ID sessionId
)
316 super.removeSession(sessionId
);
319 deleteSession
.setBytes(1, sessionId
.getId());
320 return deleteSession
.executeUpdate() > 0;
322 catch (SQLException sqle
)
328 synchronized void notifyAccess(Session session
)
332 updateTimestamp
.setTimestamp(1, new Timestamp(session
.getLastAccessedTime()));
333 updateTimestamp
.setBytes(2, session
.getId());
334 updateTimestamp
.executeUpdate();
336 catch (SQLException sqle
)
341 private byte[] certs(Certificate
[] certs
)
343 ByteArrayOutputStream out
= new ByteArrayOutputStream(2048);
344 for (int i
= 0; i
< certs
.length
; i
++)
348 out
.write(certs
[i
].getEncoded());
354 return out
.toByteArray();