| /* | |
| * Tests for the Negotiate security provider | |
| * | |
| * Copyright 2005, 2006 Kai Blin | |
| * Copyright 2012 Hans Leidekker for CodeWeavers | |
| * | |
| * This library is free software; you can redistribute it and/or | |
| * modify it under the terms of the GNU Lesser General Public | |
| * License as published by the Free Software Foundation; either | |
| * version 2.1 of the License, or (at your option) any later version. | |
| * | |
| * This library is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| * Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; if not, write to the Free Software | |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA | |
| */ | |
| #include <stdarg.h> | |
| #include <stdio.h> | |
| #include <windef.h> | |
| #include <winbase.h> | |
| #define SECURITY_WIN32 | |
| #include <sspi.h> | |
| #include <rpc.h> | |
| #include <rpcdce.h> | |
| #include <secext.h> | |
| #include "wine/test.h" | |
| #define NEGOTIATE_BASE_CAPS ( \ | |
| SECPKG_FLAG_INTEGRITY | \ | |
| SECPKG_FLAG_PRIVACY | \ | |
| SECPKG_FLAG_CONNECTION | \ | |
| SECPKG_FLAG_MULTI_REQUIRED | \ | |
| SECPKG_FLAG_EXTENDED_ERROR | \ | |
| SECPKG_FLAG_IMPERSONATION | \ | |
| SECPKG_FLAG_ACCEPT_WIN32_NAME | \ | |
| SECPKG_FLAG_NEGOTIABLE | \ | |
| SECPKG_FLAG_GSS_COMPATIBLE | \ | |
| SECPKG_FLAG_LOGON ) | |
| #define NTLM_BASE_CAPS ( \ | |
| SECPKG_FLAG_INTEGRITY | \ | |
| SECPKG_FLAG_PRIVACY | \ | |
| SECPKG_FLAG_TOKEN_ONLY | \ | |
| SECPKG_FLAG_CONNECTION | \ | |
| SECPKG_FLAG_MULTI_REQUIRED | \ | |
| SECPKG_FLAG_IMPERSONATION | \ | |
| SECPKG_FLAG_ACCEPT_WIN32_NAME | \ | |
| SECPKG_FLAG_NEGOTIABLE | \ | |
| SECPKG_FLAG_LOGON ) | |
| struct sspi_data | |
| { | |
| CredHandle cred; | |
| CtxtHandle ctxt; | |
| PSecBufferDesc in_buf; | |
| PSecBufferDesc out_buf; | |
| PSEC_WINNT_AUTH_IDENTITY_A id; | |
| ULONG max_token; | |
| }; | |
| static void cleanup_buffers( struct sspi_data *data ) | |
| { | |
| unsigned int i; | |
| if (data->in_buf) | |
| { | |
| for (i = 0; i < data->in_buf->cBuffers; ++i) | |
| HeapFree( GetProcessHeap(), 0, data->in_buf->pBuffers[i].pvBuffer ); | |
| HeapFree( GetProcessHeap(), 0, data->in_buf->pBuffers ); | |
| HeapFree( GetProcessHeap(), 0, data->in_buf ); | |
| } | |
| if (data->out_buf) | |
| { | |
| for (i = 0; i < data->out_buf->cBuffers; ++i) | |
| HeapFree( GetProcessHeap(), 0, data->out_buf->pBuffers[i].pvBuffer ); | |
| HeapFree( GetProcessHeap(), 0, data->out_buf->pBuffers ); | |
| HeapFree( GetProcessHeap(), 0, data->out_buf ); | |
| } | |
| } | |
| static void setup_buffers( struct sspi_data *data, SecPkgInfoA *info ) | |
| { | |
| SecBuffer *buffer = HeapAlloc( GetProcessHeap(), 0, sizeof(SecBuffer) ); | |
| data->in_buf = HeapAlloc( GetProcessHeap(), 0, sizeof(SecBufferDesc) ); | |
| data->out_buf = HeapAlloc( GetProcessHeap(), 0, sizeof(SecBufferDesc) ); | |
| data->max_token = info->cbMaxToken; | |
| data->in_buf->ulVersion = SECBUFFER_VERSION; | |
| data->in_buf->cBuffers = 1; | |
| data->in_buf->pBuffers = buffer; | |
| buffer->cbBuffer = info->cbMaxToken; | |
| buffer->BufferType = SECBUFFER_TOKEN; | |
| buffer->pvBuffer = HeapAlloc( GetProcessHeap(), 0, info->cbMaxToken ); | |
| buffer = HeapAlloc( GetProcessHeap(), 0, sizeof(SecBuffer) ); | |
| data->out_buf->ulVersion = SECBUFFER_VERSION; | |
| data->out_buf->cBuffers = 1; | |
| data->out_buf->pBuffers = buffer; | |
| buffer->cbBuffer = info->cbMaxToken; | |
| buffer->BufferType = SECBUFFER_TOKEN; | |
| buffer->pvBuffer = HeapAlloc( GetProcessHeap(), 0, info->cbMaxToken ); | |
| } | |
| static SECURITY_STATUS setup_client( struct sspi_data *data, SEC_CHAR *provider ) | |
| { | |
| SECURITY_STATUS ret; | |
| SecPkgInfoA *info; | |
| TimeStamp ttl; | |
| trace( "setting up client\n" ); | |
| ret = QuerySecurityPackageInfoA( provider, &info ); | |
| ok( ret == SEC_E_OK, "QuerySecurityPackageInfo returned %08x\n", ret ); | |
| setup_buffers( data, info ); | |
| FreeContextBuffer( info ); | |
| ret = AcquireCredentialsHandleA( NULL, provider, SECPKG_CRED_OUTBOUND, NULL, | |
| data->id, NULL, NULL, &data->cred, &ttl ); | |
| ok( ret == SEC_E_OK, "AcquireCredentialsHandleA returned %08x\n", ret ); | |
| return ret; | |
| } | |
| static SECURITY_STATUS setup_server( struct sspi_data *data, SEC_CHAR *provider ) | |
| { | |
| SECURITY_STATUS ret; | |
| SecPkgInfoA *info; | |
| TimeStamp ttl; | |
| trace( "setting up server\n" ); | |
| ret = QuerySecurityPackageInfoA( provider, &info ); | |
| ok( ret == SEC_E_OK, "QuerySecurityPackageInfo returned %08x\n", ret ); | |
| setup_buffers( data, info ); | |
| FreeContextBuffer( info ); | |
| ret = AcquireCredentialsHandleA( NULL, provider, SECPKG_CRED_INBOUND, NULL, | |
| NULL, NULL, NULL, &data->cred, &ttl ); | |
| ok( ret == SEC_E_OK, "AcquireCredentialsHandleA returned %08x\n", ret ); | |
| return ret; | |
| } | |
| static SECURITY_STATUS run_client( struct sspi_data *data, BOOL first ) | |
| { | |
| SECURITY_STATUS ret; | |
| TimeStamp ttl; | |
| ULONG attr; | |
| trace( "running client for the %s time\n", first ? "first" : "second" ); | |
| data->out_buf->pBuffers[0].cbBuffer = data->max_token; | |
| data->out_buf->pBuffers[0].BufferType = SECBUFFER_TOKEN; | |
| ret = InitializeSecurityContextA( first ? &data->cred : NULL, first ? NULL : &data->ctxt, | |
| NULL, 0, 0, SECURITY_NETWORK_DREP, first ? NULL : data->in_buf, | |
| 0, &data->ctxt, data->out_buf, &attr, &ttl ); | |
| if (ret == SEC_I_COMPLETE_AND_CONTINUE || ret == SEC_I_COMPLETE_NEEDED) | |
| { | |
| CompleteAuthToken( &data->ctxt, data->out_buf ); | |
| if (ret == SEC_I_COMPLETE_AND_CONTINUE) | |
| ret = SEC_I_CONTINUE_NEEDED; | |
| else if (ret == SEC_I_COMPLETE_NEEDED) | |
| ret = SEC_E_OK; | |
| } | |
| ok( data->out_buf->pBuffers[0].BufferType == SECBUFFER_TOKEN, | |
| "buffer type changed from SECBUFFER_TOKEN to %u\n", data->out_buf->pBuffers[0].BufferType ); | |
| ok( data->out_buf->pBuffers[0].cbBuffer < data->max_token, | |
| "InitializeSecurityContext didn't change buffer size\n" ); | |
| return ret; | |
| } | |
| static SECURITY_STATUS run_server( struct sspi_data *data, BOOL first ) | |
| { | |
| SECURITY_STATUS ret; | |
| TimeStamp ttl; | |
| ULONG attr; | |
| trace( "running server for the %s time\n", first ? "first" : "second" ); | |
| ret = AcceptSecurityContext( &data->cred, first ? NULL : &data->ctxt, | |
| data->in_buf, 0, SECURITY_NETWORK_DREP, | |
| &data->ctxt, data->out_buf, &attr, &ttl ); | |
| if (ret == SEC_I_COMPLETE_AND_CONTINUE || ret == SEC_I_COMPLETE_NEEDED) | |
| { | |
| CompleteAuthToken( &data->ctxt, data->out_buf ); | |
| if (ret == SEC_I_COMPLETE_AND_CONTINUE) | |
| ret = SEC_I_CONTINUE_NEEDED; | |
| else if (ret == SEC_I_COMPLETE_NEEDED) | |
| ret = SEC_E_OK; | |
| } | |
| return ret; | |
| } | |
| static void communicate( struct sspi_data *from, struct sspi_data *to ) | |
| { | |
| trace( "running communicate\n" ); | |
| memset( to->in_buf->pBuffers[0].pvBuffer, 0, to->max_token ); | |
| memcpy( to->in_buf->pBuffers[0].pvBuffer, from->out_buf->pBuffers[0].pvBuffer, | |
| from->out_buf->pBuffers[0].cbBuffer ); | |
| to->in_buf->pBuffers[0].cbBuffer = from->out_buf->pBuffers[0].cbBuffer; | |
| memset( from->out_buf->pBuffers[0].pvBuffer, 0, from->max_token ); | |
| } | |
| static void test_authentication(void) | |
| { | |
| SECURITY_STATUS status_c = SEC_I_CONTINUE_NEEDED, | |
| status_s = SEC_I_CONTINUE_NEEDED, status; | |
| struct sspi_data client, server; | |
| SEC_WINNT_AUTH_IDENTITY_A id; | |
| SecPkgContext_NegotiationInfoA info; | |
| SecPkgContext_Sizes sizes; | |
| SecPkgInfoA *pi; | |
| BOOL first = TRUE; | |
| memset(&client, 0, sizeof(client)); | |
| memset(&server, 0, sizeof(server)); | |
| id.User = (unsigned char *)"user"; | |
| id.UserLength = strlen( "user" ); | |
| id.Domain = (unsigned char *)"domain"; | |
| id.DomainLength = strlen( "domain" ); | |
| id.Password = (unsigned char *)"password"; | |
| id.PasswordLength = strlen( "password" ); | |
| id.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI; | |
| client.id = &id; | |
| if ((status = setup_client( &client, (SEC_CHAR *)"Negotiate" ))) | |
| { | |
| skip( "setup_client returned %08x, skipping test\n", status ); | |
| return; | |
| } | |
| if ((status = setup_server( &server, (SEC_CHAR *)"Negotiate" ))) | |
| { | |
| skip( "setup_server returned %08x, skipping test\n", status ); | |
| FreeCredentialsHandle( &client.cred ); | |
| return; | |
| } | |
| while (status_c == SEC_I_CONTINUE_NEEDED && status_s == SEC_I_CONTINUE_NEEDED) | |
| { | |
| status_c = run_client( &client, first ); | |
| ok( status_c == SEC_E_OK || status_c == SEC_I_CONTINUE_NEEDED, | |
| "client returned %08x, more tests will fail\n", status_c ); | |
| communicate( &client, &server ); | |
| status_s = run_server( &server, first ); | |
| ok( status_s == SEC_E_OK || status_s == SEC_I_CONTINUE_NEEDED || | |
| status_s == SEC_E_LOGON_DENIED, | |
| "server returned %08x, more tests will fail\n", status_s ); | |
| communicate( &server, &client ); | |
| trace( "looping\n"); | |
| first = FALSE; | |
| } | |
| if (status_c != SEC_E_OK) | |
| { | |
| skip( "authentication failed, skipping remaining tests\n" ); | |
| goto done; | |
| } | |
| sizes.cbMaxToken = 0xdeadbeef; | |
| sizes.cbMaxSignature = 0xdeadbeef; | |
| sizes.cbSecurityTrailer = 0xdeadbeef; | |
| sizes.cbBlockSize = 0xdeadbeef; | |
| status_c = QueryContextAttributesA( &client.ctxt, SECPKG_ATTR_SIZES, &sizes ); | |
| ok( status_c == SEC_E_OK, "pQueryContextAttributesA returned %08x\n", status_c ); | |
| ok( sizes.cbMaxToken == 2888 || sizes.cbMaxToken == 1904, | |
| "expected 2888 or 1904, got %u\n", sizes.cbMaxToken ); | |
| ok( sizes.cbMaxSignature == 16, "expected 16, got %u\n", sizes.cbMaxSignature ); | |
| ok( sizes.cbSecurityTrailer == 16, "expected 16, got %u\n", sizes.cbSecurityTrailer ); | |
| ok( !sizes.cbBlockSize, "expected 0, got %u\n", sizes.cbBlockSize ); | |
| memset( &info, 0, sizeof(info) ); | |
| status_c = QueryContextAttributesA( &client.ctxt, SECPKG_ATTR_NEGOTIATION_INFO, &info ); | |
| ok( status_c == SEC_E_OK, "pQueryContextAttributesA returned %08x\n", status_c ); | |
| pi = info.PackageInfo; | |
| ok( info.NegotiationState == SECPKG_NEGOTIATION_COMPLETE, "got %u\n", info.NegotiationState ); | |
| ok( pi != NULL, "expected non-NULL PackageInfo\n" ); | |
| if (pi) | |
| { | |
| ok( pi->fCapabilities == NTLM_BASE_CAPS || | |
| pi->fCapabilities == (NTLM_BASE_CAPS|SECPKG_FLAG_READONLY_WITH_CHECKSUM) || | |
| pi->fCapabilities == (NTLM_BASE_CAPS|SECPKG_FLAG_RESTRICTED_TOKENS) || | |
| pi->fCapabilities == (NTLM_BASE_CAPS|SECPKG_FLAG_RESTRICTED_TOKENS| | |
| SECPKG_FLAG_APPCONTAINER_CHECKS), | |
| "got %08x\n", pi->fCapabilities ); | |
| ok( pi->wVersion == 1, "got %u\n", pi->wVersion ); | |
| ok( pi->wRPCID == RPC_C_AUTHN_WINNT, "got %u\n", pi->wRPCID ); | |
| ok( !lstrcmpA( pi->Name, "NTLM" ), "got %s\n", pi->Name ); | |
| } | |
| done: | |
| cleanup_buffers( &client ); | |
| cleanup_buffers( &server ); | |
| if (client.ctxt.dwLower || client.ctxt.dwUpper) | |
| { | |
| status_c = DeleteSecurityContext( &client.ctxt ); | |
| ok( status_c == SEC_E_OK, "DeleteSecurityContext returned %08x\n", status_c ); | |
| } | |
| if (server.ctxt.dwLower || server.ctxt.dwUpper) | |
| { | |
| status_s = DeleteSecurityContext( &server.ctxt ); | |
| ok( status_s == SEC_E_OK, "DeleteSecurityContext returned %08x\n", status_s ); | |
| } | |
| if (client.cred.dwLower || client.cred.dwUpper) | |
| { | |
| status_c = FreeCredentialsHandle( &client.cred ); | |
| ok( status_c == SEC_E_OK, "FreeCredentialsHandle returned %08x\n", status_c ); | |
| } | |
| if (server.cred.dwLower || server.cred.dwUpper) | |
| { | |
| status_s = FreeCredentialsHandle(&server.cred); | |
| ok( status_s == SEC_E_OK, "FreeCredentialsHandle returned %08x\n", status_s ); | |
| } | |
| } | |
| START_TEST(negotiate) | |
| { | |
| SecPkgInfoA *info; | |
| if (QuerySecurityPackageInfoA( (SEC_CHAR *)"Negotiate", &info )) | |
| { | |
| ok( 0, "Negotiate package not installed, skipping test\n" ); | |
| return; | |
| } | |
| ok( info->fCapabilities == NEGOTIATE_BASE_CAPS || | |
| info->fCapabilities == (NEGOTIATE_BASE_CAPS|SECPKG_FLAG_READONLY_WITH_CHECKSUM) || | |
| info->fCapabilities == (NEGOTIATE_BASE_CAPS|SECPKG_FLAG_RESTRICTED_TOKENS) || | |
| info->fCapabilities == (NEGOTIATE_BASE_CAPS|SECPKG_FLAG_RESTRICTED_TOKENS| | |
| SECPKG_FLAG_APPCONTAINER_CHECKS), | |
| "got %08x\n", info->fCapabilities ); | |
| ok( info->wVersion == 1, "got %u\n", info->wVersion ); | |
| ok( info->wRPCID == RPC_C_AUTHN_GSS_NEGOTIATE, "got %u\n", info->wRPCID ); | |
| ok( !lstrcmpA( info->Name, "Negotiate" ), "got %s\n", info->Name ); | |
| FreeContextBuffer( info ); | |
| test_authentication(); | |
| } |