1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 package waffle.apache;
25
26 import com.sun.jna.platform.win32.Win32Exception;
27
28 import java.io.IOException;
29 import java.security.Principal;
30 import java.util.Base64;
31
32 import javax.servlet.http.HttpServletResponse;
33 import javax.servlet.http.HttpSession;
34
35 import org.apache.catalina.LifecycleException;
36 import org.apache.catalina.connector.Request;
37 import org.apache.catalina.realm.GenericPrincipal;
38 import org.slf4j.LoggerFactory;
39
40 import waffle.util.AuthorizationHeader;
41 import waffle.util.NtlmServletRequest;
42 import waffle.windows.auth.IWindowsIdentity;
43 import waffle.windows.auth.IWindowsSecurityContext;
44
45
46
47
48 public class NegotiateAuthenticator extends WaffleAuthenticatorBase {
49
50
51
52
53 public NegotiateAuthenticator() {
54 super();
55 this.log = LoggerFactory.getLogger(NegotiateAuthenticator.class);
56 this.info = "waffle.apache.NegotiateAuthenticator/1.0";
57 this.log.debug("[waffle.apache.NegotiateAuthenticator] loaded");
58 }
59
60 @Override
61 public synchronized void startInternal() throws LifecycleException {
62 this.log.info("[waffle.apache.NegotiateAuthenticator] started");
63 super.startInternal();
64 }
65
66 @Override
67 public synchronized void stopInternal() throws LifecycleException {
68 super.stopInternal();
69 this.log.info("[waffle.apache.NegotiateAuthenticator] stopped");
70 }
71
72 @Override
73 public boolean authenticate(final Request request, final HttpServletResponse response) {
74
75 Principal principal = request.getUserPrincipal();
76 final AuthorizationHeader authorizationHeader = new AuthorizationHeader(request);
77 final boolean ntlmPost = authorizationHeader.isNtlmType1PostAuthorizationHeader();
78
79 this.log.debug("{} {}, contentlength: {}", request.getMethod(), request.getRequestURI(),
80 Integer.valueOf(request.getContentLength()));
81 this.log.debug("authorization: {}, ntlm post: {}", authorizationHeader, Boolean.valueOf(ntlmPost));
82
83 if (principal != null && !ntlmPost) {
84
85 this.log.debug("previously authenticated user: {}", principal.getName());
86 return true;
87 }
88
89
90 if (!authorizationHeader.isNull()) {
91
92 final String securityPackage = authorizationHeader.getSecurityPackage();
93
94 final String connectionId = NtlmServletRequest.getConnectionId(request);
95
96 this.log.debug("security package: {}, connection id: {}", securityPackage, connectionId);
97
98 if (ntlmPost) {
99
100 this.auth.resetSecurityToken(connectionId);
101 }
102
103 final byte[] tokenBuffer = authorizationHeader.getTokenBytes();
104 this.log.debug("token buffer: {} byte(s)", Integer.valueOf(tokenBuffer.length));
105
106
107 IWindowsSecurityContext securityContext;
108 try {
109 securityContext = this.auth.acceptSecurityToken(connectionId, tokenBuffer, securityPackage);
110 } catch (final Win32Exception e) {
111 this.log.warn("error logging in user: {}", e.getMessage());
112 this.log.trace("", e);
113 this.sendUnauthorized(response);
114 return false;
115 }
116 this.log.debug("continue required: {}", Boolean.valueOf(securityContext.isContinue()));
117
118 final byte[] continueTokenBytes = securityContext.getToken();
119 if (continueTokenBytes != null && continueTokenBytes.length > 0) {
120 final String continueToken = Base64.getEncoder().encodeToString(continueTokenBytes);
121 this.log.debug("continue token: {}", continueToken);
122 response.addHeader("WWW-Authenticate", securityPackage + " " + continueToken);
123 }
124
125 try {
126 if (securityContext.isContinue()) {
127 response.setHeader("Connection", "keep-alive");
128 response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
129 response.flushBuffer();
130 return false;
131 }
132 } catch (final IOException e) {
133 this.log.warn("error logging in user: {}", e.getMessage());
134 this.log.trace("", e);
135 this.sendUnauthorized(response);
136 return false;
137 }
138
139
140 if (this.context == null || this.context.getRealm() == null) {
141 this.log.warn("missing context/realm");
142 this.sendError(response, HttpServletResponse.SC_SERVICE_UNAVAILABLE);
143 return false;
144 }
145
146
147 final IWindowsIdentity windowsIdentity = securityContext.getIdentity();
148
149
150 if (!this.allowGuestLogin && windowsIdentity.isGuest()) {
151 this.log.warn("guest login disabled: {}", windowsIdentity.getFqn());
152 this.sendUnauthorized(response);
153 return false;
154 }
155
156 try {
157 this.log.debug("logged in user: {} ({})", windowsIdentity.getFqn(), windowsIdentity.getSidString());
158
159 final GenericPrincipal genericPrincipal = this.createPrincipal(windowsIdentity);
160
161 if (this.log.isDebugEnabled()) {
162 this.log.debug("roles: {}", String.join(", ", genericPrincipal.getRoles()));
163 }
164
165 principal = genericPrincipal;
166
167
168 final HttpSession session = request.getSession(true);
169 this.log.debug("session id: {}", session == null ? "null" : session.getId());
170
171
172 this.register(request, response, principal, securityPackage, principal.getName(), null);
173 this.log.info("successfully logged in user: {}", principal.getName());
174
175 } finally {
176 windowsIdentity.dispose();
177 securityContext.dispose();
178 }
179
180 return true;
181 }
182
183 this.log.debug("authorization required");
184 this.sendUnauthorized(response);
185 return false;
186 }
187
188
189
190
191
192 @Override
193 protected boolean doAuthenticate(final Request request, final HttpServletResponse response) throws IOException {
194 return this.authenticate(request, response);
195 }
196
197 }