From 9771b59a5755b8dc29d13e6f0649b1b40c2d4991 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Bl=C3=A4sing?= Date: Sun, 23 Jul 2017 17:05:16 +0200 Subject: [PATCH] Enhance SSPI function converage and bugfix SecBufferDesc (Re-)Bind SSPI functions: - InitializeSecurityContext - AcceptSecurityContext - QueryCredentialsAttributes - QuerySecurityPackageInfo - EncryptMessage - DecryptMessage - MakeSignature - VerifySignature Add binding for SEC_WINNT_AUTH_IDENTITY structure. The existing SecBufferDesc binding is replaced as the binding does not correctly map the native structure. The pBuffers member is not an array of SecBuffer.ByReference, but a pointer to an array of SecBuffer's. This manifests when more than one buffer is specified. The SecBufferDesc structure is the literal transliteration of the native C header. In addition a ManagedSecBufferDesc was introduced, that allows easy access to its members, as long, as the structure is managed from the java side. Closes: #843 --- CHANGES.md | 11 + .../com/sun/jna/platform/win32/Secur32.java | 290 +++++++- .../src/com/sun/jna/platform/win32/Sspi.java | 664 +++++++++++++++++- .../com/sun/jna/platform/win32/SspiUtil.java | 101 +++ .../sun/jna/platform/win32/Secur32Test.java | 484 +++++++++++-- .../com/sun/jna/platform/win32/SspiTest.java | 27 + 6 files changed, 1476 insertions(+), 101 deletions(-) create mode 100644 contrib/platform/src/com/sun/jna/platform/win32/SspiUtil.java create mode 100644 contrib/platform/test/com/sun/jna/platform/win32/SspiTest.java diff --git a/CHANGES.md b/CHANGES.md index fcbb45e9a4..c241247df5 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -15,6 +15,17 @@ Features Bug Fixes --------- * [#652](https://github.com/java-native-access/jna/issues/652): Dead Lock in class initialization - [@matthiasblaesing](https://github.com/matthiasblaesing). +* [#843](https://github.com/java-native-access/jna/pull/843): Correctly bind `com.sun.jna.platform.win32.SecBufferDesc` and add convenience binding as `com.sun.jna.platform.win32.SspiUtil.ManagedSecBufferDesc`. Bind SSPI functions `InitializeSecurityContext`, `AcceptSecurityContext`, `QueryCredentialsAttributes`, `QuerySecurityPackageInfo`, `EncryptMessage`, `DecryptMessage`, `MakeSignature`, `VerifySignature` in `com.sun.jna.platform.win32.Secur32` - [@matthiasblaesing](https://github.com/matthiasblaesing). + + +Breaking Changes +---------------- +* `Pointer#SIZE` is removed. Its use is replaced by `Native#POINTER_SIZE` + to prevent a class loading deadlock, when JNA is initialized from multiple threads +* `SecBufferDesc` was incompatibly changed to match the correct native semantics. + SecBufferDesc describing more than one buffer were broken. For most usecases + `com.sun.jna.platform.win32.SspiUtil.ManagedSecBufferDesc` is the best + alternative. Release 4.5.0 (Next release) ============================ diff --git a/contrib/platform/src/com/sun/jna/platform/win32/Secur32.java b/contrib/platform/src/com/sun/jna/platform/win32/Secur32.java index 32f8bf98d7..f1a3768695 100644 --- a/contrib/platform/src/com/sun/jna/platform/win32/Secur32.java +++ b/contrib/platform/src/com/sun/jna/platform/win32/Secur32.java @@ -90,8 +90,8 @@ public abstract class EXTENDED_NAME_FORMAT { * @param pAuthData * A pointer to package-specific data. This parameter can be NULL, which indicates * that the default credentials for that package must be used. To use supplied - * credentials, pass a SEC_WINNT_AUTH_IDENTITY structure that includes those credentials - * in this parameter. + * credentials, pass a {@link com.sun.jna.platform.win32.Sspi.SEC_WINNT_AUTH_IDENTITY} + * structure that includes those credentials in this parameter. * @param pGetKeyFn * This parameter is not used and should be set to NULL. * @param pvGetKeyArgument @@ -111,6 +111,7 @@ int AcquireCredentialsHandle(String pszPrincipal, String pszPackage, Pointer pAuthData, Pointer pGetKeyFn, // TODO: SEC_GET_KEY_FN Pointer pvGetKeyArgument, CredHandle phCredential, TimeStamp ptsExpiry); + /** * The InitializeSecurityContext function initiates the client side, outbound security @@ -178,7 +179,7 @@ int InitializeSecurityContext(CredHandle phCredential, CtxtHandle phContext, int TargetDataRep, SecBufferDesc pInput, int Reserved2, CtxtHandle phNewContext, SecBufferDesc pOutput, IntByReference pfContextAttr, TimeStamp ptsExpiry); - + /** * The DeleteSecurityContext function deletes the local data structures associated * with the specified security context. @@ -204,7 +205,7 @@ int InitializeSecurityContext(CredHandle phCredential, CtxtHandle phContext, * If the function fails, the return value is SEC_E_INVALID_HANDLE; */ int FreeCredentialsHandle(CredHandle phCredential); - + /** * The AcceptSecurityContext function enables the server component of a transport * application to establish a security context between the server and a remote client. @@ -251,7 +252,7 @@ int AcceptSecurityContext(CredHandle phCredential, CtxtHandle phContext, SecBufferDesc pInput, int fContextReq, int TargetDataRep, CtxtHandle phNewContext, SecBufferDesc pOutput, IntByReference pfContextAttr, TimeStamp ptsTimeStamp); - + /** * The EnumerateSecurityPackages function returns an array of SecPkgInfo structures that * describe the security packages available to the client. @@ -340,4 +341,283 @@ int AcceptSecurityContext(CredHandle phCredential, CtxtHandle phContext, * If the function fails, the return value is a nonzero error code. */ int QueryContextAttributes(CtxtHandle phContext, int ulAttribute, Structure pBuffer); + + /** + * Retrieves the attributes of a credential, such as the name associated + * with the credential. The information is valid for any security context + * created with the specified credential. + * + * @param phCredential A handle of the credentials to be queried. + * @param ulAttribute Specifies the attribute of the context to be returned. + * This parameter can be one of the SECPKG_ATTR_* values + * defined in {@link Sspi}. + * @param pBuffer A pointer to a structure that receives the attributes. + * The type of structure pointed to depends on the value + * specified in the ulAttribute parameter. + * @return If the function succeeds, the return value is SEC_E_OK. If the + * function fails, the return value is a nonzero error code. + */ + int QueryCredentialsAttributes(Sspi.CredHandle phCredential, int ulAttribute, Structure pBuffer); + + /** + * Retrieves information about a specified security package. This + * information includes the bounds on sizes of authentication information, + * credentials, and contexts. + * + * @param pszPackageName Name of the security package. + * @param ppPackageInfo Variable that receives a pointer to a SecPkgInfo + * structure containing information about the + * specified security package. + * @return If the function succeeds, the return value is SEC_E_OK. + * If the function fails, the return value is a nonzero error code. + */ + int QuerySecurityPackageInfo(String pszPackageName, Sspi.PSecPkgInfo ppPackageInfo); + + /** + * EncryptMessage (Kerberos) function + * + *

+ * The EncryptMessage (Kerberos) function encrypts a message to provide + * privacy. EncryptMessage (Kerberos) allows an application to choose among + * cryptographic algorithms supported by the chosen mechanism. The + * EncryptMessage (Kerberos) function uses the security context referenced + * by the context handle. Some packages do not have messages to be encrypted + * or decrypted but rather provide an integrity hash that can be + * checked.

