1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18
19 package org.apache.catalina.authenticator;
20
21
22 import java.io.IOException;
23 import java.security.Principal;
24 import java.security.cert.X509Certificate;
25
26 import javax.servlet.http.HttpServletResponse;
27
28 import org.apache.coyote.ActionCode;
29 import org.apache.catalina.Globals;
30 import org.apache.catalina.LifecycleException;
31 import org.apache.catalina.connector.Request;
32 import org.apache.catalina.connector.Response;
33 import org.apache.catalina.deploy.LoginConfig;
34
35
36
37 /**
38 * An <b>Authenticator</b> and <b>Valve</b> implementation of authentication
39 * that utilizes SSL certificates to identify client users.
40 *
41 * @author Craig R. McClanahan
42 * @version $Revision: 500629 $ $Date: 2007-01-27 22:32:52 +0100 (sam., 27 janv. 2007) $
43 */
44
45 public class SSLAuthenticator
46 extends AuthenticatorBase {
47
48
49 // ------------------------------------------------------------- Properties
50
51
52 /**
53 * Descriptive information about this implementation.
54 */
55 protected static final String info =
56 "org.apache.catalina.authenticator.SSLAuthenticator/1.0";
57
58
59 /**
60 * Return descriptive information about this Valve implementation.
61 */
62 public String getInfo() {
63
64 return (info);
65
66 }
67
68
69 // --------------------------------------------------------- Public Methods
70
71
72 /**
73 * Authenticate the user by checking for the existence of a certificate
74 * chain, and optionally asking a trust manager to validate that we trust
75 * this user.
76 *
77 * @param request Request we are processing
78 * @param response Response we are creating
79 * @param config Login configuration describing how authentication
80 * should be performed
81 *
82 * @exception IOException if an input/output error occurs
83 */
84 public boolean authenticate(Request request,
85 Response response,
86 LoginConfig config)
87 throws IOException {
88
89 // Have we already authenticated someone?
90 Principal principal = request.getUserPrincipal();
91 //String ssoId = (String) request.getNote(Constants.REQ_SSOID_NOTE);
92 if (principal != null) {
93 if (containerLog.isDebugEnabled())
94 containerLog.debug("Already authenticated '" + principal.getName() + "'");
95 // Associate the session with any existing SSO session in order
96 // to get coordinated session invalidation at logout
97 String ssoId = (String) request.getNote(Constants.REQ_SSOID_NOTE);
98 if (ssoId != null)
99 associate(ssoId, request.getSessionInternal(true));
100 return (true);
101 }
102
103 // NOTE: We don't try to reauthenticate using any existing SSO session,
104 // because that will only work if the original authentication was
105 // BASIC or FORM, which are less secure than the CLIENT_CERT auth-type
106 // specified for this webapp
107 //
108 // Uncomment below to allow previous FORM or BASIC authentications
109 // to authenticate users for this webapp
110 // TODO make this a configurable attribute (in SingleSignOn??)
111 /*
112 // Is there an SSO session against which we can try to reauthenticate?
113 if (ssoId != null) {
114 if (log.isDebugEnabled())
115 log.debug("SSO Id " + ssoId + " set; attempting " +
116 "reauthentication");
117 // Try to reauthenticate using data cached by SSO. If this fails,
118 // either the original SSO logon was of DIGEST or SSL (which
119 // we can't reauthenticate ourselves because there is no
120 // cached username and password), or the realm denied
121 // the user's reauthentication for some reason.
122 // In either case we have to prompt the user for a logon
123 if (reauthenticateFromSSO(ssoId, request))
124 return true;
125 }
126 */
127
128 // Retrieve the certificate chain for this client
129 if (containerLog.isDebugEnabled())
130 containerLog.debug(" Looking up certificates");
131
132 X509Certificate certs[] = (X509Certificate[])
133 request.getAttribute(Globals.CERTIFICATES_ATTR);
134 if ((certs == null) || (certs.length < 1)) {
135 request.getCoyoteRequest().action
136 (ActionCode.ACTION_REQ_SSL_CERTIFICATE, null);
137 certs = (X509Certificate[])
138 request.getAttribute(Globals.CERTIFICATES_ATTR);
139 }
140 if ((certs == null) || (certs.length < 1)) {
141 if (containerLog.isDebugEnabled())
142 containerLog.debug(" No certificates included with this request");
143 response.sendError(HttpServletResponse.SC_BAD_REQUEST,
144 sm.getString("authenticator.certificates"));
145 return (false);
146 }
147
148 // Authenticate the specified certificate chain
149 principal = context.getRealm().authenticate(certs);
150 if (principal == null) {
151 if (containerLog.isDebugEnabled())
152 containerLog.debug(" Realm.authenticate() returned false");
153 response.sendError(HttpServletResponse.SC_UNAUTHORIZED,
154 sm.getString("authenticator.unauthorized"));
155 return (false);
156 }
157
158 // Cache the principal (if requested) and record this authentication
159 register(request, response, principal, Constants.CERT_METHOD,
160 null, null);
161 return (true);
162
163 }
164
165
166 // ------------------------------------------------------ Lifecycle Methods
167
168
169 /**
170 * Initialize the database we will be using for client verification
171 * and certificate validation (if any).
172 *
173 * @exception LifecycleException if this component detects a fatal error
174 * that prevents this component from being used
175 */
176 public void start() throws LifecycleException {
177
178 super.start();
179
180 }
181
182
183 /**
184 * Finalize the database we used for client verification and
185 * certificate validation (if any).
186 *
187 * @exception LifecycleException if this component detects a fatal error
188 * that prevents this component from being used
189 */
190 public void stop() throws LifecycleException {
191
192 super.stop();
193
194 }
195
196
197 }