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