+ * + * @param phContext A handle to the security context to be used to encrypt + * the message. + * @param fQOP Package-specific flags that indicate the quality of + * protection. A security package can use this parameter to + * enable the selection of cryptographic algorithms. This + * parameter can be the following flag: + * {@link Sspi#SECQOP_WRAP_NO_ENCRYPT}. + * @param pMessage A pointer to a SecBufferDesc structure. On input, the + * structure references one or more SecBuffer structures + * that can be of type SECBUFFER_DATA. That buffer contains + * the message to be encrypted. The message is encrypted in + * place, overwriting the original contents of the + * structure. + * + *

+ * The function does not process buffers with the SECBUFFER_READONLY + * attribute.

+ * + *

+ * The length of the SecBuffer structure that contains the message must be + * no greater than cbMaximumMessage, which is obtained from the + * QueryContextAttributes (Kerberos) (SECPKG_ATTR_STREAM_SIZES) + * function.

+ * + *

+ * Applications that do not use SSL must supply a SecBuffer of type + * SECBUFFER_PADDING.

+ * @param MessageSeqNo The sequence number that the transport application + * assigned to the message. If the transport application + * does not maintain sequence numbers, this parameter + * must be zero. + * @return If the function succeeds, the function returns SEC_E_OK. + * @see MSDN Entry + */ + int EncryptMessage(CtxtHandle phContext, int fQOP, SecBufferDesc pMessage, int MessageSeqNo); + + /** + * VerifySignature function. + * + *

+ * Verifies that a message signed by using the MakeSignature function was + * received in the correct sequence and has not been modified.

+ * + *

+ * Warning

+ * + *

+ * The VerifySignature function will fail if the message was signed using + * the RsaSignPssSha512 algorithm on a different version of Windows. For + * example, a message that was signed by calling the MakeSignature function + * on Windows 8 will cause the VerifySignature function on Windows 8.1 to + * fail.

+ * + * @param phContext A handle to the security context to use for the + * message. + * @param pMessage Pointer to a SecBufferDesc structure that references + * a set of SecBuffer structures that contain the + * message and signature to verify. The signature is in + * a SecBuffer structure of type SECBUFFER_TOKEN. + * @param MessageSeqNo Specifies the sequence number expected by the + * transport application, if any. If the transport + * application does not maintain sequence numbers, this + * parameter is zero. + * @param pfQOP Pointer to a ULONG variable that receives + * package-specific flags that indicate the quality of + * protection. + * + *

Some security packages ignore this parameter.

+ * + * @return If the function verifies that the message was received in the + * correct sequence and has not been modified, the return value is + * SEC_E_OK. + * + *

+ * If the function determines that the message is not correct according to + * the information in the signature, the return value can be one of the + * following error codes.

+ * + * + * + * + * + * + * + * + *
Return codeDescription
SEC_E_OUT_OF_SEQUENCEThe message was not received in the + * correct sequence.
SEC_E_MESSAGE_ALTEREDThe message has been + * altered.
SEC_E_INVALID_HANDLEThe context handle specified by + * phContext is not valid.
SEC_E_INVALID_TOKENpMessage did not contain a valid + * SECBUFFER_TOKEN buffer, or contained too few buffers.
SEC_E_QOP_NOT_SUPPORTEDThe quality of protection + * negotiated between the client and server did not include integrity + * checking.
+ */ + int VerifySignature(CtxtHandle phContext, SecBufferDesc pMessage, int MessageSeqNo, IntByReference pfQOP); + + /** + * MakeSignature function. + * + *

+ * The MakeSignature function generates a cryptographic checksum of the + * message, and also includes sequencing information to prevent message loss + * or insertion. MakeSignature allows the application to choose between + * several cryptographic algorithms, if supported by the chosen mechanism. + * The MakeSignature function uses the security context referenced by the + * context handle.

+ * + *

+ * Remarks

+ * + *

+ * Remarks

+ *

+ * The MakeSignature function generates a signature that is based on the + * message and the session key for the context.

+ *

+ * The VerifySignature function verifies the messages signed by the + * MakeSignature function.

+ *

+ * If the transport application created the security context to support + * sequence detection and the caller provides a sequence number, the + * function includes this information in the signature. This protects + * against reply, insertion, and suppression of messages. The security + * package incorporates the sequence number passed down from the transport + * application.

+ * + * @param phContext A handle to the security context to use to sign the + * message. + * @param fQOP Package-specific flags that indicate the quality of + * protection. A security package can use this parameter + * to enable the selection of cryptographic algorithms. + *

+ * When using the Digest SSP, this parameter must be set to zero.

+ * + * @param pMessage A pointer to a SecBufferDesc structure. On input, the + * structure references one or more SecBuffer structures + * that contain the message to be signed. The function + * does not process buffers with the + * SECBUFFER_READONLY_WITH_CHECKSUM attribute. + * + *

+ * The SecBufferDesc structure also references a SecBuffer structure of type + * SECBUFFER_TOKEN that receives the signature.

+ *

+ * When the Digest SSP is used as an HTTP authentication protocol, the + * buffers should be configured as follows.

+ * + * + * + * + * + * + * + *
Buffer #/buffer typeMeaning
0 / SECBUFFER_TOKENEmpty.
1 / SECBUFFER_PKG_PARAMSMethod.
2 / SECBUFFER_PKG_PARAMSURL.
3 / SECBUFFER_PKG_PARAMSHEntity. For more information, + * see Input Buffers for the Digest Challenge Response.
4 / SECBUFFER_PADDINGEmpty. Receives the + * signature.
+ *

+ * When the Digest SSP is used as an SASL mechanism, the buffers should be + * configured as follows.

+ * + * + * + * + * + *
Buffer #/buffer typeMeaning
0 / SECBUFFER_TOKENEmpty. Receives the signature. This + * buffer must be large enough to hold the largest possible signature. + * Determine the size required by calling the QueryContextAttributes + * (General) function and specifying SECPKG_ATTR_SIZES. Check the returned + * SecPkgContext_Sizes structure member cbMaxSignature.
1 / SECBUFFER_DATAMessage to be signed.
2 / SECBUFFER_PADDINGEmpty.
+ * @param MessageSeqNo * + * The sequence number that the transport application + * assigned to the message. If the transport application + * does not maintain sequence numbers, this parameter is + * zero. + * + *

+ * When using the Digest SSP, this parameter must be set to zero. The Digest + * SSP manages sequence numbering internally.

+ * + * @return If the function succeeds, the function returns SEC_E_OK. + * + *

+ * If the function fails, it returns one of the following error codes.

+ * + * + * + * + * + * + * + * + * + * + *
Return codeDescription
SEC_I_RENEGOTIATEThe remote party requires a new + * handshake sequence or the application has just initiated a shutdown. + * Return to the negotiation loop and call AcceptSecurityContext (General) + * or InitializeSecurityContext (General) again. An empty input buffer is + * passed in the first call.
SEC_E_INVALID_HANDLEThe context handle specified by + * phContext is not valid.
SEC_E_INVALID_TOKENpMessage did not contain a valid + * SECBUFFER_TOKEN buffer or contained too few buffers.
SEC_E_OUT_OF_SEQUENCEThe nonce count is out of + * sequence.
SEC_E_NO_AUTHENTICATING_AUTHORITYThe security context + * (phContext) must be revalidated.
STATUS_INVALID_PARAMETERThe nonce count is not + * numeric.
SEC_E_QOP_NOT_SUPPORTEDThe quality of protection + * negotiated between the client and server did not include integrity + * checking.
+ */ + int MakeSignature(CtxtHandle phContext, int fQOP, SecBufferDesc pMessage, int MessageSeqNo); + + /** + * DecryptMessage (Kerberos) function + * + *

+ * The DecryptMessage (Kerberos) function decrypts a message. Some packages + * do not encrypt and decrypt messages but rather perform and check an + * integrity hash.

+ * + * @param phContext A handle to the security context to be used to + * encrypt the message. + * @param pMessage A pointer to a SecBufferDesc structure. On input, the + * structure references one or more SecBuffer structures + * that may be of type SECBUFFER_DATA. The buffer + * contains the encrypted message. The encrypted message + * is decrypted in place, overwriting the original + * contents of its buffer. + * @param MessageSeqNo The sequence number expected by the transport + * application, if any. If the transport application + * does not maintain sequence numbers, this parameter + * must be set to zero. + * @param pfQOP A pointer to a variable of type ULONG that receives + * package-specific flags that indicate the quality of + * protection. This parameter can be the following flag: + * {@link Sspi#SECQOP_WRAP_NO_ENCRYPT}. + * @return If the function verifies that the message was received in the correct sequence, the function returns SEC_E_OK. + * @see MSDN Entry + */ + int DecryptMessage(CtxtHandle phContext, SecBufferDesc pMessage, int MessageSeqNo, IntByReference pfQOP); } diff --git a/contrib/platform/src/com/sun/jna/platform/win32/Sspi.java b/contrib/platform/src/com/sun/jna/platform/win32/Sspi.java index 309daf1e13..52b1cde432 100644 --- a/contrib/platform/src/com/sun/jna/platform/win32/Sspi.java +++ b/contrib/platform/src/com/sun/jna/platform/win32/Sspi.java @@ -65,6 +65,11 @@ public interface Sspi { */ int SECURITY_NATIVE_DREP = 0x10; + + /** + * Specifies network data representation. + */ + int SECURITY_NETWORK_DREP = 0x00; // Flags for the fContextReq parameter of InitializeSecurityContext or AcceptSecurityContext. @@ -142,15 +147,211 @@ public interface Sspi { */ int SECBUFFER_TOKEN = 2; - // for ulAttribute parameter in QueryContextAttributes function - // (https://msdn.microsoft.com/en-us/library/windows/desktop/aa379326(v=vs.85).aspx) + /** + * The pBuffer parameter contains a pointer to a {@link SecPkgContext_Sizes} + * structure. + * + *

Queries the sizes of the structures used in the per-message functions.

+ */ + int SECPKG_ATTR_SIZES = 0; + /** + * The pBuffer parameter contains a pointer to a {@link SecPkgCredentials_Names} + * structure. + * + *

Queries the name associated with the context.

+ */ + int SECPKG_ATTR_NAMES = 1; + /** + * The pBuffer parameter contains a pointer to a SecPkgContext_Lifespan + * structure. + * + *

Queries the life span of the context.

+ */ + int SECPKG_ATTR_LIFESPAN = 2; + /** + * The pBuffer parameter contains a pointer to a SecPkgContext_DceInfo + * structure. + * + *

Queries for authorization data used by DCE services.

+ */ + int SECPKG_ATTR_DCE_INFO = 3; + /** + * The pBuffer parameter contains a pointer to a SecPkgContext_StreamSizes + * structure. + * + *

Queries the sizes of the various parts of a stream used in the + * per-message functions.

+ *

This attribute is supported only by the Schannel security package.

+ */ + int SECPKG_ATTR_STREAM_SIZES = 4; + /** + * The pBuffer parameter contains a pointer to a SecPkgContext_KeyInfo + * structure. + * + *

Queries information about the keys used in a security context.

+ */ + int SECPKG_ATTR_KEY_INFO = 5; + /** + * The pBuffer parameter contains a pointer to a SecPkgContext_Authority + * structure. + * + *

Queries the name of the authenticating authority.

+ */ + int SECPKG_ATTR_AUTHORITY = 6; + int SECPKG_ATTR_PROTO_INFO = 7; + /** + * The pBuffer parameter contains a pointer to a + * SecPkgContext_PasswordExpiry structure. + * + *

Returns password expiration information.

+ */ + int SECPKG_ATTR_PASSWORD_EXPIRY = 8; + /** + * The pBuffer parameter contains a pointer to a + * {@link SecPkgContext_SessionKey} structure. + * + * Returns information about the session keys. + */ + int SECPKG_ATTR_SESSION_KEY = 9; /** * The pBuffer parameter contains a pointer to a * {@link SecPkgContext_PackageInfo} structure. - * + * * Returns information on the SSP in use. */ - int SECPKG_ATTR_PACKAGE_INFO = 0x0000000A; + int SECPKG_ATTR_PACKAGE_INFO = 10; + int SECPKG_ATTR_USER_FLAGS = 11; + /** + * The pBuffer parameter contains a pointer to a + * {@link SecPkgContext_NegotiationInfo} structure. + * + *

Returns information about the security package to be used with the + * negotiation process and the current state of the negotiation for the use + * of that package.

+ */ + int SECPKG_ATTR_NEGOTIATION_INFO = 12; + /** + * The pBuffer parameter contains a pointer to a SecPkgContext_NativeNames + * structure. + * + *

Returns the principal name (CNAME) from the outbound ticket.

+ */ + int SECPKG_ATTR_NATIVE_NAMES = 13; + /** + * The pBuffer parameter contains a pointer to a {@link SecPkgContext_Flags} + * structure. + * + *

Returns information about the negotiated context flags.

+ */ + int SECPKG_ATTR_FLAGS = 14; + // These attributes exist only in Win XP and greater + int SECPKG_ATTR_USE_VALIDATED = 15; + int SECPKG_ATTR_CREDENTIAL_NAME = 16; + /** + * The pBuffer parameter contains a pointer to a + * SecPkgContext_TargetInformation structure. + * + *

Returns information about the name of the remote server.

+ */ + int SECPKG_ATTR_TARGET_INFORMATION = 17; + /** + * The pBuffer parameter contains a pointer to a SecPkgContext_AccessToken + * structure. + * + *

Returns a handle to the access token.

+ */ + int SECPKG_ATTR_ACCESS_TOKEN = 18; + // These attributes exist only in Win2K3 and greater + int SECPKG_ATTR_TARGET = 19; + int SECPKG_ATTR_AUTHENTICATION_ID = 20; + // These attributes exist only in Win2K3SP1 and greater + int SECPKG_ATTR_LOGOFF_TIME = 21; + // + // win7 or greater + // + int SECPKG_ATTR_NEGO_KEYS = 22; + int SECPKG_ATTR_PROMPTING_NEEDED = 24; + /** + * The pBuffer parameter contains a pointer to a SecPkgContext_Bindings + * structure that specifies channel binding information. + * + *

This value is supported only by the Schannel security package.

+ * + *

Windows Server 2008, Windows Vista, Windows Server 2003 and + * Windows XP: + * This value is not supported.

+ */ + int SECPKG_ATTR_UNIQUE_BINDINGS = 25; + /** + * The pBuffer parameter contains a pointer to a SecPkgContext_Bindings + * structure that specifies channel binding information. + * + *

This attribute is supported only by the Schannel security package.

+ * + *

Windows Server 2008, Windows Vista, Windows Server 2003 and + * Windows XP: + * This value is not supported.

+ */ + int SECPKG_ATTR_ENDPOINT_BINDINGS = 26; + /** + * The pBuffer parameter contains a pointer to a + * SecPkgContext_ClientSpecifiedTarget structure that represents the service + * principal name (SPN) of the initial target supplied by the client. + * + *

Windows Server 2008, Windows Vista, Windows Server 2003 and + * Windows XP: + * This value is not supported.

+ */ + int SECPKG_ATTR_CLIENT_SPECIFIED_TARGET = 27; + + /** + * The pBuffer parameter contains a pointer to a + * SecPkgContext_LastClientTokenStatus structure that specifies whether the + * token from the most recent call to the InitializeSecurityContext function + * is the last token from the client. + * + *

This value is supported only by the Negotiate, Kerberos, and NTLM + * security packages.

+ * + *

Windows Server 2008, Windows Vista, Windows Server 2003 and + * Windows XP: + * This value is not supported.

+ */ + int SECPKG_ATTR_LAST_CLIENT_TOKEN_STATUS = 30; + int SECPKG_ATTR_NEGO_PKG_INFO = 31; // contains nego info of packages + int SECPKG_ATTR_NEGO_STATUS = 32; // contains the last error + int SECPKG_ATTR_CONTEXT_DELETED = 33; // a context has been deleted + + /** + * The pBuffer parameter contains a pointer to a + * SecPkgContext_SubjectAttributes structure. + * + *

This value returns information about the security attributes for the + * connection.

+ * + *

This value is supported only on the CredSSP server.

+ * + *

Windows Server 2008, Windows Vista, Windows Server 2003 and + * Windows XP: + * This value is not supported.

+ */ + int SECPKG_ATTR_SUBJECT_SECURITY_ATTRIBUTES = 128; + + /** + * Negotiation has been completed. + */ + int SECPKG_NEGOTIATION_COMPLETE = 0; + /** + * Negotiations not yet completed. + */ + int SECPKG_NEGOTIATION_OPTIMISTIC = 1; + /** + * Negotiations in progress. + */ + int SECPKG_NEGOTIATION_IN_PROGRESS = 2; + int SECPKG_NEGOTIATION_DIRECT = 3; + int SECPKG_NEGOTIATION_TRY_MULTICRED = 4; + // flags for SecPkgInfo fCapabilities // (https://msdn.microsoft.com/en-us/library/windows/desktop/aa380104(v=vs.85).aspx) @@ -252,6 +453,22 @@ public interface Sspi { */ int SECPKG_FLAG_APPCONTAINER_CHECKS = 0x00800000; + /** + * Returns the name of a credential in a pbuffer of type {@link SecPkgCredentials_Names}. + */ + int SECPKG_CRED_ATTR_NAMES = 1; + + /** + * Produce a header or trailer but do not encrypt the message. + */ + int SECQOP_WRAP_NO_ENCRYPT = 0x80000001; + /** + * Send an Schannel alert message. In this case, the pMessage parameter must + * contain a standard two-byte SSL/TLS event code. This value is supported + * only by the Schannel SSP. + */ + int SECQOP_WRAP_OOB_DATA = 0x40000000; + /** * Security handle. */ @@ -423,9 +640,21 @@ protected List getFieldOrder() { return FIELDS; } } - + + /** + * The SecBufferDesc structure describes an array of SecBuffer structures to + * pass from a transport application to a security package. + * + *

SecBufferDesc was introduced because {@link SecBufferDesc} does not + * correctly cover the case there not exactly one {@link SecBuffer} is + * passed to the security package.

+ * + *

If the SecBufferDesc is managed from the java side, prefer to use + * {@link com.sun.jna.platform.win32.SspiUtil.ManagedSecBufferDesc ManagedSecBufferDesc}.

+ */ public static class SecBufferDesc extends Structure { public static final List FIELDS = createFieldsOrder("ulVersion", "cBuffers", "pBuffers"); + /** * Version number. */ @@ -437,9 +666,7 @@ public static class SecBufferDesc extends Structure { /** * Pointer to array of buffers. */ - public SecBuffer.ByReference[] pBuffers = new SecBuffer.ByReference[] { - new SecBuffer.ByReference() - }; + public Pointer pBuffers; /** * Create a new SecBufferDesc with one SECBUFFER_EMPTY buffer. @@ -448,34 +675,12 @@ public SecBufferDesc() { super(); } - /** - * Create a new SecBufferDesc with initial data. - * @param type Token type. - * @param token Initial token data. - */ - public SecBufferDesc(int type, byte[] token) { - pBuffers[0] = new SecBuffer.ByReference(type, token); - } - - /** - * Create a new SecBufferDesc with one SecBuffer of a given type and size. - * @param type type - * @param tokenSize token size - */ - public SecBufferDesc(int type, int tokenSize) { - pBuffers[0] = new SecBuffer.ByReference(type, tokenSize); - } - - public byte[] getBytes() { - return pBuffers[0].getBytes(); - } - @Override protected List getFieldOrder() { return FIELDS; } } - + /** * A security integer. */ @@ -610,4 +815,401 @@ protected List getFieldOrder() { } } + /** + * The SecPkgCredentials_Names structure holds the name of the user + * associated with a context. + * + *

+ * The + * {@link Secur32#QueryCredentialsAttributes(com.sun.jna.platform.win32.Sspi.CredHandle, int, com.sun.jna.Structure)} + * function uses this structure.

+ */ + public static class SecPkgCredentials_Names extends Structure { + + public static class ByReference extends SecPkgCredentials_Names implements Structure.ByReference { + + } + + public static final List FIELDS = createFieldsOrder("sUserName"); + + /** + * Pointer to a null-terminated string containing the name of the user + * represented by the credential. If the security package sets the + * SECPKG_FLAG_ACCEPT_WIN32_NAME flag to indicate that it can process + * Windows names, this name can be used in other Windows calls. + */ + public Pointer sUserName; + + public SecPkgCredentials_Names() { + super(W32APITypeMapper.DEFAULT); + } + + @Override + protected List getFieldOrder() { + return FIELDS; + } + + /** + * @return value of userName attribute + */ + public synchronized String getUserName() { + if (sUserName == null) { + return null; + } + return Boolean.getBoolean("w32.ascii") ? sUserName.getString(0) : sUserName.getWideString(0); + } + + /** + * Free native buffer + * + * @return {@link WinError#SEC_E_OK} if ok + */ + public synchronized int free() { + if (sUserName != null) { + int result = Secur32.INSTANCE.FreeContextBuffer(sUserName); + sUserName = null; + return result; + } + return WinError.SEC_E_OK; + } + } + + /** + * The SecPkgContext_Sizes structure indicates the sizes of important + * structures used in the message support functions. + * + *

+ * The {@link Secur32#QueryContextAttributes(com.sun.jna.platform.win32.Sspi.CtxtHandle, int, com.sun.jna.Structure) + * } function uses this structure.

+ */ + public static class SecPkgContext_Sizes extends Structure { + + public static class ByReference extends SecPkgContext_Sizes implements Structure.ByReference { + + } + + public static final List FIELDS = createFieldsOrder("cbMaxToken", "cbMaxSignature", "cbBlockSize", "cbSecurityTrailer"); + + /** + * Specifies the maximum size of the security token used in the authentication exchanges. + */ + public int cbMaxToken; + + /** + * Specifies the maximum size of the signature created by the MakeSignature function. This member must be zero if integrity services are not requested or available. + */ + public int cbMaxSignature; + + /** + * Specifies the preferred integral size of the messages. For example, eight indicates that messages should be of size zero mod eight for optimal performance. Messages other than this block size can be padded. + */ + public int cbBlockSize; + + /** + * Size of the security trailer to be appended to messages. This member should be zero if the relevant services are not requested or available. + */ + public int cbSecurityTrailer; + + public SecPkgContext_Sizes() { + super(W32APITypeMapper.DEFAULT); + } + + @Override + protected List getFieldOrder() { + return FIELDS; + } + + @Override + public String toString() { + return "SecPkgContext_Sizes{" + "cbMaxToken=" + cbMaxToken + + ", cbMaxSignature=" + cbMaxSignature + ", cbBlockSize=" + + cbBlockSize + ", cbSecurityTrailer=" + cbSecurityTrailer + + '}'; + } + } + + public static class SecPkgContext_SessionKey extends Structure { + + public static class ByReference extends SecPkgContext_SessionKey implements Structure.ByReference { + + } + + public static final List FIELDS = createFieldsOrder("SessionKeyLength", "SessionKey"); + + /** + * Size, in bytes, of the session key. + */ + public int SessionKeyLength; + + /** + * The session key for the security context. + */ + public Pointer SessionKey; + + public SecPkgContext_SessionKey() { + super(W32APITypeMapper.DEFAULT); + } + + @Override + protected List getFieldOrder() { + return FIELDS; + } + + public byte[] getSessionKey() { + if(SessionKey == null) { + return null; + } + return SessionKey.getByteArray(0, SessionKeyLength); + } + + public synchronized void free() { + if(SessionKey != null) { + Secur32.INSTANCE.FreeContextBuffer(SessionKey); + SessionKey = null; + } + } + } + + public static class SecPkgContext_KeyInfo extends Structure { + + public static class ByReference extends SecPkgContext_KeyInfo implements Structure.ByReference { + + } + + public static final List FIELDS = createFieldsOrder("sSignatureAlgorithmName", "sEncryptAlgorithmName","KeySize", "SignatureAlgorithm", "EncryptAlgorithm"); + + /** + * Name, if available, of the algorithm used for generating signatures, for example "MD5" or "SHA-2". + */ + public Pointer sSignatureAlgorithmName; + + /** + * Name, if available, of the algorithm used for encrypting messages. Reserved for future use. + */ + public Pointer sEncryptAlgorithmName; + + /** + * Specifies the effective key length, in bits, for the session key. This is typically 40, 56, or 128 bits. + */ + public int KeySize; + + /** + * Specifies the algorithm identifier (ALG_ID) used for generating signatures, if available. + */ + public int SignatureAlgorithm; + + /** + * Specifies the algorithm identifier (ALG_ID) used for encrypting messages. Reserved for future use. + */ + public int EncryptAlgorithm; + + public SecPkgContext_KeyInfo() { + super(W32APITypeMapper.DEFAULT); + } + + @Override + protected List getFieldOrder() { + return FIELDS; + } + + public synchronized String getSignatureAlgorithmName() { + if(sSignatureAlgorithmName == null) { + return null; + } + return Boolean.getBoolean("w32.ascii") ? sSignatureAlgorithmName.getString(0) : sSignatureAlgorithmName.getWideString(0); + } + + public synchronized String getEncryptAlgorithmName() { + if(sEncryptAlgorithmName == null) { + return null; + } + return Boolean.getBoolean("w32.ascii") ? sEncryptAlgorithmName.getString(0) : sEncryptAlgorithmName.getWideString(0); + } + + public synchronized void free() { + if(sSignatureAlgorithmName != null) { + Secur32.INSTANCE.FreeContextBuffer(sSignatureAlgorithmName); + sSignatureAlgorithmName = null; + } + if(sEncryptAlgorithmName != null) { + Secur32.INSTANCE.FreeContextBuffer(sEncryptAlgorithmName); + sEncryptAlgorithmName = null; + } + } + } + + public static class SecPkgContext_Lifespan extends Structure { + + public static class ByReference extends SecPkgContext_Lifespan implements Structure.ByReference { + + } + + public static final List FIELDS = createFieldsOrder("tsStart", "tsExpiry"); + + /** + * Time at which the context was established. + */ + public TimeStamp tsStart; + + /** + * Time at which the context will expire. + */ + public TimeStamp tsExpiry; + + public SecPkgContext_Lifespan() { + super(W32APITypeMapper.DEFAULT); + } + + @Override + protected List getFieldOrder() { + return FIELDS; + } + } + + public static class SecPkgContext_NegotiationInfo extends Structure { + + public static class ByReference extends SecPkgContext_NegotiationInfo implements Structure.ByReference { + + } + + public static final List FIELDS = createFieldsOrder("PackageInfo", "NegotiationState"); + + /** + * Time at which the context was established. + */ + public PSecPkgInfo PackageInfo; + + /** + * Time at which the context will expire. + */ + public int NegotiationState; + + public SecPkgContext_NegotiationInfo() { + super(W32APITypeMapper.DEFAULT); + } + + @Override + protected List getFieldOrder() { + return FIELDS; + } + + public synchronized void free() { + if(PackageInfo != null) { + Secur32.INSTANCE.FreeContextBuffer(PackageInfo.pPkgInfo.getPointer()); + PackageInfo = null; + } + } + } + + public static class SecPkgContext_Flags extends Structure { + + public static class ByReference extends SecPkgContext_Flags implements Structure.ByReference { + + } + + public static final List FIELDS = createFieldsOrder("Flags"); + + /** + * Flag values for the current security context. These values correspond + * to the flags negotiated by the InitializeSecurityContext (General) + * and AcceptSecurityContext (General) functions. + */ + public int Flags; + + public SecPkgContext_Flags() { + super(W32APITypeMapper.DEFAULT); + } + + @Override + protected List getFieldOrder() { + return FIELDS; + } + } + + /** + * Strings in structure {@link SEC_WINNT_AUTH_IDENTITY} are ANSI + */ + public static final int SEC_WINNT_AUTH_IDENTITY_ANSI = 0x1; + /** + * String in structure {@link SEC_WINNT_AUTH_IDENTITY} are UNICODE + */ + public static final int SEC_WINNT_AUTH_IDENTITY_UNICODE = 0x2; + + + public static class SEC_WINNT_AUTH_IDENTITY extends Structure { + + public static final List FIELDS = createFieldsOrder("User", "UserLength", "Domain", "DomainLength", "Password", "PasswordLength", "Flags"); + + /** + * A string that contains the user name. + */ + public String User; + + /** + * The length, in characters, of the user string, not including the + * terminating null character. + */ + public int UserLength; + + /** + * A string that contains the domain name or the workgroup name. + */ + public String Domain; + + /** + * The length, in characters, of the domain string, not including the + * terminating null character. + */ + public int DomainLength; + + /** + * A string that contains the password of the user in the domain or + * workgroup. When you have finished using the password, remove the + * sensitive information from memory by calling SecureZeroMemory. For + * more information about protecting the password, see Handling + * Passwords. + */ + public String Password; + + /** + * The length, in characters, of the password string, not including the + * terminating null character. + */ + public int PasswordLength; + + /** + * This member can be one of the following values. + * + * + * + * + * + *
ValueMeaning
SEC_WINNT_AUTH_IDENTITY_ANSIThe strings in this structure are in ANSI format.
SEC_WINNT_AUTH_IDENTITY_UNICODEThe strings in this structure are in Unicode format.
+ * + * As the string encoding is managed by JNA do not change this + * value! + */ + public int Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE; + + + /** + * Create a new SecBufferDesc with one SECBUFFER_EMPTY buffer. + */ + public SEC_WINNT_AUTH_IDENTITY() { + super(W32APITypeMapper.UNICODE); + } + + @Override + public void write() { + UserLength = User == null ? 0 : User.length(); + DomainLength = Domain == null ? 0 : Domain.length(); + PasswordLength = Password == null ? 0 : Password.length(); + super.write(); + } + + @Override + protected List getFieldOrder() { + return FIELDS; + } + } } diff --git a/contrib/platform/src/com/sun/jna/platform/win32/SspiUtil.java b/contrib/platform/src/com/sun/jna/platform/win32/SspiUtil.java new file mode 100644 index 0000000000..a89f98345f --- /dev/null +++ b/contrib/platform/src/com/sun/jna/platform/win32/SspiUtil.java @@ -0,0 +1,101 @@ +/* Copyright (c) 2010 Daniel Doubrovkine, All Rights Reserved + * + * The contents of this file is dual-licensed under 2 + * alternative Open Source/Free licenses: LGPL 2.1 or later and + * Apache License 2.0. (starting with JNA version 4.0.0). + * + * You can freely decide which license you want to apply to + * the project. + * + * You may obtain a copy of the LGPL License at: + * + * http://www.gnu.org/licenses/licenses.html + * + * A copy is also included in the downloadable source code package + * containing JNA, in file "LGPL2.1". + * + * You may obtain a copy of the Apache License at: + * + * http://www.apache.org/licenses/ + * + * A copy is also included in the downloadable source code package + * containing JNA, in file "AL2.0". + */ +package com.sun.jna.platform.win32; + +/** + * Utility classes and methods for Sspi + */ +public class SspiUtil { + /** + * The SecBufferDesc structure describes an array of SecBuffer structures + * to pass from a transport application to a security package. + * + *

+ * ManagedSecBufferDesc is a convenience binding, that makes dealing with + * {@link com.sun.jna.platform.win32.Sspi.SecBufferDesc SecBufferDesc} + * easier by providing direct, bound access, to the contained + * {@link com.sun.jna.platform.win32.Sspi.SecBuffer SecBuffer}s. + *

+ * + *

+ * ManagedSecBufferDesc assumes, that the size (entry count) of the + * SecBufferDesc is known at construction time. It is assumed, that this + * covers all relevant use-cases.

+ */ + public static class ManagedSecBufferDesc extends Sspi.SecBufferDesc { + + private final Sspi.SecBuffer[] secBuffers; + + /** + * Create a new SecBufferDesc with initial data. + * @param type Token type. + * @param token Initial token data. + */ + public ManagedSecBufferDesc(int type, byte[] token) { + secBuffers = new Sspi.SecBuffer[] { new Sspi.SecBuffer(type, token) }; + pBuffers = secBuffers[0].getPointer(); + cBuffers = secBuffers.length; + } + + /** + * Create a new SecBufferDesc with one SecBuffer of a given type and size. + * @param type type + * @param tokenSize token size + */ + public ManagedSecBufferDesc(int type, int tokenSize) { + secBuffers = new Sspi.SecBuffer[] { new Sspi.SecBuffer(type, tokenSize) }; + pBuffers = secBuffers[0].getPointer(); + cBuffers = secBuffers.length; + } + + public ManagedSecBufferDesc(int bufferCount) { + cBuffers = bufferCount; + secBuffers = (Sspi.SecBuffer[]) new Sspi.SecBuffer().toArray(bufferCount); + pBuffers = secBuffers[0].getPointer(); + cBuffers = secBuffers.length; + } + + public Sspi.SecBuffer getBuffer(int idx) { + return secBuffers[idx]; + } + + @Override + public void write() { + for(Sspi.SecBuffer sb: secBuffers) { + sb.write(); + } + writeField("ulVersion"); + writeField("pBuffers"); + writeField("cBuffers"); + } + + @Override + public void read() { + for (Sspi.SecBuffer sb : secBuffers) { + sb.read(); + } + } + + } +} diff --git a/contrib/platform/test/com/sun/jna/platform/win32/Secur32Test.java b/contrib/platform/test/com/sun/jna/platform/win32/Secur32Test.java index 44e5bdac26..2609b6c7d5 100644 --- a/contrib/platform/test/com/sun/jna/platform/win32/Secur32Test.java +++ b/contrib/platform/test/com/sun/jna/platform/win32/Secur32Test.java @@ -12,17 +12,22 @@ */ package com.sun.jna.platform.win32; +import com.sun.jna.Memory; import com.sun.jna.Native; +import com.sun.jna.platform.win32.SspiUtil.ManagedSecBufferDesc; import com.sun.jna.platform.win32.Sspi.CredHandle; import com.sun.jna.platform.win32.Sspi.CtxtHandle; import com.sun.jna.platform.win32.Sspi.PSecPkgInfo; -import com.sun.jna.platform.win32.Sspi.SecBufferDesc; import com.sun.jna.platform.win32.Sspi.SecPkgContext_PackageInfo; +import com.sun.jna.platform.win32.Sspi.SecPkgContext_Sizes; +import com.sun.jna.platform.win32.Sspi.SecPkgCredentials_Names; import com.sun.jna.platform.win32.Sspi.SecPkgInfo; import com.sun.jna.platform.win32.Sspi.SecPkgInfo.ByReference; import com.sun.jna.platform.win32.Sspi.TimeStamp; import com.sun.jna.platform.win32.WinNT.HANDLEByReference; import com.sun.jna.ptr.IntByReference; +import java.nio.charset.Charset; +import java.util.Arrays; import junit.framework.TestCase; @@ -66,7 +71,7 @@ public void testAcquireCredentialsHandleInvalidPackage() { null, "PackageDoesntExist", Sspi.SECPKG_CRED_OUTBOUND, null, null, null, null, phCredential, ptsExpiry)); } - + public void testInitializeSecurityContext() { CredHandle phCredential = new CredHandle(); TimeStamp ptsExpiry = new TimeStamp(); @@ -76,7 +81,7 @@ public void testInitializeSecurityContext() { null, phCredential, ptsExpiry)); // initialize security context CtxtHandle phNewContext = new CtxtHandle(); - SecBufferDesc pbToken = new SecBufferDesc(Sspi.SECBUFFER_TOKEN, Sspi.MAX_TOKEN_SIZE); + ManagedSecBufferDesc pbToken = new ManagedSecBufferDesc(Sspi.SECBUFFER_TOKEN, Sspi.MAX_TOKEN_SIZE); IntByReference pfContextAttr = new IntByReference(); int rc = Secur32.INSTANCE.InitializeSecurityContext(phCredential, null, Advapi32Util.getUserName(), Sspi.ISC_REQ_CONNECTION, 0, @@ -85,7 +90,7 @@ public void testInitializeSecurityContext() { assertTrue(rc == W32Errors.SEC_I_CONTINUE_NEEDED || rc == W32Errors.SEC_E_OK); assertTrue(phNewContext.dwLower != null); assertTrue(phNewContext.dwUpper != null); - assertTrue(pbToken.pBuffers[0].getBytes().length > 0); + assertTrue(pbToken.getBuffer(0).getBytes().length > 0); // release assertEquals(W32Errors.SEC_E_OK, Secur32.INSTANCE.DeleteSecurityContext( phNewContext)); @@ -111,18 +116,18 @@ public void testAcceptSecurityContext() { null, phServerCredential, ptsServerExpiry)); // server ----------- security context CtxtHandle phServerContext = new CtxtHandle(); - SecBufferDesc pbServerToken = null; + ManagedSecBufferDesc pbServerToken = null; IntByReference pfServerContextAttr = new IntByReference(); int clientRc = W32Errors.SEC_I_CONTINUE_NEEDED; int serverRc = W32Errors.SEC_I_CONTINUE_NEEDED; do { // client ----------- initialize security context, produce a client token // client token returned is always new - SecBufferDesc pbClientToken = new SecBufferDesc(Sspi.SECBUFFER_TOKEN, Sspi.MAX_TOKEN_SIZE); + ManagedSecBufferDesc pbClientToken = new ManagedSecBufferDesc(Sspi.SECBUFFER_TOKEN, Sspi.MAX_TOKEN_SIZE); if (clientRc == W32Errors.SEC_I_CONTINUE_NEEDED) { // server token is empty the first time - SecBufferDesc pbServerTokenCopy = pbServerToken == null - ? null : new SecBufferDesc(Sspi.SECBUFFER_TOKEN, pbServerToken.getBytes()); + ManagedSecBufferDesc pbServerTokenCopy = pbServerToken == null + ? null : new ManagedSecBufferDesc(Sspi.SECBUFFER_TOKEN, pbServerToken.getBuffer(0).getBytes()); clientRc = Secur32.INSTANCE.InitializeSecurityContext( phClientCredential, phClientContext.isNull() ? null : phClientContext, @@ -140,8 +145,8 @@ public void testAcceptSecurityContext() { } // server ----------- accept security context, produce a server token if (serverRc == W32Errors.SEC_I_CONTINUE_NEEDED) { - pbServerToken = new SecBufferDesc(Sspi.SECBUFFER_TOKEN, Sspi.MAX_TOKEN_SIZE); - SecBufferDesc pbClientTokenByValue = new SecBufferDesc(Sspi.SECBUFFER_TOKEN, pbClientToken.getBytes()); + pbServerToken = new ManagedSecBufferDesc(Sspi.SECBUFFER_TOKEN, Sspi.MAX_TOKEN_SIZE); + ManagedSecBufferDesc pbClientTokenByValue = new ManagedSecBufferDesc(Sspi.SECBUFFER_TOKEN, pbClientToken.getBuffer(0).getBytes()); serverRc = Secur32.INSTANCE.AcceptSecurityContext(phServerCredential, phServerContext.isNull() ? null : phServerContext, pbClientTokenByValue, @@ -184,18 +189,18 @@ public void testImpersonateRevertSecurityContext() { null, phServerCredential, ptsServerExpiry)); // server ----------- security context CtxtHandle phServerContext = new CtxtHandle(); - SecBufferDesc pbServerToken = null; + ManagedSecBufferDesc pbServerToken = null; IntByReference pfServerContextAttr = new IntByReference(); int clientRc = W32Errors.SEC_I_CONTINUE_NEEDED; int serverRc = W32Errors.SEC_I_CONTINUE_NEEDED; do { // client ----------- initialize security context, produce a client token // client token returned is always new - SecBufferDesc pbClientToken = new SecBufferDesc(Sspi.SECBUFFER_TOKEN, Sspi.MAX_TOKEN_SIZE); + ManagedSecBufferDesc pbClientToken = new ManagedSecBufferDesc(Sspi.SECBUFFER_TOKEN, Sspi.MAX_TOKEN_SIZE); if (clientRc == W32Errors.SEC_I_CONTINUE_NEEDED) { // server token is empty the first time - SecBufferDesc pbServerTokenCopy = pbServerToken == null - ? null : new SecBufferDesc(Sspi.SECBUFFER_TOKEN, pbServerToken.getBytes()); + ManagedSecBufferDesc pbServerTokenCopy = pbServerToken == null + ? null : new ManagedSecBufferDesc(Sspi.SECBUFFER_TOKEN, pbServerToken.getBuffer(0).getBytes()); clientRc = Secur32.INSTANCE.InitializeSecurityContext( phClientCredential, phClientContext.isNull() ? null : phClientContext, @@ -213,8 +218,8 @@ public void testImpersonateRevertSecurityContext() { } // server ----------- accept security context, produce a server token if (serverRc == W32Errors.SEC_I_CONTINUE_NEEDED) { - pbServerToken = new SecBufferDesc(Sspi.SECBUFFER_TOKEN, Sspi.MAX_TOKEN_SIZE); - SecBufferDesc pbClientTokenByValue = new SecBufferDesc(Sspi.SECBUFFER_TOKEN, pbClientToken.getBytes()); + pbServerToken = new ManagedSecBufferDesc(Sspi.SECBUFFER_TOKEN, Sspi.MAX_TOKEN_SIZE); + ManagedSecBufferDesc pbClientTokenByValue = new ManagedSecBufferDesc(Sspi.SECBUFFER_TOKEN, pbClientToken.getBuffer(0).getBytes()); serverRc = Secur32.INSTANCE.AcceptSecurityContext(phServerCredential, phServerContext.isNull() ? null : phServerContext, pbClientTokenByValue, @@ -278,13 +283,13 @@ public void testQuerySecurityContextToken() { null, phServerCredential, ptsServerExpiry)); // server ----------- security context CtxtHandle phServerContext = new CtxtHandle(); - SecBufferDesc pbServerToken = new SecBufferDesc(Sspi.SECBUFFER_TOKEN, Sspi.MAX_TOKEN_SIZE); + ManagedSecBufferDesc pbServerToken = new ManagedSecBufferDesc(Sspi.SECBUFFER_TOKEN, Sspi.MAX_TOKEN_SIZE); IntByReference pfServerContextAttr = new IntByReference(); int clientRc = W32Errors.SEC_I_CONTINUE_NEEDED; int serverRc = W32Errors.SEC_I_CONTINUE_NEEDED; do { // client token returned is always new - SecBufferDesc pbClientToken = new SecBufferDesc(Sspi.SECBUFFER_TOKEN, Sspi.MAX_TOKEN_SIZE); + ManagedSecBufferDesc pbClientToken = new ManagedSecBufferDesc(Sspi.SECBUFFER_TOKEN, Sspi.MAX_TOKEN_SIZE); // client ----------- initialize security context, produce a client token if (clientRc == W32Errors.SEC_I_CONTINUE_NEEDED) { // server token is empty the first time @@ -334,58 +339,75 @@ public void testQuerySecurityContextToken() { assertEquals(W32Errors.SEC_E_OK, Secur32.INSTANCE.FreeCredentialsHandle( phClientCredential)); } - + public void testCreateEmptyToken() { - SecBufferDesc token = new SecBufferDesc(Sspi.SECBUFFER_TOKEN, Sspi.MAX_TOKEN_SIZE); - assertEquals(1, token.pBuffers.length); + ManagedSecBufferDesc token = new ManagedSecBufferDesc(Sspi.SECBUFFER_TOKEN, Sspi.MAX_TOKEN_SIZE); assertEquals(1, token.cBuffers); - assertEquals(Sspi.SECBUFFER_TOKEN, token.pBuffers[0].BufferType); - assertEquals(Sspi.MAX_TOKEN_SIZE, token.pBuffers[0].cbBuffer); - assertEquals(token.getBytes().length, token.pBuffers[0].getBytes().length); + assertEquals(Sspi.SECBUFFER_TOKEN, token.getBuffer(0).BufferType); + assertEquals(Sspi.MAX_TOKEN_SIZE, token.getBuffer(0).cbBuffer); } public void testQueryContextAttributes() { - // client ----------- acquire outbound credential handle - CredHandle phClientCredential = new CredHandle(); - TimeStamp ptsClientExpiry = new TimeStamp(); - assertEquals(W32Errors.SEC_E_OK, Secur32.INSTANCE.AcquireCredentialsHandle(null, "Negotiate", - Sspi.SECPKG_CRED_OUTBOUND, null, null, null, null, phClientCredential, ptsClientExpiry)); - // client ----------- security context - CtxtHandle phClientContext = new CtxtHandle(); - IntByReference pfClientContextAttr = new IntByReference(); - // server ----------- acquire inbound credential handle - CredHandle phServerCredential = new CredHandle(); - TimeStamp ptsServerExpiry = new TimeStamp(); - assertEquals(W32Errors.SEC_E_OK, Secur32.INSTANCE.AcquireCredentialsHandle(null, "Negotiate", - Sspi.SECPKG_CRED_INBOUND, null, null, null, null, phServerCredential, ptsServerExpiry)); - // server ----------- security context - CtxtHandle phServerContext = new CtxtHandle(); - SecBufferDesc pbServerToken = new SecBufferDesc(Sspi.SECBUFFER_TOKEN, Sspi.MAX_TOKEN_SIZE); - IntByReference pfServerContextAttr = new IntByReference(); - int clientRc = W32Errors.SEC_I_CONTINUE_NEEDED; - int serverRc = W32Errors.SEC_I_CONTINUE_NEEDED; - do { - // client token returned is always new - SecBufferDesc pbClientToken = new SecBufferDesc(Sspi.SECBUFFER_TOKEN, Sspi.MAX_TOKEN_SIZE); - // client ----------- initialize security context, produce a client - // token - if (clientRc == W32Errors.SEC_I_CONTINUE_NEEDED) { - // server token is empty the first time - clientRc = Secur32.INSTANCE.InitializeSecurityContext(phClientCredential, - phClientContext.isNull() ? null : phClientContext, Advapi32Util.getUserName(), - Sspi.ISC_REQ_CONNECTION, 0, Sspi.SECURITY_NATIVE_DREP, pbServerToken, 0, phClientContext, - pbClientToken, pfClientContextAttr, null); - assertTrue(clientRc == W32Errors.SEC_I_CONTINUE_NEEDED || clientRc == W32Errors.SEC_E_OK); - } - // server ----------- accept security context, produce a server - // token - if (serverRc == W32Errors.SEC_I_CONTINUE_NEEDED) { - serverRc = Secur32.INSTANCE.AcceptSecurityContext(phServerCredential, phServerContext.isNull() ? null - : phServerContext, pbClientToken, Sspi.ISC_REQ_CONNECTION, Sspi.SECURITY_NATIVE_DREP, - phServerContext, pbServerToken, pfServerContextAttr, ptsServerExpiry); - assertTrue(serverRc == W32Errors.SEC_I_CONTINUE_NEEDED || serverRc == W32Errors.SEC_E_OK); - } - } while (serverRc != W32Errors.SEC_E_OK || clientRc != W32Errors.SEC_E_OK); + // client ----------- acquire outbound credential handle + CredHandle phClientCredential = new CredHandle(); + TimeStamp ptsClientExpiry = new TimeStamp(); + assertEquals(W32Errors.SEC_E_OK, Secur32.INSTANCE.AcquireCredentialsHandle( + null, "Negotiate", Sspi.SECPKG_CRED_OUTBOUND, null, null, null, + null, phClientCredential, ptsClientExpiry)); + // client ----------- security context + CtxtHandle phClientContext = new CtxtHandle(); + IntByReference pfClientContextAttr = new IntByReference(); + // server ----------- acquire inbound credential handle + CredHandle phServerCredential = new CredHandle(); + TimeStamp ptsServerExpiry = new TimeStamp(); + assertEquals(W32Errors.SEC_E_OK, Secur32.INSTANCE.AcquireCredentialsHandle( + null, "Negotiate", Sspi.SECPKG_CRED_INBOUND, null, null, null, + null, phServerCredential, ptsServerExpiry)); + // server ----------- security context + CtxtHandle phServerContext = new CtxtHandle(); + ManagedSecBufferDesc pbServerToken = null; + IntByReference pfServerContextAttr = new IntByReference(); + int clientRc = W32Errors.SEC_I_CONTINUE_NEEDED; + int serverRc = W32Errors.SEC_I_CONTINUE_NEEDED; + do { + // client ----------- initialize security context, produce a client token + // client token returned is always new + ManagedSecBufferDesc pbClientToken = new ManagedSecBufferDesc(Sspi.SECBUFFER_TOKEN, Sspi.MAX_TOKEN_SIZE); + if (clientRc == W32Errors.SEC_I_CONTINUE_NEEDED) { + // server token is empty the first time + ManagedSecBufferDesc pbServerTokenCopy = pbServerToken == null + ? null : new ManagedSecBufferDesc(Sspi.SECBUFFER_TOKEN, pbServerToken.getBuffer(0).getBytes()); + clientRc = Secur32.INSTANCE.InitializeSecurityContext( + phClientCredential, + phClientContext.isNull() ? null : phClientContext, + Advapi32Util.getUserName(), + Sspi.ISC_REQ_CONNECTION, + 0, + Sspi.SECURITY_NATIVE_DREP, + pbServerTokenCopy, + 0, + phClientContext, + pbClientToken, + pfClientContextAttr, + null); + assertTrue(clientRc == W32Errors.SEC_I_CONTINUE_NEEDED || clientRc == W32Errors.SEC_E_OK); + } + // server ----------- accept security context, produce a server token + if (serverRc == W32Errors.SEC_I_CONTINUE_NEEDED) { + pbServerToken = new ManagedSecBufferDesc(Sspi.SECBUFFER_TOKEN, Sspi.MAX_TOKEN_SIZE); + ManagedSecBufferDesc pbClientTokenByValue = new ManagedSecBufferDesc(Sspi.SECBUFFER_TOKEN, pbClientToken.getBuffer(0).getBytes()); + serverRc = Secur32.INSTANCE.AcceptSecurityContext(phServerCredential, + phServerContext.isNull() ? null : phServerContext, + pbClientTokenByValue, + Sspi.ISC_REQ_CONNECTION, + Sspi.SECURITY_NATIVE_DREP, + phServerContext, + pbServerToken, + pfServerContextAttr, + ptsServerExpiry); + assertTrue(serverRc == W32Errors.SEC_I_CONTINUE_NEEDED || serverRc == W32Errors.SEC_E_OK); + } + } while(serverRc != W32Errors.SEC_E_OK || clientRc != W32Errors.SEC_E_OK); // query context attributes SecPkgContext_PackageInfo packageinfo = new SecPkgContext_PackageInfo(); assertEquals(W32Errors.SEC_E_OK, @@ -407,4 +429,336 @@ public void testQueryContextAttributes() { assertEquals(W32Errors.SEC_E_OK, Secur32.INSTANCE.DeleteSecurityContext(phClientContext)); assertEquals(W32Errors.SEC_E_OK, Secur32.INSTANCE.FreeCredentialsHandle(phClientCredential)); } + + public void testQuerySecurityPackageInfo() { + PSecPkgInfo pkgInfo = new PSecPkgInfo(); + assertEquals(W32Errors.SEC_E_OK, Secur32.INSTANCE.QuerySecurityPackageInfo("NTLM", pkgInfo)); + assertEquals(pkgInfo.pPkgInfo.Name, "NTLM"); + assertEquals(pkgInfo.pPkgInfo.fCapabilities & Sspi.SECPKG_FLAG_PRIVACY, Sspi.SECPKG_FLAG_PRIVACY); + assertEquals(W32Errors.SEC_E_OK, Secur32.INSTANCE.FreeContextBuffer(pkgInfo.getPointer())); + } + + public void testQueryCredentialAttribute() { + // acquire sample credential handle + CredHandle phClientCredential = new CredHandle(); + TimeStamp ptsClientExpiry = new TimeStamp(); + assertEquals(W32Errors.SEC_E_OK, Secur32.INSTANCE.AcquireCredentialsHandle(null, "Negotiate", + Sspi.SECPKG_CRED_OUTBOUND, null, null, null, null, phClientCredential, ptsClientExpiry)); + + SecPkgCredentials_Names names = new SecPkgCredentials_Names(); + assertEquals(W32Errors.SEC_E_OK, Secur32.INSTANCE.QueryCredentialsAttributes(phClientCredential, Sspi.SECPKG_CRED_ATTR_NAMES, names)); + + String accountName = names.getUserName(); + + assertNotNull(accountName); + assertTrue(accountName.length() > 0); + + assertEquals(W32Errors.SEC_E_OK, names.free()); + } + + public void testEncryptDecryptMessage() { + // client ----------- acquire outbound credential handle + CredHandle phClientCredential = new CredHandle(); + TimeStamp ptsClientExpiry = new TimeStamp(); + assertEquals(W32Errors.SEC_E_OK, Secur32.INSTANCE.AcquireCredentialsHandle( + null, "Negotiate", Sspi.SECPKG_CRED_OUTBOUND, null, null, null, + null, phClientCredential, ptsClientExpiry)); + // client ----------- security context + CtxtHandle phClientContext = new CtxtHandle(); + IntByReference pfClientContextAttr = new IntByReference(); + // server ----------- acquire inbound credential handle + CredHandle phServerCredential = new CredHandle(); + TimeStamp ptsServerExpiry = new TimeStamp(); + assertEquals(W32Errors.SEC_E_OK, Secur32.INSTANCE.AcquireCredentialsHandle( + null, "Negotiate", Sspi.SECPKG_CRED_INBOUND, null, null, null, + null, phServerCredential, ptsServerExpiry)); + // server ----------- security context + CtxtHandle phServerContext = new CtxtHandle(); + ManagedSecBufferDesc pbServerToken = null; + IntByReference pfServerContextAttr = new IntByReference(); + int clientRc = W32Errors.SEC_I_CONTINUE_NEEDED; + int serverRc = W32Errors.SEC_I_CONTINUE_NEEDED; + do { + // client ----------- initialize security context, produce a client token + // client token returned is always new + ManagedSecBufferDesc pbClientToken = new ManagedSecBufferDesc(Sspi.SECBUFFER_TOKEN, Sspi.MAX_TOKEN_SIZE); + if (clientRc == W32Errors.SEC_I_CONTINUE_NEEDED) { + // server token is empty the first time + ManagedSecBufferDesc pbServerTokenCopy = pbServerToken == null + ? null : new ManagedSecBufferDesc(Sspi.SECBUFFER_TOKEN, pbServerToken.getBuffer(0).getBytes()); + clientRc = Secur32.INSTANCE.InitializeSecurityContext( + phClientCredential, + phClientContext.isNull() ? null : phClientContext, + Advapi32Util.getUserName(), + Sspi.ISC_REQ_CONNECTION | Sspi.ISC_REQ_CONFIDENTIALITY, + 0, + Sspi.SECURITY_NATIVE_DREP, + pbServerTokenCopy, + 0, + phClientContext, + pbClientToken, + pfClientContextAttr, + null); + assertTrue(clientRc == W32Errors.SEC_I_CONTINUE_NEEDED || clientRc == W32Errors.SEC_E_OK); + } + // server ----------- accept security context, produce a server token + if (serverRc == W32Errors.SEC_I_CONTINUE_NEEDED) { + pbServerToken = new ManagedSecBufferDesc(Sspi.SECBUFFER_TOKEN, Sspi.MAX_TOKEN_SIZE); + ManagedSecBufferDesc pbClientTokenByValue = new ManagedSecBufferDesc(Sspi.SECBUFFER_TOKEN, pbClientToken.getBuffer(0).getBytes()); + serverRc = Secur32.INSTANCE.AcceptSecurityContext(phServerCredential, + phServerContext.isNull() ? null : phServerContext, + pbClientTokenByValue, + Sspi.ISC_REQ_CONNECTION | Sspi.ISC_REQ_CONFIDENTIALITY, + Sspi.SECURITY_NATIVE_DREP, + phServerContext, + pbServerToken, + pfServerContextAttr, + ptsServerExpiry); + assertTrue(serverRc == W32Errors.SEC_I_CONTINUE_NEEDED || serverRc == W32Errors.SEC_E_OK); + } + } while(serverRc != W32Errors.SEC_E_OK || clientRc != W32Errors.SEC_E_OK); + + assertTrue((pfServerContextAttr.getValue() & Sspi.ISC_REQ_CONFIDENTIALITY) == Sspi.ISC_REQ_CONFIDENTIALITY); + assertTrue((pfClientContextAttr.getValue() & Sspi.ISC_REQ_CONFIDENTIALITY) == Sspi.ISC_REQ_CONFIDENTIALITY); + + // Fetch size limits for crypto functions + SecPkgContext_Sizes sizes = new SecPkgContext_Sizes(); + assertEquals(W32Errors.SEC_E_OK, Secur32.INSTANCE.QueryContextAttributes(phClientContext, Sspi.SECPKG_ATTR_SIZES, sizes)); + + // Create sample input data + byte[] inputData = "Hallo Welt".getBytes(Charset.forName("ASCII")); + + // Do encryption, buffer 0 holds meta data, buffer 1 holds the + // clear text data on input and the encrypted data on output. + // Uses the phClientContext + ManagedSecBufferDesc encryptBuffers = new ManagedSecBufferDesc(2); + + Memory tokenMemory = new Memory(sizes.cbSecurityTrailer); + Memory dataMemory = new Memory(inputData.length); + dataMemory.write(0, inputData, 0, inputData.length); + + encryptBuffers.getBuffer(0).BufferType = Sspi.SECBUFFER_TOKEN; + encryptBuffers.getBuffer(0).cbBuffer = (int) tokenMemory.size(); + encryptBuffers.getBuffer(0).pvBuffer = tokenMemory; + encryptBuffers.getBuffer(1).BufferType = Sspi.SECBUFFER_DATA; + encryptBuffers.getBuffer(1).cbBuffer = (int) dataMemory.size(); + encryptBuffers.getBuffer(1).pvBuffer = dataMemory; + + assertEquals(W32Errors.SEC_E_OK, Secur32.INSTANCE.EncryptMessage(phClientContext, 0, encryptBuffers, 0)); + + byte[] encryptedTokenData = encryptBuffers.getBuffer(0).getBytes(); + byte[] encryptedData = encryptBuffers.getBuffer(1).getBytes(); + + assertNotNull(encryptedTokenData); + assertNotNull(encryptedData); + assertTrue(encryptedTokenData.length > 0); + assertTrue(encryptedData.length > 0); + assertFalse(Arrays.equals(inputData, encryptedData)); + + // Do decryption of data with the pfServerContextAttr + ManagedSecBufferDesc decryptBuffers = new ManagedSecBufferDesc(2); + + Memory decryptTokenMemory = new Memory(encryptedTokenData.length); + decryptTokenMemory.write(0, encryptedTokenData, 0, encryptedTokenData.length); + Memory decryptDataMemory = new Memory(encryptedData.length); + decryptDataMemory.write(0, encryptedData, 0, encryptedData.length); + + decryptBuffers.getBuffer(0).BufferType = Sspi.SECBUFFER_TOKEN; + decryptBuffers.getBuffer(0).cbBuffer = (int) decryptTokenMemory.size(); + decryptBuffers.getBuffer(0).pvBuffer = decryptTokenMemory; + decryptBuffers.getBuffer(1).BufferType = Sspi.SECBUFFER_DATA; + decryptBuffers.getBuffer(1).cbBuffer = (int) decryptDataMemory.size(); + decryptBuffers.getBuffer(1).pvBuffer = decryptDataMemory; + + IntByReference qosResult = new IntByReference(); + assertEquals(W32Errors.SEC_E_OK, Secur32.INSTANCE.DecryptMessage(phServerContext, decryptBuffers, 0, qosResult)); + + byte[] decryptMessageResult = decryptBuffers.getBuffer(1).getBytes(); + assertTrue(Arrays.equals(inputData, decryptMessageResult)); + + // Modify message and retry decryption. Decryption is expected to be + // refused and the buffers should be untouched. + // Modification is done by injecting a NULL byte into the beginning of + // the message + + ManagedSecBufferDesc decryptBuffers2 = new ManagedSecBufferDesc(2); + + Memory decryptTokenMemory2 = new Memory(encryptedTokenData.length); + decryptTokenMemory2.write(0, encryptedTokenData, 0, encryptedTokenData.length); + Memory decryptDataMemory2 = new Memory(encryptedData.length + 1); + decryptDataMemory2.write(1, encryptedData, 0, encryptedData.length); + + decryptBuffers2.getBuffer(0).BufferType = Sspi.SECBUFFER_TOKEN; + decryptBuffers2.getBuffer(0).cbBuffer = (int) decryptTokenMemory2.size(); + decryptBuffers2.getBuffer(0).pvBuffer = decryptTokenMemory2; + decryptBuffers2.getBuffer(1).BufferType = Sspi.SECBUFFER_DATA; + decryptBuffers2.getBuffer(1).cbBuffer = (int) decryptDataMemory2.size(); + decryptBuffers2.getBuffer(1).pvBuffer = decryptDataMemory2; + + assertEquals(W32Errors.SEC_E_MESSAGE_ALTERED, Secur32.INSTANCE.DecryptMessage(phServerContext, decryptBuffers2, 0, qosResult)); + + // release server context + assertEquals(W32Errors.SEC_E_OK, Secur32.INSTANCE.DeleteSecurityContext( + phServerContext)); + assertEquals(W32Errors.SEC_E_OK, Secur32.INSTANCE.FreeCredentialsHandle( + phServerCredential)); + // release client context + assertEquals(W32Errors.SEC_E_OK, Secur32.INSTANCE.DeleteSecurityContext( + phClientContext)); + assertEquals(W32Errors.SEC_E_OK, Secur32.INSTANCE.FreeCredentialsHandle( + phClientCredential)); + } + + public void testMakeVerifySignature() { + // client ----------- acquire outbound credential handle + CredHandle phClientCredential = new CredHandle(); + TimeStamp ptsClientExpiry = new TimeStamp(); + assertEquals(W32Errors.SEC_E_OK, Secur32.INSTANCE.AcquireCredentialsHandle( + null, "Negotiate", Sspi.SECPKG_CRED_OUTBOUND, null, null, null, + null, phClientCredential, ptsClientExpiry)); + // client ----------- security context + CtxtHandle phClientContext = new CtxtHandle(); + IntByReference pfClientContextAttr = new IntByReference(); + // server ----------- acquire inbound credential handle + CredHandle phServerCredential = new CredHandle(); + TimeStamp ptsServerExpiry = new TimeStamp(); + assertEquals(W32Errors.SEC_E_OK, Secur32.INSTANCE.AcquireCredentialsHandle( + null, "Negotiate", Sspi.SECPKG_CRED_INBOUND, null, null, null, + null, phServerCredential, ptsServerExpiry)); + // server ----------- security context + CtxtHandle phServerContext = new CtxtHandle(); + ManagedSecBufferDesc pbServerToken = null; + IntByReference pfServerContextAttr = new IntByReference(); + int clientRc = W32Errors.SEC_I_CONTINUE_NEEDED; + int serverRc = W32Errors.SEC_I_CONTINUE_NEEDED; + do { + // client ----------- initialize security context, produce a client token + // client token returned is always new + ManagedSecBufferDesc pbClientToken = new ManagedSecBufferDesc(Sspi.SECBUFFER_TOKEN, Sspi.MAX_TOKEN_SIZE); + if (clientRc == W32Errors.SEC_I_CONTINUE_NEEDED) { + // server token is empty the first time + ManagedSecBufferDesc pbServerTokenCopy = pbServerToken == null + ? null : new ManagedSecBufferDesc(Sspi.SECBUFFER_TOKEN, pbServerToken.getBuffer(0).getBytes()); + clientRc = Secur32.INSTANCE.InitializeSecurityContext( + phClientCredential, + phClientContext.isNull() ? null : phClientContext, + Advapi32Util.getUserName(), + Sspi.ISC_REQ_CONNECTION | Sspi.ISC_REQ_CONFIDENTIALITY, + 0, + Sspi.SECURITY_NATIVE_DREP, + pbServerTokenCopy, + 0, + phClientContext, + pbClientToken, + pfClientContextAttr, + null); + assertTrue(clientRc == W32Errors.SEC_I_CONTINUE_NEEDED || clientRc == W32Errors.SEC_E_OK); + } + // server ----------- accept security context, produce a server token + if (serverRc == W32Errors.SEC_I_CONTINUE_NEEDED) { + pbServerToken = new ManagedSecBufferDesc(Sspi.SECBUFFER_TOKEN, Sspi.MAX_TOKEN_SIZE); + ManagedSecBufferDesc pbClientTokenByValue = new ManagedSecBufferDesc(Sspi.SECBUFFER_TOKEN, pbClientToken.getBuffer(0).getBytes()); + serverRc = Secur32.INSTANCE.AcceptSecurityContext(phServerCredential, + phServerContext.isNull() ? null : phServerContext, + pbClientTokenByValue, + Sspi.ISC_REQ_CONNECTION | Sspi.ISC_REQ_CONFIDENTIALITY, + Sspi.SECURITY_NATIVE_DREP, + phServerContext, + pbServerToken, + pfServerContextAttr, + ptsServerExpiry); + assertTrue(serverRc == W32Errors.SEC_I_CONTINUE_NEEDED || serverRc == W32Errors.SEC_E_OK); + } + } while(serverRc != W32Errors.SEC_E_OK || clientRc != W32Errors.SEC_E_OK); + + assertTrue((pfServerContextAttr.getValue() & Sspi.ISC_REQ_CONFIDENTIALITY) == Sspi.ISC_REQ_CONFIDENTIALITY); + assertTrue((pfClientContextAttr.getValue() & Sspi.ISC_REQ_CONFIDENTIALITY) == Sspi.ISC_REQ_CONFIDENTIALITY); + + // Fetch size limits for crypto functions + SecPkgContext_Sizes sizes = new SecPkgContext_Sizes(); + assertEquals(W32Errors.SEC_E_OK, Secur32.INSTANCE.QueryContextAttributes(phClientContext, Sspi.SECPKG_ATTR_SIZES, sizes)); + + // Create sample input data + byte[] inputData = "Hallo Welt".getBytes(Charset.forName("ASCII")); + + // Make signature, buffer 0 holds signature data, buffer 1 holds the + // clear text data + ManagedSecBufferDesc signingBuffers = new ManagedSecBufferDesc(2); + + Memory tokenMemory = new Memory(sizes.cbMaxSignature); + Memory dataMemory = new Memory(inputData.length); + dataMemory.write(0, inputData, 0, inputData.length); + + signingBuffers.getBuffer(0).BufferType = Sspi.SECBUFFER_TOKEN; + signingBuffers.getBuffer(0).cbBuffer = (int) tokenMemory.size(); + signingBuffers.getBuffer(0).pvBuffer = tokenMemory; + signingBuffers.getBuffer(1).BufferType = Sspi.SECBUFFER_DATA; + signingBuffers.getBuffer(1).cbBuffer = (int) dataMemory.size(); + signingBuffers.getBuffer(1).pvBuffer = dataMemory; + + assertEquals(W32Errors.SEC_E_OK, Secur32.INSTANCE.MakeSignature(phClientContext, 0, signingBuffers, 0)); + + byte[] signingData = signingBuffers.getBuffer(0).getBytes(); + byte[] signedData = signingBuffers.getBuffer(1).getBytes(); + + assertNotNull(signingData); + assertNotNull(signedData); + assertTrue(signingData.length > 0); + assertTrue(signedData.length > 0); + assertTrue(Arrays.equals(inputData, signedData)); + + // Do verification of data with the pfServerContextAttr + ManagedSecBufferDesc verificationBuffers = new ManagedSecBufferDesc(2); + + Memory verificationSigningMemory = new Memory(signingData.length); + verificationSigningMemory.write(0, signingData, 0, signingData.length); + Memory verificiationSignedMemory = new Memory(signedData.length); + verificiationSignedMemory.write(0, signedData, 0, signedData.length); + + verificationBuffers.getBuffer(0).BufferType = Sspi.SECBUFFER_TOKEN; + verificationBuffers.getBuffer(0).cbBuffer = (int) verificationSigningMemory.size(); + verificationBuffers.getBuffer(0).pvBuffer = verificationSigningMemory; + verificationBuffers.getBuffer(1).BufferType = Sspi.SECBUFFER_DATA; + verificationBuffers.getBuffer(1).cbBuffer = (int) verificiationSignedMemory.size(); + verificationBuffers.getBuffer(1).pvBuffer = verificiationSignedMemory; + + IntByReference qosResult = new IntByReference(); + assertEquals(W32Errors.SEC_E_OK, Secur32.INSTANCE.VerifySignature(phServerContext, verificationBuffers, 0, qosResult)); + + byte[] decryptMessageResult = verificationBuffers.getBuffer(1).getBytes(); + assertTrue(Arrays.equals(inputData, decryptMessageResult)); + + // Modify message and retry decryption. Decryption is expected to be + // refused and the buffers should be untouched. + // Modification is done by injecting a NULL byte into the beginning of + // the message + + ManagedSecBufferDesc verificationBuffers2 = new ManagedSecBufferDesc(2); + + Memory verificationSigingMemory2 = new Memory(signingData.length); + verificationSigingMemory2.write(0, signingData, 0, signingData.length); + Memory verificationSignedMemory2 = new Memory(signedData.length + 1); + verificationSignedMemory2.write(1, signedData, 0, signedData.length); + + verificationBuffers2.getBuffer(0).BufferType = Sspi.SECBUFFER_TOKEN; + verificationBuffers2.getBuffer(0).cbBuffer = (int) verificationSigingMemory2.size(); + verificationBuffers2.getBuffer(0).pvBuffer = verificationSigingMemory2; + verificationBuffers2.getBuffer(1).BufferType = Sspi.SECBUFFER_DATA; + verificationBuffers2.getBuffer(1).cbBuffer = (int) verificationSignedMemory2.size(); + verificationBuffers2.getBuffer(1).pvBuffer = verificationSignedMemory2; + + qosResult = new IntByReference(); + assertEquals(W32Errors.SEC_E_MESSAGE_ALTERED, Secur32.INSTANCE.VerifySignature(phServerContext, verificationBuffers2, 0, qosResult)); + + // release server context + assertEquals(W32Errors.SEC_E_OK, Secur32.INSTANCE.DeleteSecurityContext( + phServerContext)); + assertEquals(W32Errors.SEC_E_OK, Secur32.INSTANCE.FreeCredentialsHandle( + phServerCredential)); + // release client context + assertEquals(W32Errors.SEC_E_OK, Secur32.INSTANCE.DeleteSecurityContext( + phClientContext)); + assertEquals(W32Errors.SEC_E_OK, Secur32.INSTANCE.FreeCredentialsHandle( + phClientCredential)); + } } diff --git a/contrib/platform/test/com/sun/jna/platform/win32/SspiTest.java b/contrib/platform/test/com/sun/jna/platform/win32/SspiTest.java new file mode 100644 index 0000000000..579d7d5e4c --- /dev/null +++ b/contrib/platform/test/com/sun/jna/platform/win32/SspiTest.java @@ -0,0 +1,27 @@ + +package com.sun.jna.platform.win32; + +import com.sun.jna.platform.win32.Sspi.SEC_WINNT_AUTH_IDENTITY; +import junit.framework.TestCase; + +public class SspiTest extends TestCase { + + public static void main(String[] args) { + junit.textui.TestRunner.run(SspiTest.class); + } + + public void testSecWinntAuthIdentity() { + String username = "sample Username"; + String password = ""; // empty string + String domain = "German Umlauts: \u00c4, \u00f6, \u00dc"; + SEC_WINNT_AUTH_IDENTITY identity = new SEC_WINNT_AUTH_IDENTITY(); + identity.User = username; + identity.Password = password; + identity.Domain = domain; + identity.write(); + assertEquals(username.length(), identity.UserLength); + assertEquals(password.length(), identity.PasswordLength); + assertEquals(domain.length(), identity.DomainLength); + assertEquals(Sspi.SEC_WINNT_AUTH_IDENTITY_UNICODE, identity.Flags); + } +}