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
25 import javax.servlet.http.HttpServletResponse;
26
27 import org.apache.catalina.connector.Request;
28 import org.apache.catalina.connector.Response;
29 import org.apache.catalina.deploy.LoginConfig;
30 import org.apache.catalina.util.Base64;
31 import org.apache.juli.logging.Log;
32 import org.apache.juli.logging.LogFactory;
33 import org.apache.tomcat.util.buf.ByteChunk;
34 import org.apache.tomcat.util.buf.CharChunk;
35 import org.apache.tomcat.util.buf.MessageBytes;
36
37
38
39 /**
40 * An <b>Authenticator</b> and <b>Valve</b> implementation of HTTP BASIC
41 * Authentication, as outlined in RFC 2617: "HTTP Authentication: Basic
42 * and Digest Access Authentication."
43 *
44 * @author Craig R. McClanahan
45 * @version $Revision: 467222 $ $Date: 2006-10-24 05:17:11 +0200 (mar., 24 oct. 2006) $
46 */
47
48 public class BasicAuthenticator
49 extends AuthenticatorBase {
50 private static Log log = LogFactory.getLog(BasicAuthenticator.class);
51
52
53
54 /**
55 * Authenticate bytes.
56 */
57 public static final byte[] AUTHENTICATE_BYTES = {
58 (byte) 'W',
59 (byte) 'W',
60 (byte) 'W',
61 (byte) '-',
62 (byte) 'A',
63 (byte) 'u',
64 (byte) 't',
65 (byte) 'h',
66 (byte) 'e',
67 (byte) 'n',
68 (byte) 't',
69 (byte) 'i',
70 (byte) 'c',
71 (byte) 'a',
72 (byte) 't',
73 (byte) 'e'
74 };
75
76
77 // ----------------------------------------------------- Instance Variables
78
79
80 /**
81 * Descriptive information about this implementation.
82 */
83 protected static final String info =
84 "org.apache.catalina.authenticator.BasicAuthenticator/1.0";
85
86
87 // ------------------------------------------------------------- Properties
88
89
90 /**
91 * Return descriptive information about this Valve implementation.
92 */
93 public String getInfo() {
94
95 return (info);
96
97 }
98
99
100 // --------------------------------------------------------- Public Methods
101
102
103 /**
104 * Authenticate the user making this request, based on the specified
105 * login configuration. Return <code>true</code> if any specified
106 * constraint has been satisfied, or <code>false</code> if we have
107 * created a response challenge already.
108 *
109 * @param request Request we are processing
110 * @param response Response we are creating
111 * @param config Login configuration describing how authentication
112 * should be performed
113 *
114 * @exception IOException if an input/output error occurs
115 */
116 public boolean authenticate(Request request,
117 Response response,
118 LoginConfig config)
119 throws IOException {
120
121 // Have we already authenticated someone?
122 Principal principal = request.getUserPrincipal();
123 String ssoId = (String) request.getNote(Constants.REQ_SSOID_NOTE);
124 if (principal != null) {
125 if (log.isDebugEnabled())
126 log.debug("Already authenticated '" + principal.getName() + "'");
127 // Associate the session with any existing SSO session
128 if (ssoId != null)
129 associate(ssoId, request.getSessionInternal(true));
130 return (true);
131 }
132
133 // Is there an SSO session against which we can try to reauthenticate?
134 if (ssoId != null) {
135 if (log.isDebugEnabled())
136 log.debug("SSO Id " + ssoId + " set; attempting " +
137 "reauthentication");
138 /* Try to reauthenticate using data cached by SSO. If this fails,
139 either the original SSO logon was of DIGEST or SSL (which
140 we can't reauthenticate ourselves because there is no
141 cached username and password), or the realm denied
142 the user's reauthentication for some reason.
143 In either case we have to prompt the user for a logon */
144 if (reauthenticateFromSSO(ssoId, request))
145 return true;
146 }
147
148 // Validate any credentials already included with this request
149 String username = null;
150 String password = null;
151
152 MessageBytes authorization =
153 request.getCoyoteRequest().getMimeHeaders()
154 .getValue("authorization");
155
156 if (authorization != null) {
157 authorization.toBytes();
158 ByteChunk authorizationBC = authorization.getByteChunk();
159 if (authorizationBC.startsWithIgnoreCase("basic ", 0)) {
160 authorizationBC.setOffset(authorizationBC.getOffset() + 6);
161 // FIXME: Add trimming
162 // authorizationBC.trim();
163
164 CharChunk authorizationCC = authorization.getCharChunk();
165 Base64.decode(authorizationBC, authorizationCC);
166
167 // Get username and password
168 int colon = authorizationCC.indexOf(':');
169 if (colon < 0) {
170 username = authorizationCC.toString();
171 } else {
172 char[] buf = authorizationCC.getBuffer();
173 username = new String(buf, 0, colon);
174 password = new String(buf, colon + 1,
175 authorizationCC.getEnd() - colon - 1);
176 }
177
178 authorizationBC.setOffset(authorizationBC.getOffset() - 6);
179 }
180
181 principal = context.getRealm().authenticate(username, password);
182 if (principal != null) {
183 register(request, response, principal, Constants.BASIC_METHOD,
184 username, password);
185 return (true);
186 }
187 }
188
189
190 // Send an "unauthorized" response and an appropriate challenge
191 MessageBytes authenticate =
192 response.getCoyoteResponse().getMimeHeaders()
193 .addValue(AUTHENTICATE_BYTES, 0, AUTHENTICATE_BYTES.length);
194 CharChunk authenticateCC = authenticate.getCharChunk();
195 authenticateCC.append("Basic realm=\"");
196 if (config.getRealmName() == null) {
197 authenticateCC.append(request.getServerName());
198 authenticateCC.append(':');
199 authenticateCC.append(Integer.toString(request.getServerPort()));
200 } else {
201 authenticateCC.append(config.getRealmName());
202 }
203 authenticateCC.append('\"');
204 authenticate.toChars();
205 response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
206 //response.flushBuffer();
207 return (false);
208
209 }
210
211
212 }