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