1
2
3
4
5
6
7
8
9
10
11
12
13
14 package waffle.apache;
15
16 import java.io.IOException;
17 import java.security.Principal;
18
19 import javax.servlet.RequestDispatcher;
20 import javax.servlet.ServletContext;
21 import javax.servlet.ServletException;
22 import javax.servlet.http.HttpServletResponse;
23 import javax.servlet.http.HttpSession;
24
25 import org.apache.catalina.LifecycleException;
26 import org.apache.catalina.connector.Request;
27 import org.apache.tomcat.util.descriptor.web.LoginConfig;
28 import org.slf4j.LoggerFactory;
29
30 import com.google.common.io.BaseEncoding;
31
32 import waffle.util.AuthorizationHeader;
33 import waffle.util.NtlmServletRequest;
34 import waffle.windows.auth.IWindowsIdentity;
35 import waffle.windows.auth.IWindowsSecurityContext;
36
37
38
39
40
41
42 public class MixedAuthenticator extends WaffleAuthenticatorBase {
43
44
45
46
47 public MixedAuthenticator() {
48 super();
49 this.log = LoggerFactory.getLogger(MixedAuthenticator.class);
50 this.info = "waffle.apache.MixedAuthenticator/1.0";
51 this.log.debug("[waffle.apache.MixedAuthenticator] loaded");
52 }
53
54
55
56
57 @Override
58 public synchronized void startInternal() throws LifecycleException {
59 this.log.info("[waffle.apache.MixedAuthenticator] started");
60 super.startInternal();
61 }
62
63
64
65
66 @Override
67 public synchronized void stopInternal() throws LifecycleException {
68 super.stopInternal();
69 this.log.info("[waffle.apache.MixedAuthenticator] stopped");
70 }
71
72
73
74
75 @Override
76 public boolean authenticate(final Request request, final HttpServletResponse response) {
77
78
79 if (this.context == null || this.context.getRealm() == null) {
80 this.log.warn("missing context/realm");
81 this.sendError(response, HttpServletResponse.SC_SERVICE_UNAVAILABLE);
82 return false;
83 }
84
85 this.log.debug("{} {}, contentlength: {}", request.getMethod(), request.getRequestURI(),
86 Integer.valueOf(request.getContentLength()));
87
88 final boolean negotiateCheck = request.getParameter("j_negotiate_check") != null;
89 this.log.debug("negotiateCheck: {}", Boolean.valueOf(negotiateCheck));
90 final boolean securityCheck = request.getParameter("j_security_check") != null;
91 this.log.debug("securityCheck: {}", Boolean.valueOf(securityCheck));
92
93 final Principal principal = request.getUserPrincipal();
94
95 final AuthorizationHeader authorizationHeader = new AuthorizationHeader(request);
96 final boolean ntlmPost = authorizationHeader.isNtlmType1PostAuthorizationHeader();
97 this.log.debug("authorization: {}, ntlm post: {}", authorizationHeader, Boolean.valueOf(ntlmPost));
98
99 final LoginConfig loginConfig = this.context.getLoginConfig();
100
101 if (principal != null && !ntlmPost) {
102 this.log.debug("previously authenticated user: {}", principal.getName());
103 return true;
104 } else if (negotiateCheck) {
105 if (!authorizationHeader.isNull()) {
106 return this.negotiate(request, response, authorizationHeader);
107 }
108 this.log.debug("authorization required");
109 this.sendUnauthorized(response);
110 return false;
111 } else if (securityCheck) {
112 final boolean postResult = this.post(request, response);
113 if (postResult) {
114 this.redirectTo(request, response, request.getServletPath());
115 } else {
116 this.redirectTo(request, response, loginConfig.getErrorPage());
117 }
118 return postResult;
119 } else {
120 this.redirectTo(request, response, loginConfig.getLoginPage());
121 return false;
122 }
123 }
124
125
126
127
128
129
130
131
132
133
134
135
136 private boolean negotiate(final Request request, final HttpServletResponse response,
137 final AuthorizationHeader authorizationHeader) {
138
139 final String securityPackage = authorizationHeader.getSecurityPackage();
140
141 final String connectionId = NtlmServletRequest.getConnectionId(request);
142
143 this.log.debug("security package: {}, connection id: {}", securityPackage, connectionId);
144
145 final boolean ntlmPost = authorizationHeader.isNtlmType1PostAuthorizationHeader();
146
147 if (ntlmPost) {
148
149 this.auth.resetSecurityToken(connectionId);
150 }
151
152
153 IWindowsSecurityContext securityContext;
154
155 try {
156 final byte[] tokenBuffer = authorizationHeader.getTokenBytes();
157 this.log.debug("token buffer: {} byte(s)", Integer.valueOf(tokenBuffer.length));
158 securityContext = this.auth.acceptSecurityToken(connectionId, tokenBuffer, securityPackage);
159 this.log.debug("continue required: {}", Boolean.valueOf(securityContext.isContinue()));
160
161 final byte[] continueTokenBytes = securityContext.getToken();
162 if (continueTokenBytes != null && continueTokenBytes.length > 0) {
163 final String continueToken = BaseEncoding.base64().encode(continueTokenBytes);
164 this.log.debug("continue token: {}", continueToken);
165 response.addHeader("WWW-Authenticate", securityPackage + " " + continueToken);
166 }
167
168 if (securityContext.isContinue() || ntlmPost) {
169 response.setHeader("Connection", "keep-alive");
170 response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
171 response.flushBuffer();
172 return false;
173 }
174
175 } catch (final IOException e) {
176 this.log.warn("error logging in user: {}", e.getMessage());
177 this.log.trace("{}", e);
178 this.sendUnauthorized(response);
179 return false;
180 }
181
182
183 final IWindowsIdentity windowsIdentity = securityContext.getIdentity();
184
185
186 if (!this.allowGuestLogin && windowsIdentity.isGuest()) {
187 this.log.warn("guest login disabled: {}", windowsIdentity.getFqn());
188 this.sendUnauthorized(response);
189 return false;
190 }
191
192 try {
193
194 this.log.debug("logged in user: {} ({})", windowsIdentity.getFqn(), windowsIdentity.getSidString());
195
196 final GenericWindowsPrincipal windowsPrincipal = new GenericWindowsPrincipal(windowsIdentity,
197 this.principalFormat, this.roleFormat);
198
199 this.log.debug("roles: {}", windowsPrincipal.getRolesString());
200
201
202 final HttpSession session = request.getSession(true);
203 this.log.debug("session id: {}", session == null ? "null" : session.getId());
204
205 this.register(request, response, windowsPrincipal, securityPackage, windowsPrincipal.getName(), null);
206 this.log.info("successfully logged in user: {}", windowsPrincipal.getName());
207
208 } finally {
209 windowsIdentity.dispose();
210 }
211
212 return true;
213 }
214
215
216
217
218
219
220
221
222
223
224 private boolean post(final Request request, final HttpServletResponse response) {
225
226 final String username = request.getParameter("j_username");
227 final String password = request.getParameter("j_password");
228
229 this.log.debug("logging in: {}", username);
230
231 IWindowsIdentity windowsIdentity;
232 try {
233 windowsIdentity = this.auth.logonUser(username, password);
234 } catch (final Exception e) {
235 this.log.error(e.getMessage());
236 this.log.trace("{}", e);
237 return false;
238 }
239
240
241 if (!this.allowGuestLogin && windowsIdentity.isGuest()) {
242 this.log.warn("guest login disabled: {}", windowsIdentity.getFqn());
243 return false;
244 }
245
246 try {
247 this.log.debug("successfully logged in {} ({})", username, windowsIdentity.getSidString());
248
249 final GenericWindowsPrincipal windowsPrincipal = new GenericWindowsPrincipal(windowsIdentity,
250 this.principalFormat, this.roleFormat);
251
252 this.log.debug("roles: {}", windowsPrincipal.getRolesString());
253
254
255 final HttpSession session = request.getSession(true);
256 this.log.debug("session id: {}", session == null ? "null" : session.getId());
257
258 this.register(request, response, windowsPrincipal, "FORM", windowsPrincipal.getName(), null);
259 this.log.info("successfully logged in user: {}", windowsPrincipal.getName());
260 } finally {
261 windowsIdentity.dispose();
262 }
263
264 return true;
265 }
266
267
268
269
270
271
272
273
274
275
276
277 private void redirectTo(final Request request, final HttpServletResponse response, final String url) {
278 try {
279 this.log.debug("redirecting to: {}", url);
280 final ServletContext servletContext = this.context.getServletContext();
281 final RequestDispatcher disp = servletContext.getRequestDispatcher(url);
282 disp.forward(request.getRequest(), response);
283 } catch (final IOException e) {
284 this.log.error(e.getMessage());
285 this.log.trace("{}", e);
286 throw new RuntimeException(e);
287 } catch (final ServletException e) {
288 this.log.error(e.getMessage());
289 this.log.trace("{}", e);
290 throw new RuntimeException(e);
291 }
292 }
293 }