3#include <winpr/winpr.h>
5static const char* TEST_USER =
"Username";
6static const char* TEST_DOMAIN =
"Domain";
8static SECURITY_STATUS run_client_exchange(SecurityFunctionTable* table, ULONG cbMaxToken,
9 const char* password, UINT32 extra_identity_flags)
11 SECURITY_STATUS status = SEC_E_INTERNAL_ERROR;
16 TimeStamp expiration = WINPR_C_ARRAY_INIT;
17 SEC_WINNT_AUTH_IDENTITY identity = WINPR_C_ARRAY_INIT;
20 SecBufferDesc clientOutDesc = { SECBUFFER_VERSION, 1, &clientOut };
21 SecBufferDesc serverOutDesc = { SECBUFFER_VERSION, 1, &serverOut };
23 SecBufferDesc serverInDesc = { SECBUFFER_VERSION, 1, &clientOut };
24 SecBufferDesc clientInDesc = { SECBUFFER_VERSION, 1, &serverOut };
26 const ULONG fContextReq =
27 ISC_REQ_MUTUAL_AUTH | ISC_REQ_CONFIDENTIALITY | ISC_REQ_USE_SESSION_KEY;
28 const ULONG fAcceptReq = ASC_REQ_MUTUAL_AUTH | ASC_REQ_CONFIDENTIALITY | ASC_REQ_CONNECTION;
30 SecInvalidateHandle(&clientCtx);
31 SecInvalidateHandle(&serverCtx);
33 if (sspi_SetAuthIdentity(&identity, TEST_USER, TEST_DOMAIN, password) < 0)
36 identity.Flags |= extra_identity_flags;
39 table->AcquireCredentialsHandle(
nullptr, NTLM_SSP_NAME, SECPKG_CRED_OUTBOUND,
nullptr,
40 &identity,
nullptr,
nullptr, &clientCreds, &expiration);
41 if (status != SEC_E_OK)
44 status = table->AcquireCredentialsHandle(
nullptr, NTLM_SSP_NAME, SECPKG_CRED_INBOUND,
nullptr,
45 nullptr,
nullptr,
nullptr, &serverCreds, &expiration);
46 if (status != SEC_E_OK)
50 clientOut.BufferType = SECBUFFER_TOKEN;
51 clientOut.cbBuffer = cbMaxToken;
52 clientOut.pvBuffer = malloc(cbMaxToken);
53 if (!clientOut.pvBuffer)
55 status = SEC_E_INSUFFICIENT_MEMORY;
59 status = table->InitializeSecurityContext(&clientCreds,
nullptr,
nullptr, fContextReq, 0,
60 SECURITY_NATIVE_DREP,
nullptr, 0, &clientCtx,
61 &clientOutDesc, &attrs, &expiration);
62 if (status != SEC_I_CONTINUE_NEEDED)
66 serverOut.BufferType = SECBUFFER_TOKEN;
67 serverOut.cbBuffer = cbMaxToken;
68 serverOut.pvBuffer = malloc(cbMaxToken);
69 if (!serverOut.pvBuffer)
71 status = SEC_E_INSUFFICIENT_MEMORY;
75 status = table->AcceptSecurityContext(&serverCreds,
nullptr, &serverInDesc, fAcceptReq,
76 SECURITY_NATIVE_DREP, &serverCtx, &serverOutDesc, &attrs,
78 if (status != SEC_I_CONTINUE_NEEDED && status != SEC_E_OK)
82 free(clientOut.pvBuffer);
83 clientOut.cbBuffer = cbMaxToken;
84 clientOut.pvBuffer = malloc(cbMaxToken);
85 if (!clientOut.pvBuffer)
87 status = SEC_E_INSUFFICIENT_MEMORY;
91 status = table->InitializeSecurityContext(&clientCreds, &clientCtx,
nullptr, fContextReq, 0,
92 SECURITY_NATIVE_DREP, &clientInDesc, 0, &clientCtx,
93 &clientOutDesc, &attrs, &expiration);
96 free(clientOut.pvBuffer);
97 free(serverOut.pvBuffer);
98 if (SecIsValidHandle(&clientCtx))
99 (void)table->DeleteSecurityContext(&clientCtx);
100 if (SecIsValidHandle(&serverCtx))
101 (
void)table->DeleteSecurityContext(&serverCtx);
102 if (SecIsValidHandle(&clientCreds))
103 (
void)table->FreeCredentialsHandle(&clientCreds);
104 if (SecIsValidHandle(&serverCreds))
105 (
void)table->FreeCredentialsHandle(&serverCreds);
106 sspi_FreeAuthIdentity(&identity);
110static BOOL status_is_success(SECURITY_STATUS status)
115 case SEC_I_CONTINUE_NEEDED:
116 case SEC_I_COMPLETE_NEEDED:
117 case SEC_I_COMPLETE_AND_CONTINUE:
125static BOOL test_long_plaintext_password(SecurityFunctionTable* table, ULONG cbMaxToken)
127 char long_password[1025];
128 memset(long_password,
'A',
sizeof(long_password) - 1);
129 long_password[
sizeof(long_password) - 1] =
'\0';
131 const SECURITY_STATUS status = run_client_exchange(table, cbMaxToken, long_password, 0);
132 if (status == SEC_E_NO_CREDENTIALS)
134 printf(
"FAIL: long plaintext password produced SEC_E_NO_CREDENTIALS\n");
137 if (!status_is_success(status))
139 printf(
"FAIL: long plaintext password rejected with 0x%08" PRIX32
" (%s)\n", status,
140 GetSecurityStatusString(status));
148static BOOL test_password_hash_flag(SecurityFunctionTable* table, ULONG cbMaxToken)
150 const char* hash_hex =
"d5922a65c4d5c082ca444af1be0001db";
152 const SECURITY_STATUS status =
153 run_client_exchange(table, cbMaxToken, hash_hex, SEC_WINPR_AUTH_IDENTITY_PASSWORD_HASH);
154 if (!status_is_success(status))
156 printf(
"FAIL: pass-the-hash with explicit flag rejected with 0x%08" PRIX32
" (%s)\n",
157 status, GetSecurityStatusString(status));
163int TestSspiPasswordHash(
int argc,
char* argv[])
166 SecPkgInfo* packageInfo =
nullptr;
172 SecurityFunctionTable* table = InitSecurityInterfaceEx(SSPI_INTERFACE_WINPR);
176 if (table->QuerySecurityPackageInfo(NTLM_SSP_NAME, &packageInfo) != SEC_E_OK)
179 const ULONG cbMaxToken = packageInfo->cbMaxToken;
181 if (!test_long_plaintext_password(table, cbMaxToken))
184 if (!test_password_hash_flag(table, cbMaxToken))
191 (void)table->FreeContextBuffer(packageInfo);