1 | #include <vschannel.h> |
2 | #include <sspi.h> |
3 | |
4 | // Proxy |
5 | WCHAR * psz_proxy_server = L"proxy"; |
6 | INT i_proxy_port = 80; |
7 | |
8 | // Options |
9 | INT port_number = 443; |
10 | BOOL use_proxy = FALSE; |
11 | DWORD protocol = 0; |
12 | ALG_ID aid_key_exch = 0; |
13 | |
14 | // TODO: joe-c |
15 | // socket / tls ctx |
16 | struct 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 | |
31 | TlsContext 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 | |
41 | void 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 | |
73 | void 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 | |
90 | INT 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 | |
171 | static 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 | |
247 | cleanup: |
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 | |
260 | static 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 | |
329 | static 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 | |
405 | cleanup: |
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 | |
417 | static 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 | |
485 | static 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 | |
714 | static 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 | |
920 | static 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 | |
989 | cleanup: |
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 | |
1005 | static 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 | } |