001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package org.apache.catalina.authenticator;
019:
020: import java.io.IOException;
021: import java.security.Principal;
022: import java.security.cert.X509Certificate;
023:
024: import javax.servlet.http.HttpServletResponse;
025:
026: import org.apache.coyote.ActionCode;
027: import org.apache.catalina.Globals;
028: import org.apache.catalina.LifecycleException;
029: import org.apache.catalina.connector.Request;
030: import org.apache.catalina.connector.Response;
031: import org.apache.catalina.deploy.LoginConfig;
032:
033: /**
034: * An <b>Authenticator</b> and <b>Valve</b> implementation of authentication
035: * that utilizes SSL certificates to identify client users.
036: *
037: * @author Craig R. McClanahan
038: * @version $Revision: 500629 $ $Date: 2007-01-27 22:32:52 +0100 (sam., 27 janv. 2007) $
039: */
040:
041: public class SSLAuthenticator extends AuthenticatorBase {
042:
043: // ------------------------------------------------------------- Properties
044:
045: /**
046: * Descriptive information about this implementation.
047: */
048: protected static final String info = "org.apache.catalina.authenticator.SSLAuthenticator/1.0";
049:
050: /**
051: * Return descriptive information about this Valve implementation.
052: */
053: public String getInfo() {
054:
055: return (info);
056:
057: }
058:
059: // --------------------------------------------------------- Public Methods
060:
061: /**
062: * Authenticate the user by checking for the existence of a certificate
063: * chain, and optionally asking a trust manager to validate that we trust
064: * this user.
065: *
066: * @param request Request we are processing
067: * @param response Response we are creating
068: * @param config Login configuration describing how authentication
069: * should be performed
070: *
071: * @exception IOException if an input/output error occurs
072: */
073: public boolean authenticate(Request request, Response response,
074: LoginConfig config) throws IOException {
075:
076: // Have we already authenticated someone?
077: Principal principal = request.getUserPrincipal();
078: //String ssoId = (String) request.getNote(Constants.REQ_SSOID_NOTE);
079: if (principal != null) {
080: if (containerLog.isDebugEnabled())
081: containerLog.debug("Already authenticated '"
082: + principal.getName() + "'");
083: // Associate the session with any existing SSO session in order
084: // to get coordinated session invalidation at logout
085: String ssoId = (String) request
086: .getNote(Constants.REQ_SSOID_NOTE);
087: if (ssoId != null)
088: associate(ssoId, request.getSessionInternal(true));
089: return (true);
090: }
091:
092: // NOTE: We don't try to reauthenticate using any existing SSO session,
093: // because that will only work if the original authentication was
094: // BASIC or FORM, which are less secure than the CLIENT_CERT auth-type
095: // specified for this webapp
096: //
097: // Uncomment below to allow previous FORM or BASIC authentications
098: // to authenticate users for this webapp
099: // TODO make this a configurable attribute (in SingleSignOn??)
100: /*
101: // Is there an SSO session against which we can try to reauthenticate?
102: if (ssoId != null) {
103: if (log.isDebugEnabled())
104: log.debug("SSO Id " + ssoId + " set; attempting " +
105: "reauthentication");
106: // Try to reauthenticate using data cached by SSO. If this fails,
107: // either the original SSO logon was of DIGEST or SSL (which
108: // we can't reauthenticate ourselves because there is no
109: // cached username and password), or the realm denied
110: // the user's reauthentication for some reason.
111: // In either case we have to prompt the user for a logon
112: if (reauthenticateFromSSO(ssoId, request))
113: return true;
114: }
115: */
116:
117: // Retrieve the certificate chain for this client
118: if (containerLog.isDebugEnabled())
119: containerLog.debug(" Looking up certificates");
120:
121: X509Certificate certs[] = (X509Certificate[]) request
122: .getAttribute(Globals.CERTIFICATES_ATTR);
123: if ((certs == null) || (certs.length < 1)) {
124: request.getCoyoteRequest().action(
125: ActionCode.ACTION_REQ_SSL_CERTIFICATE, null);
126: certs = (X509Certificate[]) request
127: .getAttribute(Globals.CERTIFICATES_ATTR);
128: }
129: if ((certs == null) || (certs.length < 1)) {
130: if (containerLog.isDebugEnabled())
131: containerLog
132: .debug(" No certificates included with this request");
133: response.sendError(HttpServletResponse.SC_BAD_REQUEST, sm
134: .getString("authenticator.certificates"));
135: return (false);
136: }
137:
138: // Authenticate the specified certificate chain
139: principal = context.getRealm().authenticate(certs);
140: if (principal == null) {
141: if (containerLog.isDebugEnabled())
142: containerLog
143: .debug(" Realm.authenticate() returned false");
144: response.sendError(HttpServletResponse.SC_UNAUTHORIZED, sm
145: .getString("authenticator.unauthorized"));
146: return (false);
147: }
148:
149: // Cache the principal (if requested) and record this authentication
150: register(request, response, principal, Constants.CERT_METHOD,
151: null, null);
152: return (true);
153:
154: }
155:
156: // ------------------------------------------------------ Lifecycle Methods
157:
158: /**
159: * Initialize the database we will be using for client verification
160: * and certificate validation (if any).
161: *
162: * @exception LifecycleException if this component detects a fatal error
163: * that prevents this component from being used
164: */
165: public void start() throws LifecycleException {
166:
167: super .start();
168:
169: }
170:
171: /**
172: * Finalize the database we used for client verification and
173: * certificate validation (if any).
174: *
175: * @exception LifecycleException if this component detects a fatal error
176: * that prevents this component from being used
177: */
178: public void stop() throws LifecycleException {
179:
180: super.stop();
181:
182: }
183:
184: }
|