v / thirdparty / vschannel
Raw file | 1093 loc (880 sloc) | 30.61 KB | Latest commit hash 9e09b709e
1#include <vschannel.h>
2#include <sspi.h>
3
4// Proxy
5WCHAR * psz_proxy_server = L"proxy";
6INT i_proxy_port = 80;
7
8// Options
9INT port_number = 443;
10BOOL use_proxy = FALSE;
11DWORD protocol = 0;
12ALG_ID aid_key_exch = 0;
13
14// TODO: joe-c
15// socket / tls ctx
16struct TlsContext {
17 // SSPI
18 PSecurityFunctionTable sspi;
19 // Cred store
20 HCERTSTORE cert_store;
21 SCHANNEL_CRED schannel_cred;
22 // Socket
23 SOCKET socket;
24 CredHandle h_client_creds;
25 CtxtHandle h_context;
26 PCCERT_CONTEXT p_pemote_cert_context;
27 BOOL creds_initialized;
28 BOOL context_initialized;
29};
30
31TlsContext new_tls_context() {
32 return (struct TlsContext) {
33 .cert_store = NULL,
34 .socket = INVALID_SOCKET,
35 .creds_initialized = FALSE,
36 .context_initialized = FALSE,
37 .p_pemote_cert_context = NULL
38 };
39};
40
41void vschannel_cleanup(TlsContext *tls_ctx) {
42 // Free the server certificate context.
43 if(tls_ctx->p_pemote_cert_context) {
44 CertFreeCertificateContext(tls_ctx->p_pemote_cert_context);
45 tls_ctx->p_pemote_cert_context = NULL;
46 }
47
48 // Free SSPI context handle.
49 if(tls_ctx->context_initialized) {
50 tls_ctx->sspi->DeleteSecurityContext(&tls_ctx->h_context);
51 tls_ctx->context_initialized = FALSE;
52 }
53
54 // Free SSPI credentials handle.
55 if(tls_ctx->creds_initialized) {
56 tls_ctx->sspi->FreeCredentialsHandle(&tls_ctx->h_client_creds);
57 tls_ctx->creds_initialized = FALSE;
58 }
59
60 // Close socket.
61 if(tls_ctx->socket != INVALID_SOCKET) {
62 closesocket(tls_ctx->socket);
63 tls_ctx->socket = INVALID_SOCKET;
64 }
65
66 // Close "MY" certificate store.
67 if(tls_ctx->cert_store) {
68 CertCloseStore(tls_ctx->cert_store, 0);
69 tls_ctx->cert_store = NULL;
70 }
71}
72
73void vschannel_init(TlsContext *tls_ctx) {
74 tls_ctx->sspi = InitSecurityInterface();
75
76 if(tls_ctx->sspi == NULL) {
77 wprintf(L"Error 0x%x reading security interface.\n",
78 GetLastError());
79 vschannel_cleanup(tls_ctx);
80 }
81
82 // Create credentials.
83 if(create_credentials(tls_ctx)) {
84 wprintf(L"Error creating credentials\n");
85 vschannel_cleanup(tls_ctx);
86 }
87 tls_ctx->creds_initialized = TRUE;
88}
89
90INT request(TlsContext *tls_ctx, INT iport, LPWSTR host, CHAR *req, DWORD req_len, CHAR **out)
91{
92 SecBuffer ExtraData;
93 SECURITY_STATUS Status;
94
95 INT i;
96 INT iOption;
97 PCHAR pszOption;
98
99 INT resp_length = 0;
100
101 protocol = SP_PROT_TLS1_2_CLIENT;
102
103 port_number = iport;
104
105 // Connect to server.
106 if(connect_to_server(tls_ctx, host, port_number)) {
107 wprintf(L"Error connecting to server\n");
108 vschannel_cleanup(tls_ctx);
109 return resp_length;
110 }
111
112 // Perform handshake
113 if(perform_client_handshake(tls_ctx, host, &ExtraData)) {
114 wprintf(L"Error performing handshake\n");
115 vschannel_cleanup(tls_ctx);
116 return resp_length;
117 }
118 tls_ctx->context_initialized = TRUE;
119
120 // Authenticate server's credentials.
121
122 // Get server's certificate.
123 Status = tls_ctx->sspi->QueryContextAttributes(&tls_ctx->h_context,
124 SECPKG_ATTR_REMOTE_CERT_CONTEXT,
125 (PVOID)&tls_ctx->p_pemote_cert_context);
126 if(Status != SEC_E_OK) {
127 wprintf(L"Error 0x%x querying remote certificate\n", Status);
128 vschannel_cleanup(tls_ctx);
129 return resp_length;
130 }
131
132 // Attempt to validate server certificate.
133 Status = verify_server_certificate(tls_ctx->p_pemote_cert_context, host,0);
134 if(Status) {
135 // The server certificate did not validate correctly. At this
136 // point, we cannot tell if we are connecting to the correct
137 // server, or if we are connecting to a "man in the middle"
138 // attack server.
139
140 // It is therefore best if we abort the connection.
141
142 wprintf(L"Error 0x%x authenticating server credentials!\n", Status);
143 vschannel_cleanup(tls_ctx);
144 return resp_length;
145 }
146
147 // Free the server certificate context.
148 CertFreeCertificateContext(tls_ctx->p_pemote_cert_context);
149 tls_ctx->p_pemote_cert_context = NULL;
150
151 // Request from server
152 if(https_make_request(tls_ctx, req, req_len, out, &resp_length)) {
153 vschannel_cleanup(tls_ctx);
154 return resp_length;
155 }
156
157 // Send a close_notify alert to the server and
158 // close down the connection.
159 if(disconnect_from_server(tls_ctx)) {
160 wprintf(L"Error disconnecting from server\n");
161 vschannel_cleanup(tls_ctx);
162 return resp_length;
163 }
164 tls_ctx->context_initialized = FALSE;
165 tls_ctx->socket = INVALID_SOCKET;
166
167 return resp_length;
168}
169
170
171static SECURITY_STATUS create_credentials(TlsContext *tls_ctx) {
172 TimeStamp tsExpiry;
173 SECURITY_STATUS Status;
174
175 DWORD cSupportedAlgs = 0;
176 ALG_ID rgbSupportedAlgs[16];
177
178 PCCERT_CONTEXT pCertContext = NULL;
179
180 // Open the "MY" certificate store, which is where Internet Explorer
181 // stores its client certificates.
182 if(tls_ctx->cert_store == NULL) {
183 tls_ctx->cert_store = CertOpenSystemStore(0, L"MY");
184
185 if(!tls_ctx->cert_store) {
186 wprintf(L"Error 0x%x returned by CertOpenSystemStore\n",
187 GetLastError());
188 return SEC_E_NO_CREDENTIALS;
189 }
190 }
191
192 // Build Schannel credential structure. Currently, this sample only
193 // specifies the protocol to be used (and optionally the certificate,
194 // of course). Real applications may wish to specify other parameters
195 // as well.
196
197 ZeroMemory(&tls_ctx->schannel_cred, sizeof(tls_ctx->schannel_cred));
198
199 tls_ctx->schannel_cred.dwVersion = SCHANNEL_CRED_VERSION;
200 if(pCertContext)
201 {
202 tls_ctx->schannel_cred.cCreds = 1;
203 tls_ctx->schannel_cred.paCred = &pCertContext;
204 }
205
206 tls_ctx->schannel_cred.grbitEnabledProtocols = protocol;
207
208 if(aid_key_exch)
209 {
210 rgbSupportedAlgs[cSupportedAlgs++] = aid_key_exch;
211 }
212
213 if(cSupportedAlgs)
214 {
215 tls_ctx->schannel_cred.cSupportedAlgs = cSupportedAlgs;
216 tls_ctx->schannel_cred.palgSupportedAlgs = rgbSupportedAlgs;
217 }
218
219 tls_ctx->schannel_cred.dwFlags |= SCH_CRED_NO_DEFAULT_CREDS;
220
221 // The SCH_CRED_MANUAL_CRED_VALIDATION flag is specified because
222 // this sample verifies the server certificate manually.
223 // Applications that expect to run on WinNT, Win9x, or WinME
224 // should specify this flag and also manually verify the server
225 // certificate. Applications running on newer versions of Windows can
226 // leave off this flag, in which case the InitializeSecurityContext
227 // function will validate the server certificate automatically.
228 // tls_ctx->schannel_cred.dwFlags |= SCH_CRED_MANUAL_CRED_VALIDATION;
229
230 // Create an SSPI credential.
231
232 Status = tls_ctx->sspi->AcquireCredentialsHandle(
233 NULL, // Name of principal
234 UNISP_NAME_W, // Name of package
235 SECPKG_CRED_OUTBOUND, // Flags indicating use
236 NULL, // Pointer to logon ID
237 &tls_ctx->schannel_cred, // Package specific data
238 NULL, // Pointer to GetKey() func
239 NULL, // Value to pass to GetKey()
240 &tls_ctx->h_client_creds, // (out) Cred Handle
241 &tsExpiry); // (out) Lifetime (optional)
242 if(Status != SEC_E_OK) {
243 wprintf(L"Error 0x%x returned by AcquireCredentialsHandle\n", Status);
244 goto cleanup;
245 }
246
247cleanup:
248
249 // Free the certificate context. Schannel has already made its own copy.
250
251 if(pCertContext) {
252 CertFreeCertificateContext(pCertContext);
253 }
254
255
256 return Status;
257}
258
259
260static INT connect_to_server(TlsContext *tls_ctx, LPWSTR host, INT port_number) {
261 SOCKET Socket;
262
263 SOCKADDR_STORAGE local_address = { 0 };
264 SOCKADDR_STORAGE remote_address = { 0 };
265
266 DWORD local_address_length = sizeof(local_address);
267 DWORD remote_address_length = sizeof(remote_address);
268
269 struct timeval tv;
270 tv.tv_sec = 60;
271 tv.tv_usec = 0;
272
273 Socket = socket(PF_INET, SOCK_STREAM, 0);
274 if(Socket == INVALID_SOCKET) {
275 wprintf(L"Error %d creating socket\n", WSAGetLastError());
276 return WSAGetLastError();
277 }
278
279 LPWSTR connect_name = use_proxy ? psz_proxy_server : host;
280
281 WCHAR service_name[10];
282 int res = wsprintf(service_name, L"%d", port_number);
283
284 if(WSAConnectByNameW(Socket,connect_name, service_name, &local_address_length,
285 &local_address, &remote_address_length, &remote_address, &tv, NULL) == SOCKET_ERROR) {
286 wprintf(L"Error %d connecting to \"%s\" (%s)\n",
287 WSAGetLastError(),
288 connect_name,
289 service_name);
290 closesocket(Socket);
291 return WSAGetLastError();
292 }
293
294 if(use_proxy) {
295 BYTE pbMessage[200];
296 DWORD cbMessage;
297
298 // Build message for proxy server
299 strcpy(pbMessage, "CONNECT ");
300 strcat(pbMessage, host);
301 strcat(pbMessage, ":");
302 _itoa(port_number, pbMessage + strlen(pbMessage), 10);
303 strcat(pbMessage, " HTTP/1.0\r\nUser-Agent: webclient\r\n\r\n");
304 cbMessage = (DWORD)strlen(pbMessage);
305
306 // Send message to proxy server
307 if(send(Socket, pbMessage, cbMessage, 0) == SOCKET_ERROR) {
308 wprintf(L"Error %d sending message to proxy!\n", WSAGetLastError());
309 return WSAGetLastError();
310 }
311
312 // Receive message from proxy server
313 cbMessage = recv(Socket, pbMessage, 200, 0);
314 if(cbMessage == SOCKET_ERROR) {
315 wprintf(L"Error %d receiving message from proxy\n", WSAGetLastError());
316 return WSAGetLastError();
317 }
318
319 // this sample is limited but in normal use it
320 // should continue to receive until CR LF CR LF is received
321 }
322
323 tls_ctx->socket = Socket;
324
325 return SEC_E_OK;
326}
327
328
329static LONG disconnect_from_server(TlsContext *tls_ctx) {
330 DWORD dwType;
331 PBYTE pbMessage;
332 DWORD cbMessage;
333 DWORD cbData;
334
335 SecBufferDesc OutBuffer;
336 SecBuffer OutBuffers[1];
337 DWORD dwSSPIFlags;
338 DWORD dwSSPIOutFlags;
339 TimeStamp tsExpiry;
340 DWORD Status;
341
342 // Notify schannel that we are about to close the connection.
343
344 dwType = SCHANNEL_SHUTDOWN;
345
346 OutBuffers[0].pvBuffer = &dwType;
347 OutBuffers[0].BufferType = SECBUFFER_TOKEN;
348 OutBuffers[0].cbBuffer = sizeof(dwType);
349
350 OutBuffer.cBuffers = 1;
351 OutBuffer.pBuffers = OutBuffers;
352 OutBuffer.ulVersion = SECBUFFER_VERSION;
353
354 Status = tls_ctx->sspi->ApplyControlToken(&tls_ctx->h_context, &OutBuffer);
355
356 if(FAILED(Status)) {
357 wprintf(L"Error 0x%x returned by ApplyControlToken\n", Status);
358 goto cleanup;
359 }
360
361 // Build an SSL close notify message.
362
363 dwSSPIFlags = ISC_REQ_SEQUENCE_DETECT |
364 ISC_REQ_REPLAY_DETECT |
365 ISC_REQ_CONFIDENTIALITY |
366 ISC_RET_EXTENDED_ERROR |
367 ISC_REQ_ALLOCATE_MEMORY |
368 ISC_REQ_STREAM;
369
370 OutBuffers[0].pvBuffer = NULL;
371 OutBuffers[0].BufferType = SECBUFFER_TOKEN;
372 OutBuffers[0].cbBuffer = 0;
373
374 OutBuffer.cBuffers = 1;
375 OutBuffer.pBuffers = OutBuffers;
376 OutBuffer.ulVersion = SECBUFFER_VERSION;
377
378 Status = tls_ctx->sspi->InitializeSecurityContext(
379 &tls_ctx->h_client_creds, &tls_ctx->h_context, NULL, dwSSPIFlags, 0, SECURITY_NATIVE_DREP,
380 NULL, 0, &tls_ctx->h_context, &OutBuffer, &dwSSPIOutFlags, &tsExpiry);
381
382 if(FAILED(Status)) {
383 wprintf(L"Error 0x%x returned by InitializeSecurityContext\n", Status);
384 goto cleanup;
385 }
386
387 pbMessage = OutBuffers[0].pvBuffer;
388 cbMessage = OutBuffers[0].cbBuffer;
389
390 // Send the close notify message to the server.
391
392 if(pbMessage != NULL && cbMessage != 0) {
393 cbData = send(tls_ctx->socket, pbMessage, cbMessage, 0);
394 if(cbData == SOCKET_ERROR || cbData == 0) {
395 Status = WSAGetLastError();
396 wprintf(L"Error %d sending close notify\n", Status);
397 goto cleanup;
398 }
399
400 // Free output buffer.
401 tls_ctx->sspi->FreeContextBuffer(pbMessage);
402 }
403
404
405cleanup:
406
407 // Free the security context.
408 tls_ctx->sspi->DeleteSecurityContext(&tls_ctx->h_context);
409
410 // Close the socket.
411 closesocket(tls_ctx->socket);
412
413 return Status;
414}
415
416
417static SECURITY_STATUS perform_client_handshake(TlsContext *tls_ctx, WCHAR *host, SecBuffer *pExtraData) {
418 SecBufferDesc OutBuffer;
419 SecBuffer OutBuffers[1];
420 DWORD dwSSPIFlags;
421 DWORD dwSSPIOutFlags;
422 TimeStamp tsExpiry;
423 SECURITY_STATUS scRet;
424 DWORD cbData;
425
426 dwSSPIFlags = ISC_REQ_SEQUENCE_DETECT |
427 ISC_REQ_REPLAY_DETECT |
428 ISC_REQ_CONFIDENTIALITY |
429 ISC_RET_EXTENDED_ERROR |
430 ISC_REQ_ALLOCATE_MEMORY |
431 ISC_REQ_STREAM;
432
433 //
434 // Initiate a ClientHello message and generate a token.
435 //
436
437 OutBuffers[0].pvBuffer = NULL;
438 OutBuffers[0].BufferType = SECBUFFER_TOKEN;
439 OutBuffers[0].cbBuffer = 0;
440
441 OutBuffer.cBuffers = 1;
442 OutBuffer.pBuffers = OutBuffers;
443 OutBuffer.ulVersion = SECBUFFER_VERSION;
444
445 scRet = tls_ctx->sspi->InitializeSecurityContext(
446 &tls_ctx->h_client_creds,
447 NULL,
448 host,
449 dwSSPIFlags,
450 0,
451 SECURITY_NATIVE_DREP,
452 NULL,
453 0,
454 &tls_ctx->h_context,
455 &OutBuffer,
456 &dwSSPIOutFlags,
457 &tsExpiry);
458
459 if(scRet != SEC_I_CONTINUE_NEEDED)
460 {
461 wprintf(L"Error %d returned by InitializeSecurityContext (1)\n", scRet);
462 return scRet;
463 }
464
465 // Send response to server if there is one.
466 if(OutBuffers[0].cbBuffer != 0 && OutBuffers[0].pvBuffer != NULL)
467 {
468 cbData = send(tls_ctx->socket, OutBuffers[0].pvBuffer, OutBuffers[0].cbBuffer, 0);
469 if(cbData == SOCKET_ERROR || cbData == 0) {
470 wprintf(L"Error %d sending data to server (1)\n", WSAGetLastError());
471 tls_ctx->sspi->FreeContextBuffer(OutBuffers[0].pvBuffer);
472 tls_ctx->sspi->DeleteSecurityContext(&tls_ctx->h_context);
473 return SEC_E_INTERNAL_ERROR;
474 }
475
476 // Free output buffer.
477 tls_ctx->sspi->FreeContextBuffer(OutBuffers[0].pvBuffer);
478 OutBuffers[0].pvBuffer = NULL;
479 }
480
481 return client_handshake_loop(tls_ctx, TRUE, pExtraData);
482}
483
484
485static SECURITY_STATUS client_handshake_loop(TlsContext *tls_ctx, BOOL fDoInitialRead, SecBuffer *pExtraData) {
486 SecBufferDesc InBuffer;
487 SecBuffer InBuffers[2];
488 SecBufferDesc OutBuffer;
489 SecBuffer OutBuffers[1];
490 DWORD dwSSPIFlags;
491 DWORD dwSSPIOutFlags;
492 TimeStamp tsExpiry;
493 SECURITY_STATUS scRet;
494 DWORD cbData;
495
496 PUCHAR IoBuffer;
497 DWORD cbIoBuffer;
498 BOOL fDoRead;
499
500
501 dwSSPIFlags = ISC_REQ_SEQUENCE_DETECT |
502 ISC_REQ_REPLAY_DETECT |
503 ISC_REQ_CONFIDENTIALITY |
504 ISC_RET_EXTENDED_ERROR |
505 ISC_REQ_ALLOCATE_MEMORY |
506 ISC_REQ_STREAM;
507
508 //
509 // Allocate data buffer.
510 //
511
512 IoBuffer = LocalAlloc(LPTR, IO_BUFFER_SIZE);
513 if(IoBuffer == NULL)
514 {
515 wprintf(L"Out of memory (1)\n");
516 return SEC_E_INTERNAL_ERROR;
517 }
518 cbIoBuffer = 0;
519
520 fDoRead = fDoInitialRead;
521
522
523 //
524 // Loop until the handshake is finished or an error occurs.
525 //
526
527 scRet = SEC_I_CONTINUE_NEEDED;
528
529 while(scRet == SEC_I_CONTINUE_NEEDED ||
530 scRet == SEC_E_INCOMPLETE_MESSAGE ||
531 scRet == SEC_I_INCOMPLETE_CREDENTIALS) {
532
533 // Read data from server.
534 if(0 == cbIoBuffer || scRet == SEC_E_INCOMPLETE_MESSAGE) {
535 if(fDoRead) {
536 cbData = recv(tls_ctx->socket,
537 IoBuffer + cbIoBuffer,
538 IO_BUFFER_SIZE - cbIoBuffer,
539 0);
540 if(cbData == SOCKET_ERROR) {
541 wprintf(L"Error %d reading data from server\n", WSAGetLastError());
542 scRet = SEC_E_INTERNAL_ERROR;
543 break;
544 }
545 else if(cbData == 0) {
546 wprintf(L"Server unexpectedly disconnected\n");
547 scRet = SEC_E_INTERNAL_ERROR;
548 break;
549 }
550
551 cbIoBuffer += cbData;
552 }
553 else {
554 fDoRead = TRUE;
555 }
556 }
557
558 // Set up the input buffers. Buffer 0 is used to pass in data
559 // received from the server. Schannel will consume some or all
560 // of this. Leftover data (if any) will be placed in buffer 1 and
561 // given a buffer type of SECBUFFER_EXTRA.
562
563 InBuffers[0].pvBuffer = IoBuffer;
564 InBuffers[0].cbBuffer = cbIoBuffer;
565 InBuffers[0].BufferType = SECBUFFER_TOKEN;
566
567 InBuffers[1].pvBuffer = NULL;
568 InBuffers[1].cbBuffer = 0;
569 InBuffers[1].BufferType = SECBUFFER_EMPTY;
570
571 InBuffer.cBuffers = 2;
572 InBuffer.pBuffers = InBuffers;
573 InBuffer.ulVersion = SECBUFFER_VERSION;
574
575 // Set up the output buffers. These are initialized to NULL
576 // so as to make it less likely we'll attempt to free random
577 // garbage later.
578
579 OutBuffers[0].pvBuffer = NULL;
580 OutBuffers[0].BufferType= SECBUFFER_TOKEN;
581 OutBuffers[0].cbBuffer = 0;
582
583 OutBuffer.cBuffers = 1;
584 OutBuffer.pBuffers = OutBuffers;
585 OutBuffer.ulVersion = SECBUFFER_VERSION;
586
587 // Call InitializeSecurityContext.
588
589 scRet = tls_ctx->sspi->InitializeSecurityContext(
590 &tls_ctx->h_client_creds, &tls_ctx->h_context, NULL, dwSSPIFlags, 0, SECURITY_NATIVE_DREP,
591 &InBuffer, 0, NULL, &OutBuffer, &dwSSPIOutFlags, &tsExpiry);
592
593 // If InitializeSecurityContext was successful (or if the error was
594 // one of the special extended ones), send the contends of the output
595 // buffer to the server.
596
597 if(scRet == SEC_E_OK ||
598 scRet == SEC_I_CONTINUE_NEEDED ||
599 FAILED(scRet) && (dwSSPIOutFlags & ISC_RET_EXTENDED_ERROR)) {
600 if(OutBuffers[0].cbBuffer != 0 && OutBuffers[0].pvBuffer != NULL) {
601 cbData = send(tls_ctx->socket,
602 OutBuffers[0].pvBuffer,
603 OutBuffers[0].cbBuffer,
604 0);
605 if(cbData == SOCKET_ERROR || cbData == 0) {
606 wprintf(L"Error %d sending data to server (2)\n",
607 WSAGetLastError());
608 tls_ctx->sspi->FreeContextBuffer(OutBuffers[0].pvBuffer);
609 tls_ctx->sspi->DeleteSecurityContext(&tls_ctx->h_context);
610 return SEC_E_INTERNAL_ERROR;
611 }
612
613 // Free output buffer.
614 tls_ctx->sspi->FreeContextBuffer(OutBuffers[0].pvBuffer);
615 OutBuffers[0].pvBuffer = NULL;
616 }
617 }
618
619 // If InitializeSecurityContext returned SEC_E_INCOMPLETE_MESSAGE,
620 // then we need to read more data from the server and try again.
621 if(scRet == SEC_E_INCOMPLETE_MESSAGE) {
622 continue;
623 }
624
625 // If InitializeSecurityContext returned SEC_E_OK, then the
626 // handshake completed successfully.
627
628 if(scRet == SEC_E_OK) {
629 // If the "extra" buffer contains data, this is encrypted application
630 // protocol layer stuff. It needs to be saved. The application layer
631 // will later decrypt it with DecryptMessage.
632
633 if(InBuffers[1].BufferType == SECBUFFER_EXTRA)
634 {
635 pExtraData->pvBuffer = LocalAlloc(LPTR, InBuffers[1].cbBuffer);
636 if(pExtraData->pvBuffer == NULL) {
637 wprintf(L"Out of memory (2)\n");
638 return SEC_E_INTERNAL_ERROR;
639 }
640
641 MoveMemory(pExtraData->pvBuffer,
642 IoBuffer + (cbIoBuffer - InBuffers[1].cbBuffer),
643 InBuffers[1].cbBuffer);
644
645 pExtraData->cbBuffer = InBuffers[1].cbBuffer;
646 pExtraData->BufferType = SECBUFFER_TOKEN;
647
648 // wprintf(L"%d bytes of app data was bundled with handshake data\n", pExtraData->cbBuffer);
649 }
650 else {
651 pExtraData->pvBuffer = NULL;
652 pExtraData->cbBuffer = 0;
653 pExtraData->BufferType = SECBUFFER_EMPTY;
654 }
655
656 // Bail out to quit
657 break;
658 }
659
660 // Check for fatal error.
661 if(FAILED(scRet)) {
662 wprintf(L"Error 0x%x returned by InitializeSecurityContext (2)\n", scRet);
663 break;
664 }
665
666 // If InitializeSecurityContext returned SEC_I_INCOMPLETE_CREDENTIALS,
667 // then the server just requested client authentication.
668 if(scRet == SEC_I_INCOMPLETE_CREDENTIALS) {
669 // Busted. The server has requested client authentication and
670 // the credential we supplied didn't contain a client certificate.
671
672 // This function will read the list of trusted certificate
673 // authorities ("issuers") that was received from the server
674 // and attempt to find a suitable client certificate that
675 // was issued by one of these. If this function is successful,
676 // then we will connect using the new certificate. Otherwise,
677 // we will attempt to connect anonymously (using our current
678 // credentials).
679
680 get_new_client_credentials(tls_ctx);
681
682 // Go around again.
683 fDoRead = FALSE;
684 scRet = SEC_I_CONTINUE_NEEDED;
685 continue;
686 }
687
688 // Copy any leftover data from the "extra" buffer, and go around
689 // again.
690
691 if ( InBuffers[1].BufferType == SECBUFFER_EXTRA ) {
692 MoveMemory(IoBuffer,
693 IoBuffer + (cbIoBuffer - InBuffers[1].cbBuffer),
694 InBuffers[1].cbBuffer);
695
696 cbIoBuffer = InBuffers[1].cbBuffer;
697 }
698 else {
699 cbIoBuffer = 0;
700 }
701 }
702
703 // Delete the security context in the case of a fatal error.
704 if(FAILED(scRet)) {
705 tls_ctx->sspi->DeleteSecurityContext(&tls_ctx->h_context);
706 }
707
708 LocalFree(IoBuffer);
709
710 return scRet;
711}
712
713
714static SECURITY_STATUS https_make_request(TlsContext *tls_ctx, CHAR *req, DWORD req_len, CHAR **out, int *length) {
715 SecPkgContext_StreamSizes Sizes;
716 SECURITY_STATUS scRet;
717 SecBufferDesc Message;
718 SecBuffer Buffers[4];
719 SecBuffer *pDataBuffer;
720 SecBuffer *pExtraBuffer;
721 SecBuffer ExtraBuffer;
722
723 PBYTE pbIoBuffer;
724 DWORD cbIoBuffer;
725 DWORD cbIoBufferLength;
726 PBYTE pbMessage;
727 DWORD cbMessage;
728
729 DWORD cbData;
730 INT i;
731
732
733 // Read stream encryption properties.
734 scRet = tls_ctx->sspi->QueryContextAttributes(&tls_ctx->h_context, SECPKG_ATTR_STREAM_SIZES, &Sizes);
735 if(scRet != SEC_E_OK) {
736 wprintf(L"Error 0x%x reading SECPKG_ATTR_STREAM_SIZES\n", scRet);
737 return scRet;
738 }
739
740 // Allocate a working buffer. The plaintext sent to EncryptMessage
741 // should never be more than 'Sizes.cbMaximumMessage', so a buffer
742 // size of this plus the header and trailer sizes should be safe enough.
743 cbIoBufferLength = Sizes.cbHeader + Sizes.cbMaximumMessage + Sizes.cbTrailer;
744
745 pbIoBuffer = LocalAlloc(LPTR, cbIoBufferLength);
746 if(pbIoBuffer == NULL) {
747 wprintf(L"Out of memory (2)\n");
748 return SEC_E_INTERNAL_ERROR;
749 }
750
751 // Build an HTTP request to send to the server.
752
753 // Build the HTTP request offset into the data buffer by "header size"
754 // bytes. This enables Schannel to perform the encryption in place,
755 // which is a significant performance win.
756 pbMessage = pbIoBuffer + Sizes.cbHeader;
757
758 // Build HTTP request. Note that I'm assuming that this is less than
759 // the maximum message size. If it weren't, it would have to be broken up.
760 memcpy(pbMessage, req, req_len);
761 cbMessage = req_len;
762
763 // Encrypt the HTTP request.
764 Buffers[0].pvBuffer = pbIoBuffer;
765 Buffers[0].cbBuffer = Sizes.cbHeader;
766 Buffers[0].BufferType = SECBUFFER_STREAM_HEADER;
767
768 Buffers[1].pvBuffer = pbMessage;
769 Buffers[1].cbBuffer = cbMessage;
770 Buffers[1].BufferType = SECBUFFER_DATA;
771
772 Buffers[2].pvBuffer = pbMessage + cbMessage;
773 Buffers[2].cbBuffer = Sizes.cbTrailer;
774 Buffers[2].BufferType = SECBUFFER_STREAM_TRAILER;
775
776 Buffers[3].BufferType = SECBUFFER_EMPTY;
777
778 Message.ulVersion = SECBUFFER_VERSION;
779 Message.cBuffers = 4;
780 Message.pBuffers = Buffers;
781
782 scRet = tls_ctx->sspi->EncryptMessage(&tls_ctx->h_context, 0, &Message, 0);
783
784 if(FAILED(scRet)) {
785 wprintf(L"Error 0x%x returned by EncryptMessage\n", scRet);
786 return scRet;
787 }
788
789 // Send the encrypted data to the server.
790 cbData = send(tls_ctx->socket, pbIoBuffer, Buffers[0].cbBuffer + Buffers[1].cbBuffer + Buffers[2].cbBuffer, 0);
791 if(cbData == SOCKET_ERROR || cbData == 0) {
792 wprintf(L"Error %d sending data to server (3)\n", WSAGetLastError());
793 tls_ctx->sspi->DeleteSecurityContext(&tls_ctx->h_context);
794 return SEC_E_INTERNAL_ERROR;
795 }
796
797 // Read data from server until done.
798 INT buff_size = vsc_init_resp_buff_size;
799 cbIoBuffer = 0;
800 while(TRUE){
801 // Read some data.
802 if(0 == cbIoBuffer || scRet == SEC_E_INCOMPLETE_MESSAGE) {
803 cbData = recv(tls_ctx->socket, pbIoBuffer + cbIoBuffer, cbIoBufferLength - cbIoBuffer, 0);
804 if(cbData == SOCKET_ERROR) {
805 wprintf(L"Error %d reading data from server\n", WSAGetLastError());
806 scRet = SEC_E_INTERNAL_ERROR;
807 break;
808 }
809 else if(cbData == 0) {
810 // Server disconnected.
811 if(cbIoBuffer) {
812 wprintf(L"Server unexpectedly disconnected\n");
813 scRet = SEC_E_INTERNAL_ERROR;
814 return scRet;
815 }
816 else {
817 break;
818 }
819 }
820 else {
821 cbIoBuffer += cbData;
822 }
823 }
824
825 // Attempt to decrypt the received data.
826 Buffers[0].pvBuffer = pbIoBuffer;
827 Buffers[0].cbBuffer = cbIoBuffer;
828 Buffers[0].BufferType = SECBUFFER_DATA;
829
830 Buffers[1].BufferType = SECBUFFER_EMPTY;
831 Buffers[2].BufferType = SECBUFFER_EMPTY;
832 Buffers[3].BufferType = SECBUFFER_EMPTY;
833
834 Message.ulVersion = SECBUFFER_VERSION;
835 Message.cBuffers = 4;
836 Message.pBuffers = Buffers;
837
838 scRet = tls_ctx->sspi->DecryptMessage(&tls_ctx->h_context, &Message, 0, NULL);
839
840 if(scRet == SEC_E_INCOMPLETE_MESSAGE) {
841 // The input buffer contains only a fragment of an
842 // encrypted record. Loop around and read some more
843 // data.
844 continue;
845 }
846
847 // Server signalled end of session
848 if(scRet == SEC_I_CONTEXT_EXPIRED) {
849 break;
850 }
851
852 if( scRet != SEC_E_OK &&
853 scRet != SEC_I_RENEGOTIATE &&
854 scRet != SEC_I_CONTEXT_EXPIRED)
855 {
856 wprintf(L"Error 0x%x returned by DecryptMessage\n", scRet);
857 return scRet;
858 }
859
860 // Locate data and (optional) extra buffers.
861 pDataBuffer = NULL;
862 pExtraBuffer = NULL;
863 for(i = 1; i < 4; i++) {
864 if(pDataBuffer == NULL && Buffers[i].BufferType == SECBUFFER_DATA)
865 {
866 pDataBuffer = &Buffers[i];
867 // wprintf(L"Buffers[%d].BufferType = SECBUFFER_DATA\n",i);
868 }
869 if(pExtraBuffer == NULL && Buffers[i].BufferType == SECBUFFER_EXTRA)
870 {
871 pExtraBuffer = &Buffers[i];
872 }
873 }
874
875 // increase buffer size if we need
876 int required_length = *length+(int)pDataBuffer->cbBuffer;
877 if( required_length > buff_size ) {
878 CHAR *a = VSCHANNEL_REALLOC(*out, required_length);
879 if( a == NULL ) {
880 scRet = SEC_E_INTERNAL_ERROR;
881 return scRet;
882 }
883 *out = a;
884 buff_size = required_length;
885 }
886 // Copy the decrypted data to our output buffer
887 memcpy(*out+*length, pDataBuffer->pvBuffer, (int)pDataBuffer->cbBuffer);
888 *length += (int)pDataBuffer->cbBuffer;
889
890 // Move any "extra" data to the input buffer.
891 if(pExtraBuffer) {
892 MoveMemory(pbIoBuffer, pExtraBuffer->pvBuffer, pExtraBuffer->cbBuffer);
893 cbIoBuffer = pExtraBuffer->cbBuffer;
894 }
895 else {
896 cbIoBuffer = 0;
897 }
898
899 if(scRet == SEC_I_RENEGOTIATE)
900 {
901 // The server wants to perform another handshake sequence.
902 scRet = client_handshake_loop(tls_ctx, FALSE, &ExtraBuffer);
903 if(scRet != SEC_E_OK) {
904 return scRet;
905 }
906
907 // Move any "extra" data to the input buffer.
908 if(ExtraBuffer.pvBuffer)
909 {
910 MoveMemory(pbIoBuffer, ExtraBuffer.pvBuffer, ExtraBuffer.cbBuffer);
911 cbIoBuffer = ExtraBuffer.cbBuffer;
912 }
913 }
914 }
915
916 return SEC_E_OK;
917}
918
919
920static DWORD verify_server_certificate( PCCERT_CONTEXT pServerCert, LPWSTR host, DWORD dwCertFlags) {
921 HTTPSPolicyCallbackData polHttps;
922 CERT_CHAIN_POLICY_PARA PolicyPara;
923 CERT_CHAIN_POLICY_STATUS PolicyStatus;
924 CERT_CHAIN_PARA ChainPara;
925 PCCERT_CHAIN_CONTEXT pChainContext = NULL;
926
927 CHAR *rgszUsages[] = { szOID_PKIX_KP_SERVER_AUTH,
928 szOID_SERVER_GATED_CRYPTO,
929 szOID_SGC_NETSCAPE };
930 DWORD cUsages = sizeof(rgszUsages) / sizeof(CHAR*);
931
932 PWSTR pwszServerName = NULL;
933 DWORD cchServerName;
934 DWORD Status;
935
936 if(pServerCert == NULL)
937 {
938 Status = SEC_E_WRONG_PRINCIPAL;
939 goto cleanup;
940 }
941
942 if(host == NULL || wcslen(host) == 0) {
943 Status = SEC_E_WRONG_PRINCIPAL;
944 goto cleanup;
945 }
946
947 // Build certificate chain.
948
949 ZeroMemory(&ChainPara, sizeof(ChainPara));
950 ChainPara.cbSize = sizeof(ChainPara);
951 ChainPara.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR;
952 ChainPara.RequestedUsage.Usage.cUsageIdentifier = cUsages;
953 ChainPara.RequestedUsage.Usage.rgpszUsageIdentifier = rgszUsages;
954
955 if(!CertGetCertificateChain(NULL, pServerCert, NULL, pServerCert->hCertStore, &ChainPara, 0, NULL, &pChainContext)) {
956 Status = GetLastError();
957 wprintf(L"Error 0x%x returned by CertGetCertificateChain!\n", Status);
958 goto cleanup;
959 }
960
961 // Validate certificate chain.
962 ZeroMemory(&polHttps, sizeof(HTTPSPolicyCallbackData));
963 polHttps.cbStruct = sizeof(HTTPSPolicyCallbackData);
964 polHttps.dwAuthType = AUTHTYPE_SERVER;
965 polHttps.fdwChecks = dwCertFlags;
966 polHttps.pwszServerName = host;
967
968 memset(&PolicyPara, 0, sizeof(PolicyPara));
969 PolicyPara.cbSize = sizeof(PolicyPara);
970 PolicyPara.pvExtraPolicyPara = &polHttps;
971
972 memset(&PolicyStatus, 0, sizeof(PolicyStatus));
973 PolicyStatus.cbSize = sizeof(PolicyStatus);
974
975 if(!CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_SSL, pChainContext, &PolicyPara, &PolicyStatus)){
976 Status = GetLastError();
977 wprintf(L"Error 0x%x returned by CertVerifyCertificateChainPolicy!\n", Status);
978 goto cleanup;
979 }
980
981 if(PolicyStatus.dwError) {
982 Status = PolicyStatus.dwError;
983 goto cleanup;
984 }
985
986
987 Status = SEC_E_OK;
988
989cleanup:
990
991 if(pChainContext)
992 {
993 CertFreeCertificateChain(pChainContext);
994 }
995
996 if(pwszServerName)
997 {
998 LocalFree(pwszServerName);
999 }
1000
1001 return Status;
1002}
1003
1004
1005static void get_new_client_credentials(TlsContext *tls_ctx) {
1006 CredHandle hCreds;
1007 SecPkgContext_IssuerListInfoEx IssuerListInfo;
1008 PCCERT_CHAIN_CONTEXT pChainContext;
1009 CERT_CHAIN_FIND_BY_ISSUER_PARA FindByIssuerPara;
1010 PCCERT_CONTEXT pCertContext;
1011 TimeStamp tsExpiry;
1012 SECURITY_STATUS Status;
1013
1014 // Read list of trusted issuers from schannel.
1015 Status = tls_ctx->sspi->QueryContextAttributes(&tls_ctx->h_context, SECPKG_ATTR_ISSUER_LIST_EX, (PVOID)&IssuerListInfo);
1016 if(Status != SEC_E_OK) {
1017 wprintf(L"Error 0x%x querying issuer list info\n", Status);
1018 return;
1019 }
1020
1021 // Enumerate the client certificates.
1022
1023 ZeroMemory(&FindByIssuerPara, sizeof(FindByIssuerPara));
1024
1025 FindByIssuerPara.cbSize = sizeof(FindByIssuerPara);
1026 FindByIssuerPara.pszUsageIdentifier = szOID_PKIX_KP_CLIENT_AUTH;
1027 FindByIssuerPara.dwKeySpec = 0;
1028 FindByIssuerPara.cIssuer = IssuerListInfo.cIssuers;
1029 FindByIssuerPara.rgIssuer = IssuerListInfo.aIssuers;
1030
1031 pChainContext = NULL;
1032
1033 while(TRUE) {
1034 // Find a certificate chain.
1035 pChainContext = CertFindChainInStore(tls_ctx->cert_store,
1036 X509_ASN_ENCODING,
1037 0,
1038 CERT_CHAIN_FIND_BY_ISSUER,
1039 &FindByIssuerPara,
1040 pChainContext);
1041 if(pChainContext == NULL) {
1042 wprintf(L"Error 0x%x finding cert chain\n", GetLastError());
1043 break;
1044 }
1045
1046 // Get pointer to leaf certificate context.
1047 pCertContext = pChainContext->rgpChain[0]->rgpElement[0]->pCertContext;
1048
1049 // Create schannel credential.
1050 tls_ctx->schannel_cred.dwVersion = SCHANNEL_CRED_VERSION;
1051 tls_ctx->schannel_cred.cCreds = 1;
1052 tls_ctx->schannel_cred.paCred = &pCertContext;
1053
1054 Status = tls_ctx->sspi->AcquireCredentialsHandle(
1055 NULL, // Name of principal
1056 UNISP_NAME_W, // Name of package
1057 SECPKG_CRED_OUTBOUND, // Flags indicating use
1058 NULL, // Pointer to logon ID
1059 &tls_ctx->schannel_cred, // Package specific data
1060 NULL, // Pointer to GetKey() func
1061 NULL, // Value to pass to GetKey()
1062 &hCreds, // (out) Cred Handle
1063 &tsExpiry); // (out) Lifetime (optional)
1064 if(Status != SEC_E_OK) {
1065 wprintf(L"Error 0x%x returned by AcquireCredentialsHandle\n", Status);
1066 continue;
1067 }
1068
1069 // Destroy the old credentials.
1070 tls_ctx->sspi->FreeCredentialsHandle(&tls_ctx->h_client_creds);
1071
1072 tls_ctx->h_client_creds = hCreds;
1073
1074 //
1075 // As you can see, this sample code maintains a single credential
1076 // handle, replacing it as necessary. This is a little unusual.
1077 //
1078 // Many applications maintain a global credential handle that's
1079 // anonymous (that is, it doesn't contain a client certificate),
1080 // which is used to connect to all servers. If a particular server
1081 // should require client authentication, then a new credential
1082 // is created for use when connecting to that server. The global
1083 // anonymous credential is retained for future connections to
1084 // other servers.
1085 //
1086 // Maintaining a single anonymous credential that's used whenever
1087 // possible is most efficient, since creating new credentials all
1088 // the time is rather expensive.
1089 //
1090
1091 break;
1092 }
1093}