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