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.catalina.deploy.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, final LoginConfig loginConfig) {
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 if (principal != null && !ntlmPost) {
100 this.log.debug("previously authenticated user: {}", principal.getName());
101 return true;
102 } else if (negotiateCheck) {
103 if (!authorizationHeader.isNull()) {
104 return this.negotiate(request, response, authorizationHeader);
105 }
106 this.log.debug("authorization required");
107 this.sendUnauthorized(response);
108 return false;
109 } else if (securityCheck) {
110 final boolean postResult = this.post(request, response);
111 if (postResult) {
112 this.redirectTo(request, response, request.getServletPath());
113 } else {
114 this.redirectTo(request, response, loginConfig.getErrorPage());
115 }
116 return postResult;
117 } else {
118 this.redirectTo(request, response, loginConfig.getLoginPage());
119 return false;
120 }
121 }
122
123
124
125
126
127
128
129
130
131
132
133
134 private boolean negotiate(final Request request, final HttpServletResponse response,
135 final AuthorizationHeader authorizationHeader) {
136
137 final String securityPackage = authorizationHeader.getSecurityPackage();
138
139 final String connectionId = NtlmServletRequest.getConnectionId(request);
140
141 this.log.debug("security package: {}, connection id: {}", securityPackage, connectionId);
142
143 final boolean ntlmPost = authorizationHeader.isNtlmType1PostAuthorizationHeader();
144
145 if (ntlmPost) {
146
147 this.auth.resetSecurityToken(connectionId);
148 }
149
150
151 IWindowsSecurityContext securityContext;
152
153 try {
154 final byte[] tokenBuffer = authorizationHeader.getTokenBytes();
155 this.log.debug("token buffer: {} byte(s)", Integer.valueOf(tokenBuffer.length));
156 securityContext = this.auth.acceptSecurityToken(connectionId, tokenBuffer, securityPackage);
157 this.log.debug("continue required: {}", Boolean.valueOf(securityContext.isContinue()));
158
159 final byte[] continueTokenBytes = securityContext.getToken();
160 if (continueTokenBytes != null && continueTokenBytes.length > 0) {
161 final String continueToken = BaseEncoding.base64().encode(continueTokenBytes);
162 this.log.debug("continue token: {}", continueToken);
163 response.addHeader("WWW-Authenticate", securityPackage + " " + continueToken);
164 }
165
166 if (securityContext.isContinue() || ntlmPost) {
167 response.setHeader("Connection", "keep-alive");
168 response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
169 response.flushBuffer();
170 return false;
171 }
172
173 } catch (final IOException e) {
174 this.log.warn("error logging in user: {}", e.getMessage());
175 this.log.trace("{}", e);
176 this.sendUnauthorized(response);
177 return false;
178 }
179
180
181 final IWindowsIdentity windowsIdentity = securityContext.getIdentity();
182
183
184 if (!this.allowGuestLogin && windowsIdentity.isGuest()) {
185 this.log.warn("guest login disabled: {}", windowsIdentity.getFqn());
186 this.sendUnauthorized(response);
187 return false;
188 }
189
190 try {
191
192 this.log.debug("logged in user: {} ({})", windowsIdentity.getFqn(), windowsIdentity.getSidString());
193
194 final GenericWindowsPrincipal windowsPrincipal = new GenericWindowsPrincipal(windowsIdentity,
195 this.principalFormat, this.roleFormat);
196
197 this.log.debug("roles: {}", windowsPrincipal.getRolesString());
198
199
200 final HttpSession session = request.getSession(true);
201 this.log.debug("session id: {}", session == null ? "null" : session.getId());
202
203 this.register(request, response, windowsPrincipal, securityPackage, windowsPrincipal.getName(), null);
204 this.log.info("successfully logged in user: {}", windowsPrincipal.getName());
205
206 } finally {
207 windowsIdentity.dispose();
208 }
209
210 return true;
211 }
212
213
214
215
216
217
218
219
220
221
222 private boolean post(final Request request, final HttpServletResponse response) {
223
224 final String username = request.getParameter("j_username");
225 final String password = request.getParameter("j_password");
226
227 this.log.debug("logging in: {}", username);
228
229 IWindowsIdentity windowsIdentity;
230 try {
231 windowsIdentity = this.auth.logonUser(username, password);
232 } catch (final Exception e) {
233 this.log.error(e.getMessage());
234 this.log.trace("{}", e);
235 return false;
236 }
237
238
239 if (!this.allowGuestLogin && windowsIdentity.isGuest()) {
240 this.log.warn("guest login disabled: {}", windowsIdentity.getFqn());
241 return false;
242 }
243
244 try {
245 this.log.debug("successfully logged in {} ({})", username, windowsIdentity.getSidString());
246
247 final GenericWindowsPrincipal windowsPrincipal = new GenericWindowsPrincipal(windowsIdentity,
248 this.principalFormat, this.roleFormat);
249
250 this.log.debug("roles: {}", windowsPrincipal.getRolesString());
251
252
253 final HttpSession session = request.getSession(true);
254 this.log.debug("session id: {}", session == null ? "null" : session.getId());
255
256 this.register(request, response, windowsPrincipal, "FORM", windowsPrincipal.getName(), null);
257 this.log.info("successfully logged in user: {}", windowsPrincipal.getName());
258 } finally {
259 windowsIdentity.dispose();
260 }
261
262 return true;
263 }
264
265
266
267
268
269
270
271
272
273
274
275 private void redirectTo(final Request request, final HttpServletResponse response, final String url) {
276 try {
277 this.log.debug("redirecting to: {}", url);
278 final ServletContext servletContext = this.context.getServletContext();
279 final RequestDispatcher disp = servletContext.getRequestDispatcher(url);
280 disp.forward(request.getRequest(), response);
281 } catch (final IOException e) {
282 this.log.error(e.getMessage());
283 this.log.trace("{}", e);
284 throw new RuntimeException(e);
285 } catch (final ServletException e) {
286 this.log.error(e.getMessage());
287 this.log.trace("{}", e);
288 throw new RuntimeException(e);
289 }
290 }
291 }