aboutsummaryrefslogtreecommitdiff
path: root/v_windows/v/old/thirdparty/vschannel/vschannel.c
diff options
context:
space:
mode:
Diffstat (limited to 'v_windows/v/old/thirdparty/vschannel/vschannel.c')
-rw-r--r--v_windows/v/old/thirdparty/vschannel/vschannel.c1095
1 files changed, 1095 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;
+ }
+}