diff options
Diffstat (limited to 'v_windows/v/old/thirdparty/vschannel')
| -rw-r--r-- | v_windows/v/old/thirdparty/vschannel/vschannel.c | 1095 | ||||
| -rw-r--r-- | v_windows/v/old/thirdparty/vschannel/vschannel.h | 47 | 
2 files changed, 1142 insertions, 0 deletions
diff --git a/v_windows/v/old/thirdparty/vschannel/vschannel.c b/v_windows/v/old/thirdparty/vschannel/vschannel.c new file mode 100644 index 0000000..0d55dde --- /dev/null +++ b/v_windows/v/old/thirdparty/vschannel/vschannel.c @@ -0,0 +1,1095 @@ +#include <vschannel.h> +#include <sspi.h> + +// Proxy +WCHAR *  psz_proxy_server  = L"proxy"; +INT     i_proxy_port      = 80; + +// Options +INT     port_number     = 443; +BOOL    use_proxy       = FALSE; +DWORD   protocol        = 0; +ALG_ID  aid_key_exch    = 0; + +// TODO: joe-c +// socket / tls ctx +struct TlsContext { +	// SSPI +	PSecurityFunctionTable sspi; +	// Cred store +	HCERTSTORE             cert_store; +	SCHANNEL_CRED          schannel_cred; +	// Socket +	SOCKET                 socket;	 +	CredHandle             h_client_creds; +	CtxtHandle             h_context; +	PCCERT_CONTEXT         p_pemote_cert_context; +	BOOL                   creds_initialized; +	BOOL                   context_initialized; +}; + +TlsContext new_tls_context() { +	return (struct TlsContext) { +		.cert_store            = NULL, +		.socket                = INVALID_SOCKET, +		.creds_initialized     = FALSE, +		.context_initialized   = FALSE, +		.p_pemote_cert_context = NULL +	}; +}; + +void vschannel_cleanup(TlsContext *tls_ctx) { +	// Free the server certificate context. +	if(tls_ctx->p_pemote_cert_context) { +		CertFreeCertificateContext(tls_ctx->p_pemote_cert_context); +		tls_ctx->p_pemote_cert_context = NULL; +	} + +	// Free SSPI context handle. +	if(tls_ctx->context_initialized) { +		tls_ctx->sspi->DeleteSecurityContext(&tls_ctx->h_context); +		tls_ctx->context_initialized = FALSE; +	} + +	// Free SSPI credentials handle. +	if(tls_ctx->creds_initialized) { +		tls_ctx->sspi->FreeCredentialsHandle(&tls_ctx->h_client_creds); +		tls_ctx->creds_initialized = FALSE; +	} +	 +	// Close socket. +	if(tls_ctx->socket != INVALID_SOCKET) { +		closesocket(tls_ctx->socket); +		tls_ctx->socket = INVALID_SOCKET; +	} +	 +	// Close "MY" certificate store. +	if(tls_ctx->cert_store) { +		CertCloseStore(tls_ctx->cert_store, 0); +		tls_ctx->cert_store = NULL; +	} +} + +void vschannel_init(TlsContext *tls_ctx) { +	tls_ctx->sspi = InitSecurityInterface(); + +	if(tls_ctx->sspi == NULL) { +		wprintf(L"Error 0x%x reading security interface.\n", +			   GetLastError()); +		vschannel_cleanup(tls_ctx); +	} + +	// Create credentials. +	if(create_credentials(tls_ctx)) { +		wprintf(L"Error creating credentials\n"); +		vschannel_cleanup(tls_ctx); +	} +	tls_ctx->creds_initialized = TRUE; +} + +INT request(TlsContext *tls_ctx, INT iport, LPWSTR host, CHAR *req, CHAR **out) +{ +	SecBuffer  ExtraData; +	SECURITY_STATUS Status; + +	INT i; +	INT iOption; +	PCHAR pszOption; + +	INT resp_length = 0; + +	protocol = SP_PROT_TLS1_2_CLIENT; + +	port_number = iport; + +	// Connect to server. +	if(connect_to_server(tls_ctx, host, port_number)) { +		wprintf(L"Error connecting to server\n"); +		vschannel_cleanup(tls_ctx); +		return resp_length; +	} + +	// Perform handshake +	if(perform_client_handshake(tls_ctx, host, &ExtraData)) { +		wprintf(L"Error performing handshake\n"); +		vschannel_cleanup(tls_ctx); +		return resp_length; +	} +	tls_ctx->context_initialized = TRUE; + +	// Authenticate server's credentials. + +	// Get server's certificate. +	Status = tls_ctx->sspi->QueryContextAttributes(&tls_ctx->h_context, +											 SECPKG_ATTR_REMOTE_CERT_CONTEXT, +											 (PVOID)&tls_ctx->p_pemote_cert_context); +	if(Status != SEC_E_OK) { +		wprintf(L"Error 0x%x querying remote certificate\n", Status); +		vschannel_cleanup(tls_ctx); +		return resp_length; +	} + +	// Attempt to validate server certificate. +	Status = verify_server_certificate(tls_ctx->p_pemote_cert_context, host,0); +	if(Status) { +		// The server certificate did not validate correctly. At this +		// point, we cannot tell if we are connecting to the correct  +		// server, or if we are connecting to a "man in the middle"  +		// attack server. + +		// It is therefore best if we abort the connection. + +		wprintf(L"Error 0x%x authenticating server credentials!\n", Status); +		vschannel_cleanup(tls_ctx); +		return resp_length; +	} + +	// Free the server certificate context. +	CertFreeCertificateContext(tls_ctx->p_pemote_cert_context); +	tls_ctx->p_pemote_cert_context = NULL; + +	// Request from server +	if(https_make_request(tls_ctx, req, out, &resp_length)) { +		vschannel_cleanup(tls_ctx); +		return resp_length; +	} +	 +	// Send a close_notify alert to the server and +	// close down the connection. +	if(disconnect_from_server(tls_ctx)) { +		wprintf(L"Error disconnecting from server\n"); +		vschannel_cleanup(tls_ctx); +		return resp_length; +	} +	tls_ctx->context_initialized = FALSE; +	tls_ctx->socket = INVALID_SOCKET; + +	return resp_length; +} + + +static SECURITY_STATUS create_credentials(TlsContext *tls_ctx) { +	TimeStamp       tsExpiry; +	SECURITY_STATUS Status; + +	DWORD           cSupportedAlgs = 0; +	ALG_ID          rgbSupportedAlgs[16]; + +	PCCERT_CONTEXT  pCertContext = NULL; + +	// Open the "MY" certificate store, which is where Internet Explorer +	// stores its client certificates. +	if(tls_ctx->cert_store == NULL) { +		tls_ctx->cert_store = CertOpenSystemStore(0, L"MY"); + +		if(!tls_ctx->cert_store) { +			wprintf(L"Error 0x%x returned by CertOpenSystemStore\n",  +			GetLastError()); +			return SEC_E_NO_CREDENTIALS; +		} +	} + +	// Build Schannel credential structure. Currently, this sample only +	// specifies the protocol to be used (and optionally the certificate,  +	// of course). Real applications may wish to specify other parameters  +	// as well. + +	ZeroMemory(&tls_ctx->schannel_cred, sizeof(tls_ctx->schannel_cred)); + +	tls_ctx->schannel_cred.dwVersion  = SCHANNEL_CRED_VERSION; +	if(pCertContext) +	{ +		tls_ctx->schannel_cred.cCreds     = 1; +		tls_ctx->schannel_cred.paCred     = &pCertContext; +	} + +	tls_ctx->schannel_cred.grbitEnabledProtocols = protocol; + +	if(aid_key_exch) +	{ +		rgbSupportedAlgs[cSupportedAlgs++] = aid_key_exch; +	} + +	if(cSupportedAlgs) +	{ +		tls_ctx->schannel_cred.cSupportedAlgs    = cSupportedAlgs; +		tls_ctx->schannel_cred.palgSupportedAlgs = rgbSupportedAlgs; +	} + +	tls_ctx->schannel_cred.dwFlags |= SCH_CRED_NO_DEFAULT_CREDS; + +	// The SCH_CRED_MANUAL_CRED_VALIDATION flag is specified because +	// this sample verifies the server certificate manually.  +	// Applications that expect to run on WinNT, Win9x, or WinME  +	// should specify this flag and also manually verify the server +	// certificate. Applications running on newer versions of Windows can +	// leave off this flag, in which case the InitializeSecurityContext +	// function will validate the server certificate automatically. +	// tls_ctx->schannel_cred.dwFlags |= SCH_CRED_MANUAL_CRED_VALIDATION; + +	// Create an SSPI credential. + +	Status = tls_ctx->sspi->AcquireCredentialsHandle( +						NULL,                   // Name of principal     +						UNISP_NAME_W,           // Name of package +						SECPKG_CRED_OUTBOUND,   // Flags indicating use +						NULL,                   // Pointer to logon ID +						&tls_ctx->schannel_cred,          // Package specific data +						NULL,                   // Pointer to GetKey() func +						NULL,                   // Value to pass to GetKey() +						&tls_ctx->h_client_creds,                // (out) Cred Handle +						&tsExpiry);             // (out) Lifetime (optional) +	if(Status != SEC_E_OK) { +		wprintf(L"Error 0x%x returned by AcquireCredentialsHandle\n", Status); +		goto cleanup; +	} + +cleanup: + +	// Free the certificate context. Schannel has already made its own copy. + +	if(pCertContext) { +		CertFreeCertificateContext(pCertContext); +	} + + +	return Status; +} + + +static INT connect_to_server(TlsContext *tls_ctx, LPWSTR host, INT port_number) { +	SOCKET Socket; +	 +	SOCKADDR_STORAGE local_address = { 0 }; +	SOCKADDR_STORAGE remote_address = { 0 }; + +	DWORD local_address_length =  sizeof(local_address); +	DWORD remote_address_length = sizeof(remote_address); + +	struct timeval tv; +	tv.tv_sec = 60; +	tv.tv_usec = 0; + +	Socket = socket(PF_INET, SOCK_STREAM, 0); +	if(Socket == INVALID_SOCKET) { +		wprintf(L"Error %d creating socket\n", WSAGetLastError()); +		return WSAGetLastError(); +	} + +	LPWSTR connect_name = use_proxy ? psz_proxy_server : host; + +	WCHAR service_name[10]; +	int res = wsprintf(service_name, L"%d", port_number); + +	if(WSAConnectByNameW(Socket,connect_name, service_name, &local_address_length,  +		&local_address, &remote_address_length, &remote_address, &tv, NULL) == SOCKET_ERROR) { +		wprintf(L"Error %d connecting to \"%s\" (%s)\n",  +			WSAGetLastError(), +			connect_name,  +			service_name); +		closesocket(Socket); +		return WSAGetLastError(); +	} + +	if(use_proxy) { +		BYTE  pbMessage[200];  +		DWORD cbMessage; + +		// Build message for proxy server +		strcpy(pbMessage, "CONNECT "); +		strcat(pbMessage, host); +		strcat(pbMessage, ":"); +		_itoa(port_number, pbMessage + strlen(pbMessage), 10); +		strcat(pbMessage, " HTTP/1.0\r\nUser-Agent: webclient\r\n\r\n"); +		cbMessage = (DWORD)strlen(pbMessage); + +		// Send message to proxy server +		if(send(Socket, pbMessage, cbMessage, 0) == SOCKET_ERROR) { +			wprintf(L"Error %d sending message to proxy!\n", WSAGetLastError()); +			return WSAGetLastError(); +		} + +		// Receive message from proxy server +		cbMessage = recv(Socket, pbMessage, 200, 0); +		if(cbMessage == SOCKET_ERROR) { +			wprintf(L"Error %d receiving message from proxy\n", WSAGetLastError()); +			return WSAGetLastError(); +		} + +		// this sample is limited but in normal use it  +		// should continue to receive until CR LF CR LF is received +	} + +	tls_ctx->socket = Socket; + +	return SEC_E_OK; +} + + +static LONG disconnect_from_server(TlsContext *tls_ctx) { +	DWORD           dwType; +	PBYTE           pbMessage; +	DWORD           cbMessage; +	DWORD           cbData; + +	SecBufferDesc   OutBuffer; +	SecBuffer       OutBuffers[1]; +	DWORD           dwSSPIFlags; +	DWORD           dwSSPIOutFlags; +	TimeStamp       tsExpiry; +	DWORD           Status; + +	// Notify schannel that we are about to close the connection. + +	dwType = SCHANNEL_SHUTDOWN; + +	OutBuffers[0].pvBuffer   = &dwType; +	OutBuffers[0].BufferType = SECBUFFER_TOKEN; +	OutBuffers[0].cbBuffer   = sizeof(dwType); + +	OutBuffer.cBuffers  = 1; +	OutBuffer.pBuffers  = OutBuffers; +	OutBuffer.ulVersion = SECBUFFER_VERSION; + +	Status = tls_ctx->sspi->ApplyControlToken(&tls_ctx->h_context, &OutBuffer); + +	if(FAILED(Status)) { +		wprintf(L"Error 0x%x returned by ApplyControlToken\n", Status); +		goto cleanup; +	} + +	// Build an SSL close notify message. + +	dwSSPIFlags = ISC_REQ_SEQUENCE_DETECT   | +				  ISC_REQ_REPLAY_DETECT     | +				  ISC_REQ_CONFIDENTIALITY   | +				  ISC_RET_EXTENDED_ERROR    | +				  ISC_REQ_ALLOCATE_MEMORY   | +				  ISC_REQ_STREAM; + +	OutBuffers[0].pvBuffer   = NULL; +	OutBuffers[0].BufferType = SECBUFFER_TOKEN; +	OutBuffers[0].cbBuffer   = 0; + +	OutBuffer.cBuffers  = 1; +	OutBuffer.pBuffers  = OutBuffers; +	OutBuffer.ulVersion = SECBUFFER_VERSION; + +	Status = tls_ctx->sspi->InitializeSecurityContext( +		&tls_ctx->h_client_creds, &tls_ctx->h_context, NULL, dwSSPIFlags, 0, SECURITY_NATIVE_DREP, +		NULL, 0, &tls_ctx->h_context, &OutBuffer, &dwSSPIOutFlags, &tsExpiry); + +	if(FAILED(Status))  { +		wprintf(L"Error 0x%x returned by InitializeSecurityContext\n", Status); +		goto cleanup; +	} + +	pbMessage = OutBuffers[0].pvBuffer; +	cbMessage = OutBuffers[0].cbBuffer; + +	// Send the close notify message to the server. + +	if(pbMessage != NULL && cbMessage != 0) { +		cbData = send(tls_ctx->socket, pbMessage, cbMessage, 0); +		if(cbData == SOCKET_ERROR || cbData == 0) { +			Status = WSAGetLastError(); +			wprintf(L"Error %d sending close notify\n", Status); +			goto cleanup; +		} + +		// Free output buffer. +		tls_ctx->sspi->FreeContextBuffer(pbMessage); +	} +	 + +cleanup: + +	// Free the security context. +	tls_ctx->sspi->DeleteSecurityContext(&tls_ctx->h_context); + +	// Close the socket. +	closesocket(tls_ctx->socket); + +	return Status; +} + + +static SECURITY_STATUS perform_client_handshake(TlsContext *tls_ctx, WCHAR *host, SecBuffer *pExtraData) { +	SecBufferDesc   OutBuffer; +	SecBuffer       OutBuffers[1]; +	DWORD           dwSSPIFlags; +	DWORD           dwSSPIOutFlags; +	TimeStamp       tsExpiry; +	SECURITY_STATUS scRet; +	DWORD           cbData; + +	dwSSPIFlags = ISC_REQ_SEQUENCE_DETECT   | +				  ISC_REQ_REPLAY_DETECT     | +				  ISC_REQ_CONFIDENTIALITY   | +				  ISC_RET_EXTENDED_ERROR    | +				  ISC_REQ_ALLOCATE_MEMORY   | +				  ISC_REQ_STREAM; + +	// +	//  Initiate a ClientHello message and generate a token. +	// + +	OutBuffers[0].pvBuffer   = NULL; +	OutBuffers[0].BufferType = SECBUFFER_TOKEN; +	OutBuffers[0].cbBuffer   = 0; + +	OutBuffer.cBuffers = 1; +	OutBuffer.pBuffers = OutBuffers; +	OutBuffer.ulVersion = SECBUFFER_VERSION; + +	scRet = tls_ctx->sspi->InitializeSecurityContext( +					&tls_ctx->h_client_creds, +					NULL, +					host, +					dwSSPIFlags, +					0, +					SECURITY_NATIVE_DREP, +					NULL, +					0, +					&tls_ctx->h_context, +					&OutBuffer, +					&dwSSPIOutFlags, +					&tsExpiry); + +	if(scRet != SEC_I_CONTINUE_NEEDED) +	{ +		wprintf(L"Error %d returned by InitializeSecurityContext (1)\n", scRet); +		return scRet; +	} + +	// Send response to server if there is one. +	if(OutBuffers[0].cbBuffer != 0 && OutBuffers[0].pvBuffer != NULL) +	{ +		cbData = send(tls_ctx->socket, OutBuffers[0].pvBuffer, OutBuffers[0].cbBuffer, 0); +		if(cbData == SOCKET_ERROR || cbData == 0) { +			wprintf(L"Error %d sending data to server (1)\n", WSAGetLastError()); +			tls_ctx->sspi->FreeContextBuffer(OutBuffers[0].pvBuffer); +			tls_ctx->sspi->DeleteSecurityContext(&tls_ctx->h_context); +			return SEC_E_INTERNAL_ERROR; +		} + +		// Free output buffer. +		tls_ctx->sspi->FreeContextBuffer(OutBuffers[0].pvBuffer); +		OutBuffers[0].pvBuffer = NULL; +	} + +	return client_handshake_loop(tls_ctx, TRUE, pExtraData); +} + + +static SECURITY_STATUS client_handshake_loop(TlsContext *tls_ctx, BOOL fDoInitialRead, SecBuffer *pExtraData) { +	SecBufferDesc   InBuffer; +	SecBuffer       InBuffers[2]; +	SecBufferDesc   OutBuffer; +	SecBuffer       OutBuffers[1]; +	DWORD           dwSSPIFlags; +	DWORD           dwSSPIOutFlags; +	TimeStamp       tsExpiry; +	SECURITY_STATUS scRet; +	DWORD           cbData; + +	PUCHAR          IoBuffer; +	DWORD           cbIoBuffer; +	BOOL            fDoRead; + + +	dwSSPIFlags = ISC_REQ_SEQUENCE_DETECT   | +				  ISC_REQ_REPLAY_DETECT     | +				  ISC_REQ_CONFIDENTIALITY   | +				  ISC_RET_EXTENDED_ERROR    | +				  ISC_REQ_ALLOCATE_MEMORY   | +				  ISC_REQ_STREAM; + +	// +	// Allocate data buffer. +	// + +	IoBuffer = LocalAlloc(LPTR, IO_BUFFER_SIZE); +	if(IoBuffer == NULL) +	{ +		wprintf(L"Out of memory (1)\n"); +		return SEC_E_INTERNAL_ERROR; +	} +	cbIoBuffer = 0; + +	fDoRead = fDoInitialRead; + + +	//  +	// Loop until the handshake is finished or an error occurs. +	// + +	scRet = SEC_I_CONTINUE_NEEDED; + +	while(scRet == SEC_I_CONTINUE_NEEDED || +		  scRet == SEC_E_INCOMPLETE_MESSAGE || +		  scRet == SEC_I_INCOMPLETE_CREDENTIALS) { +		 +		// Read data from server. +		if(0 == cbIoBuffer || scRet == SEC_E_INCOMPLETE_MESSAGE) { +			if(fDoRead) { +				cbData = recv(tls_ctx->socket,  +							  IoBuffer + cbIoBuffer,  +							  IO_BUFFER_SIZE - cbIoBuffer,  +							  0); +				if(cbData == SOCKET_ERROR) { +					wprintf(L"Error %d reading data from server\n", WSAGetLastError()); +					scRet = SEC_E_INTERNAL_ERROR; +					break; +				} +				else if(cbData == 0) { +					wprintf(L"Server unexpectedly disconnected\n"); +					scRet = SEC_E_INTERNAL_ERROR; +					break; +				} + +				cbIoBuffer += cbData; +			} +			else { +				fDoRead = TRUE; +			} +		} + +		// Set up the input buffers. Buffer 0 is used to pass in data +		// received from the server. Schannel will consume some or all +		// of this. Leftover data (if any) will be placed in buffer 1 and +		// given a buffer type of SECBUFFER_EXTRA. + +		InBuffers[0].pvBuffer   = IoBuffer; +		InBuffers[0].cbBuffer   = cbIoBuffer; +		InBuffers[0].BufferType = SECBUFFER_TOKEN; + +		InBuffers[1].pvBuffer   = NULL; +		InBuffers[1].cbBuffer   = 0; +		InBuffers[1].BufferType = SECBUFFER_EMPTY; + +		InBuffer.cBuffers       = 2; +		InBuffer.pBuffers       = InBuffers; +		InBuffer.ulVersion      = SECBUFFER_VERSION; + +		// Set up the output buffers. These are initialized to NULL +		// so as to make it less likely we'll attempt to free random +		// garbage later. + +		OutBuffers[0].pvBuffer  = NULL; +		OutBuffers[0].BufferType= SECBUFFER_TOKEN; +		OutBuffers[0].cbBuffer  = 0; + +		OutBuffer.cBuffers      = 1; +		OutBuffer.pBuffers      = OutBuffers; +		OutBuffer.ulVersion     = SECBUFFER_VERSION; + +		// Call InitializeSecurityContext. + +		scRet = tls_ctx->sspi->InitializeSecurityContext( +			&tls_ctx->h_client_creds, &tls_ctx->h_context,	NULL, dwSSPIFlags, 0, SECURITY_NATIVE_DREP, +			&InBuffer, 0, NULL, &OutBuffer, &dwSSPIOutFlags, &tsExpiry); + +		// If InitializeSecurityContext was successful (or if the error was  +		// one of the special extended ones), send the contends of the output +		// buffer to the server. + +		if(scRet == SEC_E_OK || +		   scRet == SEC_I_CONTINUE_NEEDED || +		   FAILED(scRet) && (dwSSPIOutFlags & ISC_RET_EXTENDED_ERROR)) { +			if(OutBuffers[0].cbBuffer != 0 && OutBuffers[0].pvBuffer != NULL) { +				cbData = send(tls_ctx->socket, +							  OutBuffers[0].pvBuffer, +							  OutBuffers[0].cbBuffer, +							  0); +				if(cbData == SOCKET_ERROR || cbData == 0) { +					wprintf(L"Error %d sending data to server (2)\n",  +						WSAGetLastError()); +					tls_ctx->sspi->FreeContextBuffer(OutBuffers[0].pvBuffer); +					tls_ctx->sspi->DeleteSecurityContext(&tls_ctx->h_context); +					return SEC_E_INTERNAL_ERROR; +				} + +				// Free output buffer. +				tls_ctx->sspi->FreeContextBuffer(OutBuffers[0].pvBuffer); +				OutBuffers[0].pvBuffer = NULL; +			} +		} + +		// If InitializeSecurityContext returned SEC_E_INCOMPLETE_MESSAGE, +		// then we need to read more data from the server and try again. +		if(scRet == SEC_E_INCOMPLETE_MESSAGE) { +			continue; +		} + +		// If InitializeSecurityContext returned SEC_E_OK, then the  +		// handshake completed successfully. + +		if(scRet == SEC_E_OK) { +			// If the "extra" buffer contains data, this is encrypted application +			// protocol layer stuff. It needs to be saved. The application layer +			// will later decrypt it with DecryptMessage. + +			if(InBuffers[1].BufferType == SECBUFFER_EXTRA) +			{ +				pExtraData->pvBuffer = LocalAlloc(LPTR, InBuffers[1].cbBuffer); +				if(pExtraData->pvBuffer == NULL) { +					wprintf(L"Out of memory (2)\n"); +					return SEC_E_INTERNAL_ERROR; +				} + +				MoveMemory(pExtraData->pvBuffer, +					IoBuffer + (cbIoBuffer - InBuffers[1].cbBuffer), +					InBuffers[1].cbBuffer); + +				pExtraData->cbBuffer   = InBuffers[1].cbBuffer; +				pExtraData->BufferType = SECBUFFER_TOKEN; + +				// wprintf(L"%d bytes of app data was bundled with handshake data\n", pExtraData->cbBuffer); +			} +			else { +				pExtraData->pvBuffer   = NULL; +				pExtraData->cbBuffer   = 0; +				pExtraData->BufferType = SECBUFFER_EMPTY; +			} + +			// Bail out to quit +			break; +		} + +		// Check for fatal error. +		if(FAILED(scRet)) { +			wprintf(L"Error 0x%x returned by InitializeSecurityContext (2)\n", scRet); +			break; +		} + +		// If InitializeSecurityContext returned SEC_I_INCOMPLETE_CREDENTIALS, +		// then the server just requested client authentication.  +		if(scRet == SEC_I_INCOMPLETE_CREDENTIALS) { +			// Busted. The server has requested client authentication and +			// the credential we supplied didn't contain a client certificate. + +			// This function will read the list of trusted certificate +			// authorities ("issuers") that was received from the server +			// and attempt to find a suitable client certificate that +			// was issued by one of these. If this function is successful,  +			// then we will connect using the new certificate. Otherwise, +			// we will attempt to connect anonymously (using our current +			// credentials). +			 +			get_new_client_credentials(tls_ctx); + +			// Go around again. +			fDoRead = FALSE; +			scRet = SEC_I_CONTINUE_NEEDED; +			continue; +		} + +		// Copy any leftover data from the "extra" buffer, and go around +		// again. + +		if ( InBuffers[1].BufferType == SECBUFFER_EXTRA ) { +			MoveMemory(IoBuffer, +					   IoBuffer + (cbIoBuffer - InBuffers[1].cbBuffer), +					   InBuffers[1].cbBuffer); + +			cbIoBuffer = InBuffers[1].cbBuffer; +		} +		else { +			cbIoBuffer = 0; +		} +	} + +	// Delete the security context in the case of a fatal error. +	if(FAILED(scRet)) { +		tls_ctx->sspi->DeleteSecurityContext(&tls_ctx->h_context); +	} + +	LocalFree(IoBuffer); + +	return scRet; +} + + +static SECURITY_STATUS https_make_request(TlsContext *tls_ctx, CHAR *req, CHAR **out, int *length) { +	SecPkgContext_StreamSizes Sizes; +	SECURITY_STATUS scRet; +	SecBufferDesc   Message; +	SecBuffer       Buffers[4]; +	SecBuffer 	   *pDataBuffer; +	SecBuffer 	   *pExtraBuffer; +	SecBuffer       ExtraBuffer; + +	PBYTE pbIoBuffer; +	DWORD cbIoBuffer; +	DWORD cbIoBufferLength; +	PBYTE pbMessage; +	DWORD cbMessage; + +	DWORD cbData; +	INT   i; + + +	// Read stream encryption properties. +	scRet = tls_ctx->sspi->QueryContextAttributes(&tls_ctx->h_context, SECPKG_ATTR_STREAM_SIZES, &Sizes); +	if(scRet != SEC_E_OK) { +		wprintf(L"Error 0x%x reading SECPKG_ATTR_STREAM_SIZES\n", scRet); +		return scRet; +	} + +	// Allocate a working buffer. The plaintext sent to EncryptMessage +	// should never be more than 'Sizes.cbMaximumMessage', so a buffer  +	// size of this plus the header and trailer sizes should be safe enough. +	cbIoBufferLength = Sizes.cbHeader +  Sizes.cbMaximumMessage + Sizes.cbTrailer; + +	pbIoBuffer = LocalAlloc(LPTR, cbIoBufferLength); +	if(pbIoBuffer == NULL) { +		wprintf(L"Out of memory (2)\n"); +		return SEC_E_INTERNAL_ERROR; +	} +	 +	// Build an HTTP request to send to the server. + +	// Build the HTTP request offset into the data buffer by "header size" +	// bytes. This enables Schannel to perform the encryption in place, +	// which is a significant performance win. +	pbMessage = pbIoBuffer + Sizes.cbHeader; + +	// Build HTTP request. Note that I'm assuming that this is less than +	// the maximum message size. If it weren't, it would have to be broken up. +	sprintf(pbMessage, "%s", req); + +	cbMessage = (DWORD)strlen(pbMessage); + + +	// Encrypt the HTTP request. +	Buffers[0].pvBuffer     = pbIoBuffer; +	Buffers[0].cbBuffer     = Sizes.cbHeader; +	Buffers[0].BufferType   = SECBUFFER_STREAM_HEADER; + +	Buffers[1].pvBuffer     = pbMessage; +	Buffers[1].cbBuffer     = cbMessage; +	Buffers[1].BufferType   = SECBUFFER_DATA; + +	Buffers[2].pvBuffer     = pbMessage + cbMessage; +	Buffers[2].cbBuffer     = Sizes.cbTrailer; +	Buffers[2].BufferType   = SECBUFFER_STREAM_TRAILER; + +	Buffers[3].BufferType   = SECBUFFER_EMPTY; + +	Message.ulVersion       = SECBUFFER_VERSION; +	Message.cBuffers        = 4; +	Message.pBuffers        = Buffers; + +	scRet = tls_ctx->sspi->EncryptMessage(&tls_ctx->h_context, 0, &Message, 0); + +	if(FAILED(scRet)) { +		wprintf(L"Error 0x%x returned by EncryptMessage\n", scRet); +		return scRet; +	} + +	// Send the encrypted data to the server. +	cbData = send(tls_ctx->socket, pbIoBuffer, Buffers[0].cbBuffer + Buffers[1].cbBuffer + Buffers[2].cbBuffer, 0); +	if(cbData == SOCKET_ERROR || cbData == 0) { +		wprintf(L"Error %d sending data to server (3)\n",  WSAGetLastError()); +		tls_ctx->sspi->DeleteSecurityContext(&tls_ctx->h_context); +		return SEC_E_INTERNAL_ERROR; +	} + +	// Read data from server until done. +	INT buff_size = vsc_init_resp_buff_size; +	cbIoBuffer = 0; +	while(TRUE){ +		// Read some data. +		if(0 == cbIoBuffer || scRet == SEC_E_INCOMPLETE_MESSAGE) { +			cbData = recv(tls_ctx->socket, pbIoBuffer + cbIoBuffer, cbIoBufferLength - cbIoBuffer, 0); +			if(cbData == SOCKET_ERROR) { +				wprintf(L"Error %d reading data from server\n", WSAGetLastError()); +				scRet = SEC_E_INTERNAL_ERROR; +				break; +			} +			else if(cbData == 0) { +				// Server disconnected. +				if(cbIoBuffer) { +					wprintf(L"Server unexpectedly disconnected\n"); +					scRet = SEC_E_INTERNAL_ERROR; +					return scRet; +				} +				else { +					break; +				} +			} +			else { +				cbIoBuffer += cbData; +			} +		} + +		// Attempt to decrypt the received data. +		Buffers[0].pvBuffer     = pbIoBuffer; +		Buffers[0].cbBuffer     = cbIoBuffer; +		Buffers[0].BufferType   = SECBUFFER_DATA; + +		Buffers[1].BufferType   = SECBUFFER_EMPTY; +		Buffers[2].BufferType   = SECBUFFER_EMPTY; +		Buffers[3].BufferType   = SECBUFFER_EMPTY; + +		Message.ulVersion       = SECBUFFER_VERSION; +		Message.cBuffers        = 4; +		Message.pBuffers        = Buffers; + +		scRet = tls_ctx->sspi->DecryptMessage(&tls_ctx->h_context, &Message, 0, NULL); + +		if(scRet == SEC_E_INCOMPLETE_MESSAGE) { +			// The input buffer contains only a fragment of an +			// encrypted record. Loop around and read some more +			// data. +			continue; +		} + +		// Server signalled end of session +		if(scRet == SEC_I_CONTEXT_EXPIRED) { +			break; +		} + +		if( scRet != SEC_E_OK &&  +			scRet != SEC_I_RENEGOTIATE &&  +			scRet != SEC_I_CONTEXT_EXPIRED) +		{ +			wprintf(L"Error 0x%x returned by DecryptMessage\n", scRet); +			return scRet; +		} + +		// Locate data and (optional) extra buffers. +		pDataBuffer  = NULL; +		pExtraBuffer = NULL; +		for(i = 1; i < 4; i++) { +			if(pDataBuffer == NULL && Buffers[i].BufferType == SECBUFFER_DATA) +			{ +				pDataBuffer = &Buffers[i]; +				// wprintf(L"Buffers[%d].BufferType = SECBUFFER_DATA\n",i); +			} +			if(pExtraBuffer == NULL && Buffers[i].BufferType == SECBUFFER_EXTRA) +			{ +				pExtraBuffer = &Buffers[i]; +			} +		} + +		// increase buffer size if we need +		int required_length = *length+(int)pDataBuffer->cbBuffer; +		if( required_length > buff_size ) { +			CHAR *a = realloc(*out, required_length); +			if( a == NULL ) { +				scRet = SEC_E_INTERNAL_ERROR; +				return scRet; +			} +			*out = a; +			buff_size = required_length; +		} +		// Copy the decrypted data to our output buffer +		memcpy(*out+*length, pDataBuffer->pvBuffer, (int)pDataBuffer->cbBuffer); +		*length += (int)pDataBuffer->cbBuffer; +		 +		// Move any "extra" data to the input buffer. +		if(pExtraBuffer) { +			MoveMemory(pbIoBuffer, pExtraBuffer->pvBuffer, pExtraBuffer->cbBuffer); +			cbIoBuffer = pExtraBuffer->cbBuffer; +		} +		else { +			cbIoBuffer = 0; +		} + +		if(scRet == SEC_I_RENEGOTIATE) +		{ +			// The server wants to perform another handshake sequence. +			scRet = client_handshake_loop(tls_ctx, FALSE, &ExtraBuffer); +			if(scRet != SEC_E_OK) { +				return scRet; +			} + +			// Move any "extra" data to the input buffer. +			if(ExtraBuffer.pvBuffer) +			{ +				MoveMemory(pbIoBuffer, ExtraBuffer.pvBuffer, ExtraBuffer.cbBuffer); +				cbIoBuffer = ExtraBuffer.cbBuffer; +			} +		} +	} + +	return SEC_E_OK; +} + + +static DWORD verify_server_certificate( PCCERT_CONTEXT  pServerCert, LPWSTR host, DWORD dwCertFlags) { +	HTTPSPolicyCallbackData  polHttps; +	CERT_CHAIN_POLICY_PARA   PolicyPara; +	CERT_CHAIN_POLICY_STATUS PolicyStatus; +	CERT_CHAIN_PARA          ChainPara; +	PCCERT_CHAIN_CONTEXT     pChainContext = NULL; + +	CHAR *rgszUsages[] = {  szOID_PKIX_KP_SERVER_AUTH, +							szOID_SERVER_GATED_CRYPTO, +							szOID_SGC_NETSCAPE }; +	DWORD cUsages = sizeof(rgszUsages) / sizeof(CHAR*); + +	PWSTR   pwszServerName = NULL; +	DWORD   cchServerName; +	DWORD   Status; + +	if(pServerCert == NULL) +	{ +		Status = SEC_E_WRONG_PRINCIPAL; +		goto cleanup; +	} + +	if(host == NULL || wcslen(host) == 0) { +		Status = SEC_E_WRONG_PRINCIPAL; +		goto cleanup; +	} + +	// Build certificate chain. + +	ZeroMemory(&ChainPara, sizeof(ChainPara)); +	ChainPara.cbSize = sizeof(ChainPara); +	ChainPara.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR; +	ChainPara.RequestedUsage.Usage.cUsageIdentifier     = cUsages; +	ChainPara.RequestedUsage.Usage.rgpszUsageIdentifier = rgszUsages; + +	if(!CertGetCertificateChain(NULL, pServerCert, NULL, pServerCert->hCertStore, &ChainPara, 0, NULL, &pChainContext)) { +		Status = GetLastError(); +		wprintf(L"Error 0x%x returned by CertGetCertificateChain!\n", Status); +		goto cleanup; +	} + +	// Validate certificate chain. +	ZeroMemory(&polHttps, sizeof(HTTPSPolicyCallbackData)); +	polHttps.cbStruct           = sizeof(HTTPSPolicyCallbackData); +	polHttps.dwAuthType         = AUTHTYPE_SERVER; +	polHttps.fdwChecks          = dwCertFlags; +	polHttps.pwszServerName     = host; + +	memset(&PolicyPara, 0, sizeof(PolicyPara)); +	PolicyPara.cbSize            = sizeof(PolicyPara); +	PolicyPara.pvExtraPolicyPara = &polHttps; + +	memset(&PolicyStatus, 0, sizeof(PolicyStatus)); +	PolicyStatus.cbSize = sizeof(PolicyStatus); + +	if(!CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_SSL, pChainContext, &PolicyPara, &PolicyStatus)){ +		Status = GetLastError(); +		wprintf(L"Error 0x%x returned by CertVerifyCertificateChainPolicy!\n", Status); +		goto cleanup; +	} + +	if(PolicyStatus.dwError) { +		Status = PolicyStatus.dwError; +		goto cleanup; +	} + + +	Status = SEC_E_OK; + +cleanup: + +	if(pChainContext) +	{ +		CertFreeCertificateChain(pChainContext); +	} + +	if(pwszServerName) +	{ +		LocalFree(pwszServerName); +	} + +	return Status; +} + + +static void get_new_client_credentials(TlsContext *tls_ctx) { +	CredHandle hCreds; +	SecPkgContext_IssuerListInfoEx IssuerListInfo; +	PCCERT_CHAIN_CONTEXT pChainContext; +	CERT_CHAIN_FIND_BY_ISSUER_PARA FindByIssuerPara; +	PCCERT_CONTEXT  pCertContext; +	TimeStamp       tsExpiry; +	SECURITY_STATUS Status; + +	// Read list of trusted issuers from schannel. +	Status = tls_ctx->sspi->QueryContextAttributes(&tls_ctx->h_context, SECPKG_ATTR_ISSUER_LIST_EX, (PVOID)&IssuerListInfo); +	if(Status != SEC_E_OK) { +		wprintf(L"Error 0x%x querying issuer list info\n", Status); +		return; +	} + +	// Enumerate the client certificates. + +	ZeroMemory(&FindByIssuerPara, sizeof(FindByIssuerPara)); + +	FindByIssuerPara.cbSize = sizeof(FindByIssuerPara); +	FindByIssuerPara.pszUsageIdentifier = szOID_PKIX_KP_CLIENT_AUTH; +	FindByIssuerPara.dwKeySpec = 0; +	FindByIssuerPara.cIssuer   = IssuerListInfo.cIssuers; +	FindByIssuerPara.rgIssuer  = IssuerListInfo.aIssuers; + +	pChainContext = NULL; + +	while(TRUE) { +		// Find a certificate chain. +		pChainContext = CertFindChainInStore(tls_ctx->cert_store, +											 X509_ASN_ENCODING, +											 0, +											 CERT_CHAIN_FIND_BY_ISSUER, +											 &FindByIssuerPara, +											 pChainContext); +		if(pChainContext == NULL) { +			wprintf(L"Error 0x%x finding cert chain\n", GetLastError()); +			break; +		} + +		// Get pointer to leaf certificate context. +		pCertContext = pChainContext->rgpChain[0]->rgpElement[0]->pCertContext; + +		// Create schannel credential. +		tls_ctx->schannel_cred.dwVersion = SCHANNEL_CRED_VERSION; +		tls_ctx->schannel_cred.cCreds = 1; +		tls_ctx->schannel_cred.paCred = &pCertContext; + +		Status = tls_ctx->sspi->AcquireCredentialsHandle( +							NULL,                   // Name of principal +							UNISP_NAME_W,           // Name of package +							SECPKG_CRED_OUTBOUND,   // Flags indicating use +							NULL,                   // Pointer to logon ID +							&tls_ctx->schannel_cred,          // Package specific data +							NULL,                   // Pointer to GetKey() func +							NULL,                   // Value to pass to GetKey() +							&hCreds,                // (out) Cred Handle +							&tsExpiry);             // (out) Lifetime (optional) +		if(Status != SEC_E_OK) { +			wprintf(L"Error 0x%x returned by AcquireCredentialsHandle\n", Status); +			continue; +		} + +		// Destroy the old credentials. +		tls_ctx->sspi->FreeCredentialsHandle(&tls_ctx->h_client_creds); + +		tls_ctx->h_client_creds = hCreds; + +		// +		// As you can see, this sample code maintains a single credential +		// handle, replacing it as necessary. This is a little unusual. +		// +		// Many applications maintain a global credential handle that's +		// anonymous (that is, it doesn't contain a client certificate), +		// which is used to connect to all servers. If a particular server +		// should require client authentication, then a new credential  +		// is created for use when connecting to that server. The global +		// anonymous credential is retained for future connections to +		// other servers. +		// +		// Maintaining a single anonymous credential that's used whenever +		// possible is most efficient, since creating new credentials all +		// the time is rather expensive. +		// + +		break; +	} +} diff --git a/v_windows/v/old/thirdparty/vschannel/vschannel.h b/v_windows/v/old/thirdparty/vschannel/vschannel.h new file mode 100644 index 0000000..0338645 --- /dev/null +++ b/v_windows/v/old/thirdparty/vschannel/vschannel.h @@ -0,0 +1,47 @@ +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <windows.h> +#include <winsock.h> +#include <wincrypt.h> +#include <wintrust.h> +#include <schannel.h> + +#define SECURITY_WIN32 +#include <security.h> +#include <sspi.h> + +#define vsc_init_resp_buff_size 44000 + +#define IO_BUFFER_SIZE  0x10000 + +#define TLS_MAX_BUFSIZ      32768 + +// Define here to be sure +#define SP_PROT_TLS1_2_CLIENT 0x00000800 + +typedef struct TlsContext TlsContext; + +TlsContext new_tls_context(); + +static void vschannel_init(TlsContext *tls_ctx); + +static void vschannel_cleanup(TlsContext *tls_ctx); + +static INT request(TlsContext *tls_ctx, INT iport, LPWSTR host, CHAR *req, CHAR **out); + +static SECURITY_STATUS https_make_request(TlsContext *tls_ctx, CHAR *req, CHAR **out, int *length); + +static INT connect_to_server(TlsContext *tls_ctx, LPWSTR host, INT port_number); + +static LONG disconnect_from_server(TlsContext *tls_ctx); + +static SECURITY_STATUS perform_client_handshake(TlsContext *tls_ctx, LPWSTR host, SecBuffer *pExtraData); + +static SECURITY_STATUS client_handshake_loop(TlsContext *tls_ctx, BOOL fDoInitialRead, SecBuffer *pExtraData); + +static DWORD verify_server_certificate(PCCERT_CONTEXT  pServerCert, LPWSTR host, DWORD dwCertFlags); + +static SECURITY_STATUS create_credentials(TlsContext *tls_ctx); + +static void get_new_client_credentials(TlsContext *tls_ctx);  | 
