View Javadoc
1   /*
2    * MIT License
3    *
4    * Copyright (c) 2010-2024 The Waffle Project Contributors: https://github.com/Waffle/waffle/graphs/contributors
5    *
6    * Permission is hereby granted, free of charge, to any person obtaining a copy
7    * of this software and associated documentation files (the "Software"), to deal
8    * in the Software without restriction, including without limitation the rights
9    * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10   * copies of the Software, and to permit persons to whom the Software is
11   * furnished to do so, subject to the following conditions:
12   *
13   * The above copyright notice and this permission notice shall be included in all
14   * copies or substantial portions of the Software.
15   *
16   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22   * SOFTWARE.
23   */
24  package waffle.windows.auth;
25  
26  import static org.assertj.core.api.Assertions.assertThat;
27  
28  import com.sun.jna.WString;
29  import com.sun.jna.platform.win32.Advapi32Util;
30  import com.sun.jna.platform.win32.LMAccess;
31  import com.sun.jna.platform.win32.LMErr;
32  import com.sun.jna.platform.win32.LMJoin;
33  import com.sun.jna.platform.win32.Netapi32;
34  import com.sun.jna.platform.win32.Netapi32Util;
35  import com.sun.jna.platform.win32.Sspi;
36  import com.sun.jna.platform.win32.SspiUtil.ManagedSecBufferDesc;
37  
38  import java.util.Base64;
39  
40  import org.junit.jupiter.api.Assertions;
41  import org.junit.jupiter.api.Assumptions;
42  import org.junit.jupiter.api.Disabled;
43  import org.junit.jupiter.api.Test;
44  import org.slf4j.Logger;
45  import org.slf4j.LoggerFactory;
46  
47  import waffle.mock.MockWindowsAccount;
48  import waffle.windows.auth.impl.WindowsAccountImpl;
49  import waffle.windows.auth.impl.WindowsAuthProviderImpl;
50  import waffle.windows.auth.impl.WindowsCredentialsHandleImpl;
51  import waffle.windows.auth.impl.WindowsSecurityContextImpl;
52  
53  /**
54   * The Class WindowsAuthProviderTest.
55   */
56  class WindowsAuthProviderTest {
57  
58      /** The Constant LOGGER. */
59      private static final Logger LOGGER = LoggerFactory.getLogger(WindowsAuthProviderTest.class);
60  
61      /**
62       * Test logon guest user.
63       */
64      // TODO This was commented out, uncommented and ignore until I can determine if this is valid
65      @Disabled
66      @Test
67      void testLogonGuestUser() {
68          final IWindowsAuthProvider prov = new WindowsAuthProviderImpl();
69          final IWindowsIdentity identity = prov.logonUser("garbage", "garbage");
70          WindowsAuthProviderTest.LOGGER.info("Fqn: {}", identity.getFqn());
71          WindowsAuthProviderTest.LOGGER.info("Guest: {}", Boolean.valueOf(identity.isGuest()));
72          Assertions.assertTrue(identity.getFqn().endsWith("\\Guest"));
73          Assertions.assertTrue(identity.isGuest());
74          identity.dispose();
75      }
76  
77      /**
78       * Test logon user.
79       */
80      @Test
81      void testLogonUser() {
82          final LMAccess.USER_INFO_1 userInfo = new LMAccess.USER_INFO_1();
83          userInfo.usri1_name = new WString("WaffleTestUser").toString();
84          userInfo.usri1_password = new WString("!WAFFLEP$$Wrd0").toString();
85          userInfo.usri1_priv = LMAccess.USER_PRIV_USER;
86          // ignore test if not able to add user (need to be administrator to do this).
87          Assumptions.assumeTrue(LMErr.NERR_Success == Netapi32.INSTANCE.NetUserAdd(null, 1, userInfo, null));
88          try {
89              final IWindowsAuthProvider prov = new WindowsAuthProviderImpl();
90              final IWindowsIdentity identity = prov.logonUser(userInfo.usri1_name, userInfo.usri1_password.toString());
91              Assertions.assertTrue(identity.getFqn().endsWith("\\" + userInfo.usri1_name));
92              Assertions.assertFalse(identity.isGuest());
93              identity.dispose();
94          } finally {
95              Assertions.assertEquals(LMErr.NERR_Success, Netapi32.INSTANCE.NetUserDel(null, userInfo.usri1_name));
96          }
97      }
98  
99      /**
100      * Test impersonate logged on user.
101      */
102     @Test
103     void testImpersonateLoggedOnUser() {
104         final LMAccess.USER_INFO_1 userInfo = new LMAccess.USER_INFO_1();
105         userInfo.usri1_name = new WString(MockWindowsAccount.TEST_USER_NAME).toString();
106         userInfo.usri1_password = new WString(MockWindowsAccount.TEST_PASSWORD).toString();
107         userInfo.usri1_priv = LMAccess.USER_PRIV_USER;
108         // ignore test if not able to add user (need to be administrator to do this).
109         Assumptions.assumeTrue(LMErr.NERR_Success == Netapi32.INSTANCE.NetUserAdd(null, 1, userInfo, null));
110         try {
111             final IWindowsAuthProvider prov = new WindowsAuthProviderImpl();
112             final IWindowsIdentity identity = prov.logonUser(userInfo.usri1_name, userInfo.usri1_password);
113             final IWindowsImpersonationContext ctx = identity.impersonate();
114             Assertions.assertEquals(userInfo.usri1_name, Advapi32Util.getUserName());
115             ctx.revertToSelf();
116             Assertions.assertNotEquals(userInfo.usri1_name, Advapi32Util.getUserName());
117             identity.dispose();
118         } finally {
119             Assertions.assertEquals(LMErr.NERR_Success, Netapi32.INSTANCE.NetUserDel(null, userInfo.usri1_name));
120         }
121     }
122 
123     /**
124      * Test get current computer.
125      */
126     @Test
127     void testGetCurrentComputer() {
128         final IWindowsAuthProvider prov = new WindowsAuthProviderImpl();
129         final IWindowsComputer computer = prov.getCurrentComputer();
130         WindowsAuthProviderTest.LOGGER.info(computer.getComputerName());
131         assertThat(computer.getComputerName()).isNotEmpty();
132         WindowsAuthProviderTest.LOGGER.info(computer.getJoinStatus());
133         WindowsAuthProviderTest.LOGGER.info(computer.getMemberOf());
134         final String[] localGroups = computer.getGroups();
135         Assertions.assertNotNull(localGroups);
136         assertThat(localGroups).isNotEmpty();
137         for (final String localGroup : localGroups) {
138             WindowsAuthProviderTest.LOGGER.info(" {}", localGroup);
139         }
140     }
141 
142     /**
143      * Test get domains.
144      */
145     @Test
146     void testGetDomains() {
147         if (Netapi32Util.getJoinStatus() != LMJoin.NETSETUP_JOIN_STATUS.NetSetupDomainName) {
148             return;
149         }
150 
151         final IWindowsAuthProvider prov = new WindowsAuthProviderImpl();
152         final IWindowsDomain[] domains = prov.getDomains();
153         Assertions.assertNotNull(domains);
154         for (final IWindowsDomain domain : domains) {
155             WindowsAuthProviderTest.LOGGER.info("{}: {}", domain.getFqn(), domain.getTrustDirectionString());
156         }
157     }
158 
159     /**
160      * Test accept security token.
161      */
162     @Test
163     void testAcceptSecurityToken() {
164         final String securityPackage = "Negotiate";
165         final String targetName = "localhost";
166         IWindowsCredentialsHandle clientCredentials = null;
167         WindowsSecurityContextImpl clientContext = null;
168         IWindowsSecurityContext serverContext = null;
169         try {
170             // client credentials handle
171             clientCredentials = WindowsCredentialsHandleImpl.getCurrent(securityPackage);
172             clientCredentials.initialize();
173             // initial client security context
174             clientContext = new WindowsSecurityContextImpl();
175             clientContext.setPrincipalName(WindowsAccountImpl.getCurrentUsername());
176             clientContext.setCredentialsHandle(clientCredentials);
177             clientContext.setSecurityPackage(securityPackage);
178             clientContext.initialize(null, null, targetName);
179             // accept on the server
180             final WindowsAuthProviderImpl provider = new WindowsAuthProviderImpl();
181             final String connectionId = "testConnection-" + Thread.currentThread().getId();
182             do {
183                 // accept the token on the server
184                 serverContext = provider.acceptSecurityToken(connectionId, clientContext.getToken(), securityPackage);
185 
186                 if (serverContext != null && serverContext.isContinue()) {
187                     // initialize on the client
188                     final ManagedSecBufferDesc continueToken = new ManagedSecBufferDesc(Sspi.SECBUFFER_TOKEN,
189                             serverContext.getToken());
190                     clientContext.initialize(clientContext.getHandle(), continueToken, targetName);
191                     WindowsAuthProviderTest.LOGGER.info("Token: {}",
192                             Base64.getEncoder().encodeToString(serverContext.getToken()));
193                 }
194 
195             } while (serverContext != null && serverContext.isContinue());
196 
197             if (serverContext != null) {
198                 assertThat(serverContext.getIdentity().getFqn()).isNotEmpty();
199 
200                 WindowsAuthProviderTest.LOGGER.info(serverContext.getIdentity().getFqn());
201                 for (final IWindowsAccount group : serverContext.getIdentity().getGroups()) {
202                     WindowsAuthProviderTest.LOGGER.info(" {}", group.getFqn());
203                 }
204             }
205         } finally {
206             if (serverContext != null) {
207                 serverContext.dispose();
208             }
209             if (clientContext != null) {
210                 clientContext.dispose();
211             }
212             if (clientCredentials != null) {
213                 clientCredentials.dispose();
214             }
215         }
216     }
217 
218     /**
219      * Test security contexts expire.
220      *
221      * @throws InterruptedException
222      *             the interrupted exception
223      */
224     @Test
225     void testSecurityContextsExpire() throws InterruptedException {
226         final String securityPackage = "Negotiate";
227         IWindowsCredentialsHandle clientCredentials = null;
228         WindowsSecurityContextImpl clientContext = null;
229         IWindowsSecurityContext serverContext = null;
230         try {
231             // client credentials handle
232             clientCredentials = WindowsCredentialsHandleImpl.getCurrent(securityPackage);
233             clientCredentials.initialize();
234             // initial client security context
235             clientContext = new WindowsSecurityContextImpl();
236             clientContext.setPrincipalName(WindowsAccountImpl.getCurrentUsername());
237             clientContext.setCredentialsHandle(clientCredentials);
238             clientContext.setSecurityPackage(securityPackage);
239             clientContext.initialize(null, null, WindowsAccountImpl.getCurrentUsername());
240             // accept on the server
241             final WindowsAuthProviderImpl provider = new WindowsAuthProviderImpl(1);
242             final int max = 100;
243             for (int i = 0; i < max; i++) {
244                 Thread.sleep(25);
245                 final String connectionId = "testConnection_" + i;
246                 serverContext = provider.acceptSecurityToken(connectionId, clientContext.getToken(), securityPackage);
247                 assertThat(provider.getContinueContextsSize()).isPositive();
248             }
249             WindowsAuthProviderTest.LOGGER.info("Cached security contexts: {}",
250                     Integer.valueOf(provider.getContinueContextsSize()));
251             Assertions.assertNotEquals(max, provider.getContinueContextsSize());
252         } finally {
253             if (serverContext != null) {
254                 serverContext.dispose();
255             }
256             if (clientContext != null) {
257                 clientContext.dispose();
258             }
259             if (clientCredentials != null) {
260                 clientCredentials.dispose();
261             }
262         }
263     }
264 
265     /**
266      * Test accept and impersonate security token.
267      */
268     @Test
269     void testAcceptAndImpersonateSecurityToken() {
270         final String securityPackage = "Negotiate";
271         final String targetName = "localhost";
272         IWindowsCredentialsHandle clientCredentials = null;
273         WindowsSecurityContextImpl clientContext = null;
274         IWindowsSecurityContext serverContext = null;
275         try {
276             // client credentials handle
277             clientCredentials = WindowsCredentialsHandleImpl.getCurrent(securityPackage);
278             clientCredentials.initialize();
279             // initial client security context
280             clientContext = new WindowsSecurityContextImpl();
281             clientContext.setPrincipalName(WindowsAccountImpl.getCurrentUsername());
282             clientContext.setCredentialsHandle(clientCredentials);
283             clientContext.setSecurityPackage(securityPackage);
284             clientContext.initialize(null, null, targetName);
285             // accept on the server
286             final WindowsAuthProviderImpl provider = new WindowsAuthProviderImpl();
287             final String connectionId = "testConnection";
288             do {
289                 // accept the token on the server
290                 serverContext = provider.acceptSecurityToken(connectionId, clientContext.getToken(), securityPackage);
291 
292                 if (serverContext != null && serverContext.isContinue()) {
293                     // initialize on the client
294                     final ManagedSecBufferDesc continueToken = new ManagedSecBufferDesc(Sspi.SECBUFFER_TOKEN,
295                             serverContext.getToken());
296                     clientContext.initialize(clientContext.getHandle(), continueToken, targetName);
297                 }
298 
299             } while (serverContext != null && serverContext.isContinue());
300 
301             if (serverContext != null) {
302                 assertThat(serverContext.getIdentity().getFqn()).isNotEmpty();
303 
304                 final IWindowsImpersonationContext impersonationCtx = serverContext.impersonate();
305                 impersonationCtx.revertToSelf();
306 
307                 WindowsAuthProviderTest.LOGGER.info(serverContext.getIdentity().getFqn());
308                 for (final IWindowsAccount group : serverContext.getIdentity().getGroups()) {
309                     WindowsAuthProviderTest.LOGGER.info(" {}", group.getFqn());
310                 }
311             }
312         } finally {
313             if (serverContext != null) {
314                 serverContext.dispose();
315             }
316             if (clientContext != null) {
317                 clientContext.dispose();
318             }
319             if (clientCredentials != null) {
320                 clientCredentials.dispose();
321             }
322         }
323     }
324 }