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.windows.auth.impl;
25
26 import com.sun.jna.platform.win32.Advapi32;
27 import com.sun.jna.platform.win32.Kernel32;
28 import com.sun.jna.platform.win32.Netapi32Util;
29 import com.sun.jna.platform.win32.Netapi32Util.DomainTrust;
30 import com.sun.jna.platform.win32.Secur32;
31 import com.sun.jna.platform.win32.Sspi;
32 import com.sun.jna.platform.win32.Sspi.CtxtHandle;
33 import com.sun.jna.platform.win32.SspiUtil.ManagedSecBufferDesc;
34 import com.sun.jna.platform.win32.Win32Exception;
35 import com.sun.jna.platform.win32.WinBase;
36 import com.sun.jna.platform.win32.WinError;
37 import com.sun.jna.platform.win32.WinNT.HANDLEByReference;
38 import com.sun.jna.ptr.IntByReference;
39
40 import java.net.InetAddress;
41 import java.net.UnknownHostException;
42 import java.util.ArrayList;
43 import java.util.List;
44
45 import waffle.util.cache.Cache;
46 import waffle.windows.auth.IWindowsAccount;
47 import waffle.windows.auth.IWindowsAuthProvider;
48 import waffle.windows.auth.IWindowsComputer;
49 import waffle.windows.auth.IWindowsCredentialsHandle;
50 import waffle.windows.auth.IWindowsDomain;
51 import waffle.windows.auth.IWindowsIdentity;
52 import waffle.windows.auth.IWindowsSecurityContext;
53
54
55
56
57 public class WindowsAuthProviderImpl implements IWindowsAuthProvider {
58
59
60 public static final int CONTINUE_CONTEXT_TIMEOUT = 30;
61
62
63
64
65 private static class ContinueContext {
66
67 CtxtHandle continueHandle;
68
69
70 IWindowsCredentialsHandle serverCredential;
71
72
73
74
75
76
77
78
79
80 public ContinueContext(final CtxtHandle handle, final IWindowsCredentialsHandle windowsCredential) {
81 this.continueHandle = handle;
82 this.serverCredential = windowsCredential;
83 }
84 }
85
86
87 private final Cache<String, ContinueContext> continueContexts;
88
89
90
91
92 public WindowsAuthProviderImpl() {
93 this(WindowsAuthProviderImpl.CONTINUE_CONTEXT_TIMEOUT);
94 }
95
96
97
98
99
100
101
102 public WindowsAuthProviderImpl(final int continueContextsTimeout) {
103 this.continueContexts = Cache.newCache(continueContextsTimeout);
104 }
105
106 @Override
107 public IWindowsSecurityContext acceptSecurityToken(final String connectionId, final byte[] token,
108 final String securityPackage) {
109
110 if (token == null || token.length == 0) {
111 this.resetSecurityToken(connectionId);
112 throw new Win32Exception(WinError.SEC_E_INVALID_TOKEN);
113 }
114
115 CtxtHandle continueHandle = null;
116 IWindowsCredentialsHandle serverCredential;
117 ContinueContext continueContext = this.continueContexts.get(connectionId);
118 if (continueContext != null) {
119 continueHandle = continueContext.continueHandle;
120 serverCredential = continueContext.serverCredential;
121 } else {
122 serverCredential = new WindowsCredentialsHandleImpl(null, Sspi.SECPKG_CRED_INBOUND, securityPackage);
123 serverCredential.initialize();
124 }
125
126 WindowsSecurityContextImpl sc;
127
128 int rc;
129 int tokenSize = Sspi.MAX_TOKEN_SIZE;
130
131 do {
132 final ManagedSecBufferDesc pbServerToken = new ManagedSecBufferDesc(Sspi.SECBUFFER_TOKEN, tokenSize);
133 final ManagedSecBufferDesc pbClientToken = new ManagedSecBufferDesc(Sspi.SECBUFFER_TOKEN, token);
134 final IntByReference pfClientContextAttr = new IntByReference();
135
136 final CtxtHandle phNewServerContext = new CtxtHandle();
137 rc = Secur32.INSTANCE.AcceptSecurityContext(serverCredential.getHandle(), continueHandle, pbClientToken,
138 Sspi.ISC_REQ_CONNECTION, Sspi.SECURITY_NATIVE_DREP, phNewServerContext, pbServerToken,
139 pfClientContextAttr, null);
140
141 sc = new WindowsSecurityContextImpl();
142 sc.setCredentialsHandle(serverCredential);
143 sc.setSecurityPackage(securityPackage);
144 sc.setSecurityContext(phNewServerContext);
145
146 switch (rc) {
147 case WinError.SEC_E_BUFFER_TOO_SMALL:
148 tokenSize += Sspi.MAX_TOKEN_SIZE;
149 sc.dispose();
150 WindowsSecurityContextImpl.dispose(continueHandle);
151 break;
152 case WinError.SEC_E_OK:
153
154 this.resetSecurityToken(connectionId);
155
156 if (pbServerToken.pBuffers != null && pbServerToken.cBuffers == 1
157 && pbServerToken.getBuffer(0).cbBuffer > 0) {
158 sc.setToken(pbServerToken.getBuffer(0).getBytes() == null ? new byte[0]
159 : pbServerToken.getBuffer(0).getBytes().clone());
160 }
161 sc.setContinue(false);
162 break;
163 case WinError.SEC_I_CONTINUE_NEEDED:
164
165 continueContext = new ContinueContext(phNewServerContext, serverCredential);
166 this.continueContexts.put(connectionId, continueContext);
167 sc.setToken(pbServerToken.getBuffer(0).getBytes() == null ? new byte[0]
168 : pbServerToken.getBuffer(0).getBytes().clone());
169 sc.setContinue(true);
170 break;
171 default:
172 sc.dispose();
173 WindowsSecurityContextImpl.dispose(continueHandle);
174 this.resetSecurityToken(connectionId);
175 throw new Win32Exception(rc);
176 }
177 } while (rc == WinError.SEC_E_BUFFER_TOO_SMALL);
178
179 return sc;
180 }
181
182 @Override
183 public IWindowsComputer getCurrentComputer() {
184 try {
185 return new WindowsComputerImpl(InetAddress.getLocalHost().getHostName());
186 } catch (final UnknownHostException e) {
187 throw new RuntimeException(e);
188 }
189 }
190
191 @Override
192 public IWindowsDomain[] getDomains() {
193 final List<IWindowsDomain> domains = new ArrayList<>();
194 final DomainTrust[] trusts = Netapi32Util.getDomainTrusts();
195 for (final DomainTrust trust : trusts) {
196 domains.add(new WindowsDomainImpl(trust));
197 }
198 return domains.toArray(new IWindowsDomain[0]);
199 }
200
201 @Override
202 public IWindowsIdentity logonDomainUser(final String username, final String domain, final String password) {
203 return this.logonDomainUserEx(username, domain, password, WinBase.LOGON32_LOGON_NETWORK,
204 WinBase.LOGON32_PROVIDER_DEFAULT);
205 }
206
207 @Override
208 public IWindowsIdentity logonDomainUserEx(final String username, final String domain, final String password,
209 final int logonType, final int logonProvider) {
210 final HANDLEByReference phUser = new HANDLEByReference();
211 if (!Advapi32.INSTANCE.LogonUser(username, domain, password, logonType, logonProvider, phUser)) {
212 throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
213 }
214 return new WindowsIdentityImpl(phUser.getValue());
215 }
216
217 @Override
218 public IWindowsIdentity logonUser(final String username, final String password) {
219
220
221 final String[] userNameDomain = username.split("\\\\", 2);
222 if (userNameDomain.length == 2) {
223 return this.logonDomainUser(userNameDomain[1], userNameDomain[0], password);
224 }
225 return this.logonDomainUser(username, null, password);
226 }
227
228 @Override
229 public IWindowsAccount lookupAccount(final String username) {
230 return new WindowsAccountImpl(username);
231 }
232
233 @Override
234 public void resetSecurityToken(final String connectionId) {
235 this.continueContexts.remove(connectionId);
236 }
237
238
239
240
241
242
243 public int getContinueContextsSize() {
244 return this.continueContexts.size();
245 }
246 }