001: // ========================================================================
002: // Copyright 2002-2005 Mort Bay Consulting Pty. Ltd.
003: // ------------------------------------------------------------------------
004: // Licensed under the Apache License, Version 2.0 (the "License");
005: // you may not use this file except in compliance with the License.
006: // You may obtain a copy of the License at
007: // http://www.apache.org/licenses/LICENSE-2.0
008: // Unless required by applicable law or agreed to in writing, software
009: // distributed under the License is distributed on an "AS IS" BASIS,
010: // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
011: // See the License for the specific language governing permissions and
012: // limitations under the License.
013: // ========================================================================
014:
015: package org.mortbay.jetty.security;
016:
017: import java.io.IOException;
018: import java.security.Principal;
019:
020: import javax.net.ssl.SSLSocket;
021:
022: import org.mortbay.jetty.HttpConnection;
023: import org.mortbay.jetty.Request;
024: import org.mortbay.jetty.Response;
025: import org.mortbay.log.Log;
026:
027: /* ------------------------------------------------------------ */
028: /** Client Certificate Authenticator.
029: * This Authenticator uses a client certificate to authenticate the user.
030: * Each client certificate supplied is tried against the realm using the
031: * Principal name as the username and a string representation of the
032: * certificate as the credential.
033: * @author Greg Wilkins (gregw)
034: */
035: public class ClientCertAuthenticator implements Authenticator {
036: private int _maxHandShakeSeconds = 60;
037:
038: /* ------------------------------------------------------------ */
039: public ClientCertAuthenticator() {
040: Log.warn("Client Cert Authentication is EXPERIMENTAL");
041: }
042:
043: /* ------------------------------------------------------------ */
044: public int getMaxHandShakeSeconds() {
045: return _maxHandShakeSeconds;
046: }
047:
048: /* ------------------------------------------------------------ */
049: /**
050: * @param maxHandShakeSeconds Maximum time to wait for SSL handshake if
051: * Client certification is required.
052: */
053: public void setMaxHandShakeSeconds(int maxHandShakeSeconds) {
054: _maxHandShakeSeconds = maxHandShakeSeconds;
055: }
056:
057: /* ------------------------------------------------------------ */
058: /**
059: * @return UserPrinciple if authenticated or null if not. If
060: * Authentication fails, then the authenticator may have committed
061: * the response as an auth challenge or redirect.
062: * @exception IOException
063: */
064: public Principal authenticate(UserRealm realm,
065: String pathInContext, Request request, Response response)
066: throws IOException {
067: java.security.cert.X509Certificate[] certs = (java.security.cert.X509Certificate[]) request
068: .getAttribute("javax.servlet.request.X509Certificate");
069:
070: if (response != null
071: && (certs == null || certs.length == 0 || certs[0] == null)) {
072: // No certs available so lets try and force the issue
073:
074: // Get the SSLSocket
075: HttpConnection connection = HttpConnection
076: .getCurrentConnection();
077: if (!connection.getConnector().isConfidential(request))
078: return null;
079:
080: Object s = connection.getEndPoint().getTransport();
081: if (!(s instanceof SSLSocket))
082: return null;
083: SSLSocket socket = (SSLSocket) s;
084:
085: if (!socket.getNeedClientAuth()) {
086: // Need to re-handshake
087: socket.setNeedClientAuth(true);
088: socket.startHandshake();
089:
090: // Need to wait here - but not forever. The Handshake
091: // Listener API does not look like a good option to
092: // avoid waiting forever. So we will take a slightly
093: // busy timelimited approach. For now:
094: for (int i = (_maxHandShakeSeconds * 4); i-- > 0;) {
095: certs = (java.security.cert.X509Certificate[]) request
096: .getAttribute("javax.servlet.request.X509Certificate");
097: if (certs != null && certs.length > 0
098: && certs[0] != null)
099: break;
100: try {
101: Thread.sleep(250);
102: } catch (Exception e) {
103: break;
104: }
105: }
106: }
107: }
108:
109: if (certs == null || certs.length == 0 || certs[0] == null)
110: return null;
111:
112: Principal principal = certs[0].getSubjectDN();
113: if (principal == null)
114: principal = certs[0].getIssuerDN();
115: String username = principal == null ? "clientcert" : principal
116: .getName();
117:
118: Principal user = realm.authenticate(username, certs, request);
119:
120: request.setAuthType(Constraint.__CERT_AUTH);
121: request.setUserPrincipal(user);
122: return user;
123: }
124:
125: /* ------------------------------------------------------------ */
126: public String getAuthMethod() {
127: return Constraint.__CERT_AUTH;
128: }
129:
130: }
|