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.impl;
25  
26  import com.sun.jna.platform.win32.Secur32;
27  import com.sun.jna.platform.win32.Sspi;
28  import com.sun.jna.platform.win32.Sspi.CtxtHandle;
29  import com.sun.jna.platform.win32.Sspi.SecBufferDesc;
30  import com.sun.jna.platform.win32.SspiUtil.ManagedSecBufferDesc;
31  import com.sun.jna.platform.win32.Win32Exception;
32  import com.sun.jna.platform.win32.WinError;
33  import com.sun.jna.platform.win32.WinNT.HANDLEByReference;
34  import com.sun.jna.ptr.IntByReference;
35  
36  import waffle.windows.auth.IWindowsCredentialsHandle;
37  import waffle.windows.auth.IWindowsIdentity;
38  import waffle.windows.auth.IWindowsImpersonationContext;
39  import waffle.windows.auth.IWindowsSecurityContext;
40  
41  /**
42   * Windows Security Context.
43   */
44  public class WindowsSecurityContextImpl implements IWindowsSecurityContext {
45  
46      /** The principal name. */
47      private String principalName;
48  
49      /** The security package. */
50      private String securityPackage;
51  
52      /** The token. */
53      private ManagedSecBufferDesc token;
54  
55      /** The ctx. */
56      private CtxtHandle ctx;
57  
58      /** The credentials. */
59      private IWindowsCredentialsHandle credentials;
60  
61      /** The continue flag. */
62      private boolean continueFlag;
63  
64      @Override
65      public IWindowsImpersonationContext impersonate() {
66          return new WindowsSecurityContextImpersonationContextImpl(this.ctx);
67      }
68  
69      @Override
70      public IWindowsIdentity getIdentity() {
71          final HANDLEByReference phContextToken = new HANDLEByReference();
72          final int rc = Secur32.INSTANCE.QuerySecurityContextToken(this.ctx, phContextToken);
73          if (WinError.SEC_E_OK != rc) {
74              throw new Win32Exception(rc);
75          }
76          return new WindowsIdentityImpl(phContextToken.getValue());
77      }
78  
79      @Override
80      public String getSecurityPackage() {
81          return this.securityPackage;
82      }
83  
84      @Override
85      public byte[] getToken() {
86          return this.token == null || this.token.getBuffer(0).getBytes() == null ? null
87                  : this.token.getBuffer(0).getBytes().clone();
88      }
89  
90      /**
91       * Get the current Windows security context for a given SSPI package.
92       *
93       * @param securityPackage
94       *            SSPI package.
95       * @param targetName
96       *            The target of the context. The string contents are security-package specific.
97       *
98       * @return Windows security context.
99       */
100     public static IWindowsSecurityContext getCurrent(final String securityPackage, final String targetName) {
101         IWindowsCredentialsHandle credentialsHandle = WindowsCredentialsHandleImpl.getCurrent(securityPackage);
102         credentialsHandle.initialize();
103         try {
104             final WindowsSecurityContextImpl ctx = new WindowsSecurityContextImpl();
105             ctx.setPrincipalName(WindowsAccountImpl.getCurrentUsername());
106             ctx.setCredentialsHandle(credentialsHandle);
107             ctx.setSecurityPackage(securityPackage);
108             ctx.initialize(null, null, targetName);
109 
110             // Starting from here ctx 'owns' the credentials handle, so let's null out the
111             // variable. This will prevent the finally block below from disposing it right away.
112             credentialsHandle = null;
113 
114             return ctx;
115         } finally {
116             if (credentialsHandle != null) {
117                 credentialsHandle.dispose();
118             }
119         }
120     }
121 
122     @Override
123     public void initialize(final CtxtHandle continueCtx, final SecBufferDesc continueToken, final String targetName) {
124         final IntByReference attr = new IntByReference();
125         this.ctx = new CtxtHandle();
126         int tokenSize = Sspi.MAX_TOKEN_SIZE;
127         int rc;
128         do {
129             this.token = new ManagedSecBufferDesc(Sspi.SECBUFFER_TOKEN, tokenSize);
130             rc = Secur32.INSTANCE.InitializeSecurityContext(this.credentials.getHandle(), continueCtx, targetName,
131                     Sspi.ISC_REQ_CONNECTION, 0, Sspi.SECURITY_NATIVE_DREP, continueToken, 0, this.ctx, this.token, attr,
132                     null);
133             switch (rc) {
134                 case WinError.SEC_E_INSUFFICIENT_MEMORY:
135                 case WinError.SEC_E_BUFFER_TOO_SMALL:
136                     tokenSize += Sspi.MAX_TOKEN_SIZE;
137                     break;
138                 case WinError.SEC_I_CONTINUE_NEEDED:
139                     this.continueFlag = true;
140                     break;
141                 case WinError.SEC_E_OK:
142                     this.continueFlag = false;
143                     break;
144                 default:
145                     throw new Win32Exception(rc);
146             }
147         } while (rc == WinError.SEC_E_INSUFFICIENT_MEMORY || rc == WinError.SEC_E_BUFFER_TOO_SMALL);
148     }
149 
150     @Override
151     public void dispose() {
152         WindowsSecurityContextImpl.dispose(this.ctx);
153 
154         if (this.credentials != null) {
155             this.credentials.dispose();
156         }
157     }
158 
159     /**
160      * Dispose a security context.
161      *
162      * @param ctx
163      *            Security context.
164      *
165      * @return True if a context was disposed.
166      */
167     public static boolean dispose(final CtxtHandle ctx) {
168         if (ctx != null && !ctx.isNull()) {
169             final int rc = Secur32.INSTANCE.DeleteSecurityContext(ctx);
170             if (WinError.SEC_E_OK != rc) {
171                 throw new Win32Exception(rc);
172             }
173             return true;
174         }
175         return false;
176     }
177 
178     @Override
179     public String getPrincipalName() {
180         return this.principalName;
181     }
182 
183     /**
184      * Sets the principal name.
185      *
186      * @param value
187      *            the new principal name
188      */
189     public void setPrincipalName(final String value) {
190         this.principalName = value;
191     }
192 
193     @Override
194     public CtxtHandle getHandle() {
195         return this.ctx;
196     }
197 
198     /**
199      * Sets the credentials handle.
200      *
201      * @param handle
202      *            the new credentials handle
203      */
204     public void setCredentialsHandle(final IWindowsCredentialsHandle handle) {
205         this.credentials = handle;
206     }
207 
208     /**
209      * Sets the token.
210      *
211      * @param bytes
212      *            the new token
213      */
214     public void setToken(final byte[] bytes) {
215         this.token = new ManagedSecBufferDesc(Sspi.SECBUFFER_TOKEN, bytes);
216     }
217 
218     /**
219      * Sets the security package.
220      *
221      * @param value
222      *            the new security package
223      */
224     public void setSecurityPackage(final String value) {
225         this.securityPackage = value;
226     }
227 
228     /**
229      * Sets the security context.
230      *
231      * @param phNewServerContext
232      *            the new security context
233      */
234     public void setSecurityContext(final CtxtHandle phNewServerContext) {
235         this.ctx = phNewServerContext;
236     }
237 
238     @Override
239     public boolean isContinue() {
240         return this.continueFlag;
241     }
242 
243     /**
244      * Sets the continue.
245      *
246      * @param b
247      *            the new continue
248      */
249     public void setContinue(final boolean b) {
250         this.continueFlag = b;
251     }
252 
253 }