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