v / thirdparty / vschannel / vschannel.c
1756 lines · 1487 sloc · 55.41 KB · 5d739b1b1ac7ec2625d864cbb17d758c6c92f8bc
Raw
1#include <vschannel.h>
2#include <sspi.h>
3
4// ALPN (RFC 7301) compatibility shim. Older toolchain headers (notably the
5// ones bundled with tcc) predate the SChannel ALPN additions, so the structs,
6// enums and constants below are missing there. Define them ourselves when the
7// SDK headers did not. SECPKG_ATTR_APPLICATION_PROTOCOL guards the schannel.h
8// types; SECBUFFER_APPLICATION_PROTOCOLS guards the sspi.h buffer constant.
9#ifndef ANYSIZE_ARRAY
10#define ANYSIZE_ARRAY 1
11#endif
12
13#ifndef SECPKG_ATTR_APPLICATION_PROTOCOL
14#define SECPKG_ATTR_APPLICATION_PROTOCOL 35
15
16typedef enum _SEC_APPLICATION_PROTOCOL_NEGOTIATION_EXT {
17 SecApplicationProtocolNegotiationExt_None,
18 SecApplicationProtocolNegotiationExt_NPN,
19 SecApplicationProtocolNegotiationExt_ALPN
20} SEC_APPLICATION_PROTOCOL_NEGOTIATION_EXT, *PSEC_APPLICATION_PROTOCOL_NEGOTIATION_EXT;
21
22typedef struct _SEC_APPLICATION_PROTOCOL_LIST {
23 SEC_APPLICATION_PROTOCOL_NEGOTIATION_EXT ProtoNegoExt;
24 unsigned short ProtocolListSize;
25 unsigned char ProtocolList[ANYSIZE_ARRAY];
26} SEC_APPLICATION_PROTOCOL_LIST, *PSEC_APPLICATION_PROTOCOL_LIST;
27
28typedef struct _SEC_APPLICATION_PROTOCOLS {
29 unsigned long ProtocolListsSize;
30 SEC_APPLICATION_PROTOCOL_LIST ProtocolLists[ANYSIZE_ARRAY];
31} SEC_APPLICATION_PROTOCOLS, *PSEC_APPLICATION_PROTOCOLS;
32
33typedef enum _SEC_APPLICATION_PROTOCOL_NEGOTIATION_STATUS {
34 SecApplicationProtocolNegotiationStatus_None,
35 SecApplicationProtocolNegotiationStatus_Success,
36 SecApplicationProtocolNegotiationStatus_SelectedClientOnly
37} SEC_APPLICATION_PROTOCOL_NEGOTIATION_STATUS, *PSEC_APPLICATION_PROTOCOL_NEGOTIATION_STATUS;
38
39#define MAX_PROTOCOL_ID_SIZE 0xff
40
41typedef struct _SecPkgContext_ApplicationProtocol {
42 SEC_APPLICATION_PROTOCOL_NEGOTIATION_STATUS ProtoNegoStatus;
43 SEC_APPLICATION_PROTOCOL_NEGOTIATION_EXT ProtoNegoExt;
44 unsigned char ProtocolIdSize;
45 unsigned char ProtocolId[MAX_PROTOCOL_ID_SIZE];
46} SecPkgContext_ApplicationProtocol, *PSecPkgContext_ApplicationProtocol;
47#endif // SECPKG_ATTR_APPLICATION_PROTOCOL
48
49#ifndef SECBUFFER_APPLICATION_PROTOCOLS
50#define SECBUFFER_APPLICATION_PROTOCOLS 18
51#endif
52
53// Proxy
54WCHAR * psz_proxy_server = L"proxy";
55INT i_proxy_port = 80;
56
57// Options
58INT port_number = 443;
59BOOL use_proxy = FALSE;
60DWORD protocol = 0;
61ALG_ID aid_key_exch = 0;
62
63// TODO: joe-c
64// socket / tls ctx
65struct TlsContext {
66 // SSPI
67 PSecurityFunctionTable sspi;
68 // Cred store
69 HCERTSTORE cert_store;
70 SCHANNEL_CRED schannel_cred;
71 // Socket
72 SOCKET socket;
73 CredHandle h_client_creds;
74 CtxtHandle h_context;
75 PCCERT_CONTEXT p_pemote_cert_context;
76 INT last_error_code;
77 BOOL validate_server_certificate;
78 BOOL creds_initialized;
79 BOOL context_initialized;
80 // ALPN protocol list to advertise, in the standard ALPN wire format (each
81 // name 1-byte length-prefixed), e.g. "\x02h2\x08http/1.1". alpn_wire_len == 0
82 // means "do not advertise ALPN".
83 unsigned char alpn_wire[256];
84 unsigned long alpn_wire_len;
85 // Negotiated application protocol name (e.g. "h2"); negotiated_alpn_len == 0
86 // when the server selected none.
87 char negotiated_alpn[256];
88 unsigned long negotiated_alpn_len;
89 // Streaming transport state, used by the keep-the-connection-open path
90 // (vschannel_h2_connect / vschannel_write / vschannel_read) that backs the
91 // HTTP/2 driver. The one-shot request() path does not touch these.
92 SecPkgContext_StreamSizes stream_sizes; // cached TLS record sizes
93 BOOL stream_sizes_valid;
94 // Ciphertext staging buffer: bytes recv()'d from the socket that have not yet
95 // been decrypted into a full record (SEC_E_INCOMPLETE_MESSAGE) plus any
96 // trailing SECBUFFER_EXTRA from the last DecryptMessage.
97 unsigned char *recv_buf;
98 unsigned long recv_buf_cap;
99 unsigned long recv_buf_len;
100 // Decrypted plaintext carryover: a single DecryptMessage can yield more
101 // application bytes than the caller's read buffer can hold, so the remainder
102 // is stashed here and drained on the next vschannel_read().
103 unsigned char *plain_buf;
104 unsigned long plain_buf_cap;
105 unsigned long plain_buf_len; // valid decrypted bytes
106 unsigned long plain_buf_off; // bytes already returned to caller
107 // Reusable encryption buffer for vschannel_write(): one full record
108 // (header + max message + trailer). Cached so the HTTP/2 driver's many small
109 // writes do not LocalAlloc/LocalFree on every call.
110 unsigned char *send_buf;
111 unsigned long send_buf_cap;
112 BOOL stream_eof; // close_notify / context expired seen
113};
114
115TlsContext new_tls_context() {
116 return (struct TlsContext) {
117 .cert_store = NULL,
118 .last_error_code = 0,
119 .socket = INVALID_SOCKET,
120 .validate_server_certificate = TRUE,
121 .creds_initialized = FALSE,
122 .context_initialized = FALSE,
123 .p_pemote_cert_context = NULL,
124 .alpn_wire_len = 0,
125 .negotiated_alpn_len = 0,
126 .stream_sizes_valid = FALSE,
127 .recv_buf = NULL,
128 .recv_buf_cap = 0,
129 .recv_buf_len = 0,
130 .plain_buf = NULL,
131 .plain_buf_cap = 0,
132 .plain_buf_len = 0,
133 .plain_buf_off = 0,
134 .send_buf = NULL,
135 .send_buf_cap = 0,
136 .stream_eof = FALSE
137 };
138};
139
140// vschannel_alpn_supported reports whether this Windows version's SChannel
141// supports client-side ALPN, which was introduced in Windows 8.1 / Server
142// 2012 R2 (version 6.3). On older versions, passing a
143// SECBUFFER_APPLICATION_PROTOCOLS input buffer into the handshake can fail it
144// outright, so callers should skip ALPN (and HTTP/2) entirely there. Uses
145// RtlGetVersion because GetVersionEx lies on manifest-less binaries from
146// Windows 8.1 onwards.
147INT vschannel_alpn_supported() {
148 static INT cached = -1;
149 if (cached < 0) {
150 typedef LONG (WINAPI *RtlGetVersionFn)(OSVERSIONINFOW *);
151 OSVERSIONINFOW vi;
152 RtlGetVersionFn get_version = (RtlGetVersionFn)GetProcAddress(
153 GetModuleHandleW(L"ntdll.dll"), "RtlGetVersion");
154 INT supported = 0;
155 if (get_version != NULL) {
156 ZeroMemory(&vi, sizeof(vi));
157 vi.dwOSVersionInfoSize = sizeof(vi);
158 if (get_version(&vi) == 0
159 && (vi.dwMajorVersion > 6
160 || (vi.dwMajorVersion == 6 && vi.dwMinorVersion >= 3))) {
161 supported = 1;
162 }
163 }
164 cached = supported;
165 }
166 return cached;
167}
168
169// vschannel_set_alpn configures the ALPN protocol list to advertise during the
170// next handshake. `wire` is the standard ALPN wire format (each protocol name
171// preceded by a 1-byte length), e.g. "\x02h2\x08http/1.1". Passing len == 0
172// disables ALPN advertisement.
173void vschannel_set_alpn(TlsContext *tls_ctx, const char *wire, INT len) {
174 if (len < 0) {
175 len = 0;
176 }
177 if (len > (INT)sizeof(tls_ctx->alpn_wire)) {
178 len = (INT)sizeof(tls_ctx->alpn_wire);
179 }
180 if (len > 0) {
181 memcpy(tls_ctx->alpn_wire, wire, (size_t)len);
182 }
183 tls_ctx->alpn_wire_len = (unsigned long)len;
184}
185
186// vschannel_get_alpn copies the protocol the server selected via ALPN (e.g.
187// "h2") into `out` and returns its length, or 0 if none was negotiated.
188INT vschannel_get_alpn(TlsContext *tls_ctx, char *out, INT out_cap) {
189 unsigned long n = tls_ctx->negotiated_alpn_len;
190 if (out_cap < 0) {
191 out_cap = 0;
192 }
193 if (n > (unsigned long)out_cap) {
194 n = (unsigned long)out_cap;
195 }
196 if (n > 0) {
197 memcpy(out, tls_ctx->negotiated_alpn, (size_t)n);
198 }
199 return (INT)n;
200}
201
202// vschannel_capture_alpn queries the negotiated ALPN protocol from a completed
203// handshake and stores it on the context for vschannel_get_alpn().
204static void vschannel_capture_alpn(TlsContext *tls_ctx) {
205 SecPkgContext_ApplicationProtocol appproto;
206 SECURITY_STATUS st;
207
208 tls_ctx->negotiated_alpn_len = 0;
209 st = tls_ctx->sspi->QueryContextAttributes(&tls_ctx->h_context,
210 SECPKG_ATTR_APPLICATION_PROTOCOL, &appproto);
211 if (st == SEC_E_OK
212 && appproto.ProtoNegoStatus == SecApplicationProtocolNegotiationStatus_Success
213 && appproto.ProtocolIdSize > 0
214 && appproto.ProtocolIdSize <= sizeof(tls_ctx->negotiated_alpn)) {
215 memcpy(tls_ctx->negotiated_alpn, appproto.ProtocolId, appproto.ProtocolIdSize);
216 tls_ctx->negotiated_alpn_len = appproto.ProtocolIdSize;
217 }
218}
219
220static void vschannel_clear_last_error(TlsContext *tls_ctx) {
221 tls_ctx->last_error_code = 0;
222}
223
224static void vschannel_set_last_error(TlsContext *tls_ctx, INT err_code) {
225 tls_ctx->last_error_code = err_code;
226}
227
228static INT vschannel_last_error(TlsContext *tls_ctx) {
229 return tls_ctx->last_error_code;
230}
231
232void vschannel_cleanup(TlsContext *tls_ctx) {
233 // Free the server certificate context.
234 if(tls_ctx->p_pemote_cert_context) {
235 CertFreeCertificateContext(tls_ctx->p_pemote_cert_context);
236 tls_ctx->p_pemote_cert_context = NULL;
237 }
238
239 // Free SSPI context handle.
240 if(tls_ctx->context_initialized) {
241 tls_ctx->sspi->DeleteSecurityContext(&tls_ctx->h_context);
242 tls_ctx->context_initialized = FALSE;
243 }
244
245 // Free SSPI credentials handle.
246 if(tls_ctx->creds_initialized) {
247 tls_ctx->sspi->FreeCredentialsHandle(&tls_ctx->h_client_creds);
248 tls_ctx->creds_initialized = FALSE;
249 }
250
251 // Close socket.
252 if(tls_ctx->socket != INVALID_SOCKET) {
253 closesocket(tls_ctx->socket);
254 tls_ctx->socket = INVALID_SOCKET;
255 }
256
257 // Close "MY" certificate store.
258 if(tls_ctx->cert_store) {
259 CertCloseStore(tls_ctx->cert_store, 0);
260 tls_ctx->cert_store = NULL;
261 }
262
263 // Free streaming-transport buffers.
264 if(tls_ctx->recv_buf) {
265 LocalFree(tls_ctx->recv_buf);
266 tls_ctx->recv_buf = NULL;
267 }
268 tls_ctx->recv_buf_cap = 0;
269 tls_ctx->recv_buf_len = 0;
270 if(tls_ctx->plain_buf) {
271 LocalFree(tls_ctx->plain_buf);
272 tls_ctx->plain_buf = NULL;
273 }
274 tls_ctx->plain_buf_cap = 0;
275 tls_ctx->plain_buf_len = 0;
276 tls_ctx->plain_buf_off = 0;
277 if(tls_ctx->send_buf) {
278 LocalFree(tls_ctx->send_buf);
279 tls_ctx->send_buf = NULL;
280 }
281 tls_ctx->send_buf_cap = 0;
282 tls_ctx->stream_sizes_valid = FALSE;
283 tls_ctx->stream_eof = FALSE;
284}
285
286void vschannel_init(TlsContext *tls_ctx, BOOL validate_server_certificate) {
287 tls_ctx->sspi = InitSecurityInterface();
288 tls_ctx->validate_server_certificate = validate_server_certificate;
289
290 if(tls_ctx->sspi == NULL) {
291 wprintf(L"Error 0x%x reading security interface.\n",
292 GetLastError());
293 vschannel_cleanup(tls_ctx);
294 }
295
296 // Create credentials.
297 if(create_credentials(tls_ctx)) {
298 wprintf(L"Error creating credentials\n");
299 vschannel_cleanup(tls_ctx);
300 }
301 tls_ctx->creds_initialized = TRUE;
302}
303
304// vschannel_open_and_handshake performs the connection setup shared by
305// request(), vschannel_alpn_probe() and vschannel_h2_connect(): connect to
306// host:iport, run the TLS handshake (advertising any configured ALPN), record
307// the negotiated protocol, and — when verify_cert is set — validate the server
308// certificate. On success the connection is left open (context_initialized) and,
309// when pExtraData is non-NULL, it receives any application bytes the handshake
310// bundled with its final flight (the caller then owns pExtraData->pvBuffer and
311// must LocalFree it). On failure it records the error, frees any bundled extra,
312// tears the connection down, and returns a non-zero SECURITY_STATUS. A connect
313// failure already set last_error via connect_to_server.
314static SECURITY_STATUS vschannel_open_and_handshake(TlsContext *tls_ctx, INT iport, LPWSTR host, BOOL verify_cert, SecBuffer *pExtraData) {
315 SecBuffer local_extra;
316 SecBuffer *extra = pExtraData ? pExtraData : &local_extra;
317 SECURITY_STATUS Status;
318
319 extra->pvBuffer = NULL;
320 extra->cbBuffer = 0;
321
322 protocol = SP_PROT_TLS1_2_CLIENT;
323 port_number = iport;
324 vschannel_clear_last_error(tls_ctx);
325
326 if(connect_to_server(tls_ctx, host, port_number)) {
327 vschannel_cleanup(tls_ctx);
328 return SEC_E_INTERNAL_ERROR;
329 }
330
331 Status = perform_client_handshake(tls_ctx, host, extra);
332 if(Status != SEC_E_OK) {
333 vschannel_set_last_error(tls_ctx, Status);
334 goto fail;
335 }
336 tls_ctx->context_initialized = TRUE;
337
338 // Record the ALPN protocol the server selected (if any).
339 vschannel_capture_alpn(tls_ctx);
340
341 if(verify_cert) {
342 // Get and validate the server's certificate.
343 Status = tls_ctx->sspi->QueryContextAttributes(&tls_ctx->h_context,
344 SECPKG_ATTR_REMOTE_CERT_CONTEXT, (PVOID)&tls_ctx->p_pemote_cert_context);
345 if(Status != SEC_E_OK) {
346 vschannel_set_last_error(tls_ctx, Status);
347 goto fail;
348 }
349 Status = verify_server_certificate(tls_ctx->p_pemote_cert_context, host, 0);
350 if(Status != SEC_E_OK) {
351 // Could not authenticate the server (possible MITM): abort.
352 vschannel_set_last_error(tls_ctx, Status);
353 goto fail;
354 }
355 CertFreeCertificateContext(tls_ctx->p_pemote_cert_context);
356 tls_ctx->p_pemote_cert_context = NULL;
357 }
358
359 // If the caller does not want the bundled application data, drop it.
360 if(pExtraData == NULL && local_extra.pvBuffer != NULL) {
361 LocalFree(local_extra.pvBuffer);
362 }
363 return SEC_E_OK;
364
365fail:
366 if(extra->pvBuffer != NULL) {
367 LocalFree(extra->pvBuffer);
368 extra->pvBuffer = NULL;
369 }
370 vschannel_cleanup(tls_ctx);
371 return Status;
372}
373
374INT request(TlsContext *tls_ctx, INT iport, LPWSTR host, CHAR *req, DWORD req_len, CHAR **out, vschannel_allocator afn)
375{
376 SECURITY_STATUS Status;
377 INT resp_length = 0;
378
379 // Connect + handshake (+ cert validation when enabled). request() does not
380 // consume handshake-bundled application data (HTTP/1.1 servers do not send
381 // before the request), so pass NULL to have it dropped.
382 if(vschannel_open_and_handshake(tls_ctx, iport, host,
383 tls_ctx->validate_server_certificate, NULL) != SEC_E_OK) {
384 return resp_length;
385 }
386
387 // Request from server
388 Status = https_make_request(tls_ctx, req, req_len, out, &resp_length, afn);
389 if(Status) {
390 vschannel_set_last_error(tls_ctx, Status);
391 vschannel_cleanup(tls_ctx);
392 return resp_length;
393 }
394
395 // Send a close_notify alert to the server and
396 // close down the connection.
397 Status = disconnect_from_server(tls_ctx);
398 if(Status) {
399 vschannel_set_last_error(tls_ctx, Status);
400 wprintf(L"Error disconnecting from server\n");
401 vschannel_cleanup(tls_ctx);
402 return resp_length;
403 }
404 tls_ctx->context_initialized = FALSE;
405 tls_ctx->socket = INVALID_SOCKET;
406
407 return resp_length;
408}
409
410// vschannel_request_on_open runs a one-shot HTTP/1.1 request over a connection
411// that vschannel_h2_connect() already opened and handshaked, then closes it.
412// It is the HTTP/1.1 fallback used when a server, asked for ALPN `h2`, does not
413// select it: rather than reconnect, we reuse the open connection. Returns the
414// response length (see request()).
415INT vschannel_request_on_open(TlsContext *tls_ctx, CHAR *req, DWORD req_len, CHAR **out, vschannel_allocator afn) {
416 SECURITY_STATUS Status;
417 INT resp_length = 0;
418
419 Status = https_make_request(tls_ctx, req, req_len, out, &resp_length, afn);
420 if(Status) {
421 vschannel_set_last_error(tls_ctx, Status);
422 vschannel_cleanup(tls_ctx);
423 return resp_length;
424 }
425
426 Status = disconnect_from_server(tls_ctx);
427 if(Status) {
428 vschannel_set_last_error(tls_ctx, Status);
429 vschannel_cleanup(tls_ctx);
430 return resp_length;
431 }
432 tls_ctx->context_initialized = FALSE;
433 tls_ctx->socket = INVALID_SOCKET;
434
435 return resp_length;
436}
437
438// vschannel_alpn_probe connects to host:iport, performs the TLS handshake while
439// advertising whatever ALPN list was configured via vschannel_set_alpn(),
440// captures the protocol the server selected into `out` (up to out_cap bytes),
441// and disconnects without sending an application request. Returns the
442// negotiated protocol length (0 = handshake succeeded but no protocol selected),
443// or -1 on connect/handshake failure (see vschannel_last_error). Intended for
444// tests and capability checks, since request() only speaks HTTP/1.1.
445INT vschannel_alpn_probe(TlsContext *tls_ctx, INT iport, LPWSTR host, char *out, INT out_cap) {
446 // Probe only: handshake (no cert validation, no application data) then close.
447 if(vschannel_open_and_handshake(tls_ctx, iport, host, FALSE, NULL) != SEC_E_OK) {
448 return -1;
449 }
450
451 disconnect_from_server(tls_ctx);
452 tls_ctx->context_initialized = FALSE;
453 tls_ctx->socket = INVALID_SOCKET;
454
455 return vschannel_get_alpn(tls_ctx, out, out_cap);
456}
457
458// ---------------------------------------------------------------------------
459// Streaming transport (keep the TLS connection open and exchange raw bytes).
460//
461// request() above is a one-shot: connect, handshake, send the whole HTTP/1.1
462// request, read the whole response, disconnect. An HTTP/2 driver instead needs
463// a long-lived, byte-oriented transport. The functions below expose exactly
464// that: vschannel_h2_connect() opens the connection (handshake + cert check)
465// and leaves it open, vschannel_write()/vschannel_read() move application bytes
466// across it, and vschannel_h2_close() shuts it down. They reuse the same SSPI
467// primitives as request()/https_make_request(), but keep the encrypt/decrypt
468// state on the TlsContext so reads can span calls.
469// ---------------------------------------------------------------------------
470
471// vschannel_ensure_stream_state caches the negotiated TLS record sizes and
472// allocates the ciphertext/plaintext working buffers, once per connection.
473static SECURITY_STATUS vschannel_ensure_stream_state(TlsContext *tls_ctx) {
474 if(tls_ctx->stream_sizes_valid) {
475 return SEC_E_OK;
476 }
477 SECURITY_STATUS scRet = tls_ctx->sspi->QueryContextAttributes(&tls_ctx->h_context,
478 SECPKG_ATTR_STREAM_SIZES, &tls_ctx->stream_sizes);
479 if(scRet != SEC_E_OK) {
480 return scRet;
481 }
482 // One full wire record: header + max plaintext + trailer. recv() never needs
483 // more than this buffered to complete a single record; trailing bytes of the
484 // next record are carried as SECBUFFER_EXTRA.
485 tls_ctx->recv_buf_cap = tls_ctx->stream_sizes.cbHeader
486 + tls_ctx->stream_sizes.cbMaximumMessage + tls_ctx->stream_sizes.cbTrailer;
487 tls_ctx->recv_buf = (unsigned char *)LocalAlloc(LPTR, tls_ctx->recv_buf_cap);
488 // One record decrypts to at most cbMaximumMessage plaintext bytes.
489 tls_ctx->plain_buf_cap = tls_ctx->stream_sizes.cbMaximumMessage;
490 tls_ctx->plain_buf = (unsigned char *)LocalAlloc(LPTR, tls_ctx->plain_buf_cap);
491 // Reusable send buffer: one full outgoing record.
492 tls_ctx->send_buf_cap = tls_ctx->recv_buf_cap;
493 tls_ctx->send_buf = (unsigned char *)LocalAlloc(LPTR, tls_ctx->send_buf_cap);
494 if(tls_ctx->recv_buf == NULL || tls_ctx->plain_buf == NULL || tls_ctx->send_buf == NULL) {
495 return SEC_E_INTERNAL_ERROR;
496 }
497 tls_ctx->recv_buf_len = 0;
498 tls_ctx->plain_buf_len = 0;
499 tls_ctx->plain_buf_off = 0;
500 tls_ctx->stream_sizes_valid = TRUE;
501 return SEC_E_OK;
502}
503
504// vschannel_h2_connect connects to host:iport, performs the TLS handshake while
505// advertising whatever ALPN list was set via vschannel_set_alpn(), validates the
506// server certificate (when enabled), and leaves the connection open for
507// vschannel_write()/vschannel_read(). Returns 0 on success, non-zero on failure
508// (see vschannel_last_error). The negotiated protocol is available afterwards
509// via vschannel_get_alpn().
510INT vschannel_h2_connect(TlsContext *tls_ctx, INT iport, LPWSTR host) {
511 SecBuffer ExtraData;
512 SECURITY_STATUS Status;
513
514 // Connect + handshake + cert validation (when enabled). Unlike the one-shot
515 // paths, keep the handshake-bundled application data: for HTTP/2 it is the
516 // server's first record (SETTINGS), needed below.
517 if(vschannel_open_and_handshake(tls_ctx, iport, host,
518 tls_ctx->validate_server_certificate, &ExtraData) != SEC_E_OK) {
519 return -1;
520 }
521
522 Status = vschannel_ensure_stream_state(tls_ctx);
523 if(Status != SEC_E_OK) {
524 vschannel_set_last_error(tls_ctx, Status);
525 if(ExtraData.pvBuffer != NULL) {
526 LocalFree(ExtraData.pvBuffer);
527 }
528 vschannel_cleanup(tls_ctx);
529 return -1;
530 }
531
532 // The final handshake flight often arrives in the same TCP segment as the
533 // server's first application record (for HTTP/2, the SETTINGS frame), which
534 // the handshake hands back as SECBUFFER_EXTRA. Those bytes are already off
535 // the socket, so they must be carried into the read buffer; otherwise the
536 // first vschannel_read() would skip them and H2Conn would desync. Grow the
537 // staging buffer if the bundled data exceeds one record.
538 if(ExtraData.pvBuffer != NULL) {
539 if(ExtraData.cbBuffer > 0) {
540 if(ExtraData.cbBuffer > tls_ctx->recv_buf_cap) {
541 unsigned char *grown = (unsigned char *)LocalAlloc(LPTR, ExtraData.cbBuffer);
542 if(grown != NULL) {
543 LocalFree(tls_ctx->recv_buf);
544 tls_ctx->recv_buf = grown;
545 tls_ctx->recv_buf_cap = ExtraData.cbBuffer;
546 }
547 }
548 if(ExtraData.cbBuffer <= tls_ctx->recv_buf_cap) {
549 MoveMemory(tls_ctx->recv_buf, ExtraData.pvBuffer, ExtraData.cbBuffer);
550 tls_ctx->recv_buf_len = ExtraData.cbBuffer;
551 }
552 }
553 LocalFree(ExtraData.pvBuffer);
554 }
555
556 return 0;
557}
558
559// vschannel_write encrypts and sends `len` application bytes over the open
560// connection, chunked to the negotiated maximum record size. Returns the number
561// of bytes consumed (== len) on success, or -1 on error.
562INT vschannel_write(TlsContext *tls_ctx, const char *buf, INT len) {
563 SecBufferDesc Message;
564 SecBuffer Buffers[4];
565 SECURITY_STATUS scRet;
566 PBYTE io;
567 DWORD off;
568 INT cbData;
569
570 if(len <= 0) {
571 return 0;
572 }
573 if(vschannel_ensure_stream_state(tls_ctx) != SEC_E_OK) {
574 return -1;
575 }
576
577 // Reuse the per-context send buffer (one full record) rather than allocating
578 // on every write; the HTTP/2 driver issues many small writes per request.
579 io = (PBYTE)tls_ctx->send_buf;
580
581 off = 0;
582 while(off < (DWORD)len) {
583 DWORD chunk = (DWORD)len - off;
584 if(chunk > tls_ctx->stream_sizes.cbMaximumMessage) {
585 chunk = tls_ctx->stream_sizes.cbMaximumMessage;
586 }
587 memcpy(io + tls_ctx->stream_sizes.cbHeader, buf + off, chunk);
588
589 Buffers[0].pvBuffer = io;
590 Buffers[0].cbBuffer = tls_ctx->stream_sizes.cbHeader;
591 Buffers[0].BufferType = SECBUFFER_STREAM_HEADER;
592 Buffers[1].pvBuffer = io + tls_ctx->stream_sizes.cbHeader;
593 Buffers[1].cbBuffer = chunk;
594 Buffers[1].BufferType = SECBUFFER_DATA;
595 Buffers[2].pvBuffer = io + tls_ctx->stream_sizes.cbHeader + chunk;
596 Buffers[2].cbBuffer = tls_ctx->stream_sizes.cbTrailer;
597 Buffers[2].BufferType = SECBUFFER_STREAM_TRAILER;
598 Buffers[3].BufferType = SECBUFFER_EMPTY;
599
600 Message.ulVersion = SECBUFFER_VERSION;
601 Message.cBuffers = 4;
602 Message.pBuffers = Buffers;
603
604 scRet = tls_ctx->sspi->EncryptMessage(&tls_ctx->h_context, 0, &Message, 0);
605 if(FAILED(scRet)) {
606 vschannel_set_last_error(tls_ctx, scRet);
607 return -1;
608 }
609
610 DWORD to_send = Buffers[0].cbBuffer + Buffers[1].cbBuffer + Buffers[2].cbBuffer;
611 DWORD sent = 0;
612 while(sent < to_send) {
613 cbData = send(tls_ctx->socket, (char*)io + sent, (int)(to_send - sent), 0);
614 if(cbData == SOCKET_ERROR || cbData == 0) {
615 vschannel_set_last_error(tls_ctx, WSAGetLastError());
616 return -1;
617 }
618 sent += (DWORD)cbData;
619 }
620 off += chunk;
621 }
622
623 return len;
624}
625
626// vschannel_read returns up to `cap` decrypted application bytes from the open
627// connection. It returns the number of bytes written into `buf` (> 0), 0 at
628// end of stream (close_notify / context expired / peer closed the socket), or
629// -1 on error. Leftover decrypted plaintext that did not fit in `buf`, and
630// ciphertext that did not yet form a complete record, are carried on the
631// TlsContext across calls.
632INT vschannel_read(TlsContext *tls_ctx, char *buf, INT cap) {
633 SecBufferDesc Message;
634 SecBuffer Buffers[4];
635 SecBuffer ExtraBuffer;
636 SecBuffer *pDataBuffer;
637 SecBuffer *pExtraBuffer;
638 SECURITY_STATUS scRet;
639 INT cbData;
640 int i;
641
642 if(cap <= 0) {
643 return 0;
644 }
645 if(vschannel_ensure_stream_state(tls_ctx) != SEC_E_OK) {
646 return -1;
647 }
648
649 // 1. Serve leftover decrypted plaintext from a previous record first.
650 if(tls_ctx->plain_buf_off < tls_ctx->plain_buf_len) {
651 DWORD avail = tls_ctx->plain_buf_len - tls_ctx->plain_buf_off;
652 DWORD n = avail < (DWORD)cap ? avail : (DWORD)cap;
653 memcpy(buf, tls_ctx->plain_buf + tls_ctx->plain_buf_off, n);
654 tls_ctx->plain_buf_off += n;
655 return (INT)n;
656 }
657
658 if(tls_ctx->stream_eof) {
659 return 0;
660 }
661
662 for(;;) {
663 // Try to decrypt whatever ciphertext we have buffered.
664 if(tls_ctx->recv_buf_len > 0) {
665 Buffers[0].pvBuffer = tls_ctx->recv_buf;
666 Buffers[0].cbBuffer = tls_ctx->recv_buf_len;
667 Buffers[0].BufferType = SECBUFFER_DATA;
668 Buffers[1].BufferType = SECBUFFER_EMPTY;
669 Buffers[2].BufferType = SECBUFFER_EMPTY;
670 Buffers[3].BufferType = SECBUFFER_EMPTY;
671
672 Message.ulVersion = SECBUFFER_VERSION;
673 Message.cBuffers = 4;
674 Message.pBuffers = Buffers;
675
676 scRet = tls_ctx->sspi->DecryptMessage(&tls_ctx->h_context, &Message, 0, NULL);
677
678 if(scRet == SEC_E_OK || scRet == SEC_I_RENEGOTIATE || scRet == SEC_I_CONTEXT_EXPIRED) {
679 pDataBuffer = NULL;
680 pExtraBuffer = NULL;
681 for(i = 1; i < 4; i++) {
682 if(pDataBuffer == NULL && Buffers[i].BufferType == SECBUFFER_DATA) {
683 pDataBuffer = &Buffers[i];
684 }
685 if(pExtraBuffer == NULL && Buffers[i].BufferType == SECBUFFER_EXTRA) {
686 pExtraBuffer = &Buffers[i];
687 }
688 }
689
690 // Copy out decrypted application data: as much as fits in the
691 // caller's buffer, the rest into the plaintext carryover. Both
692 // reads happen before the SECBUFFER_EXTRA move below, since the
693 // data buffer points inside recv_buf.
694 INT produced = 0;
695 if(pDataBuffer && pDataBuffer->cbBuffer > 0) {
696 DWORD data_len = pDataBuffer->cbBuffer;
697 DWORD n = data_len < (DWORD)cap ? data_len : (DWORD)cap;
698 memcpy(buf, pDataBuffer->pvBuffer, n);
699 produced = (INT)n;
700 DWORD rest = data_len - n;
701 if(rest > 0) {
702 memcpy(tls_ctx->plain_buf, (unsigned char*)pDataBuffer->pvBuffer + n, rest);
703 tls_ctx->plain_buf_len = rest;
704 tls_ctx->plain_buf_off = 0;
705 }
706 }
707
708 // Carry trailing ciphertext (start of the next record) to the
709 // front of recv_buf for the next decrypt.
710 if(pExtraBuffer) {
711 MoveMemory(tls_ctx->recv_buf, pExtraBuffer->pvBuffer, pExtraBuffer->cbBuffer);
712 tls_ctx->recv_buf_len = pExtraBuffer->cbBuffer;
713 } else {
714 tls_ctx->recv_buf_len = 0;
715 }
716
717 if(scRet == SEC_I_RENEGOTIATE) {
718 // The server requested a new handshake. Run it, then carry any
719 // extra data it left behind and keep reading.
720 SECURITY_STATUS rh = client_handshake_loop(tls_ctx, FALSE, &ExtraBuffer);
721 if(rh != SEC_E_OK) {
722 vschannel_set_last_error(tls_ctx, rh);
723 return -1;
724 }
725 if(ExtraBuffer.pvBuffer) {
726 if(ExtraBuffer.cbBuffer <= tls_ctx->recv_buf_cap) {
727 MoveMemory(tls_ctx->recv_buf, ExtraBuffer.pvBuffer, ExtraBuffer.cbBuffer);
728 tls_ctx->recv_buf_len = ExtraBuffer.cbBuffer;
729 }
730 LocalFree(ExtraBuffer.pvBuffer);
731 }
732 } else if(scRet == SEC_I_CONTEXT_EXPIRED) {
733 // Graceful close_notify from the server.
734 tls_ctx->stream_eof = TRUE;
735 if(produced > 0) {
736 return produced;
737 }
738 return 0;
739 }
740
741 if(produced > 0) {
742 return produced;
743 }
744 // A record with no application data (e.g. a session ticket or a
745 // renegotiation). Keep going.
746 continue;
747 } else if(scRet == SEC_E_INCOMPLETE_MESSAGE) {
748 // Need more bytes to complete the current record: fall through to
749 // recv() more ciphertext.
750 } else {
751 vschannel_set_last_error(tls_ctx, scRet);
752 return -1;
753 }
754 }
755
756 // Receive more ciphertext.
757 if(tls_ctx->recv_buf_len >= tls_ctx->recv_buf_cap) {
758 // Should not happen: a single record fits in recv_buf_cap.
759 vschannel_set_last_error(tls_ctx, SEC_E_INTERNAL_ERROR);
760 return -1;
761 }
762 cbData = recv(tls_ctx->socket, (char*)tls_ctx->recv_buf + tls_ctx->recv_buf_len,
763 (int)(tls_ctx->recv_buf_cap - tls_ctx->recv_buf_len), 0);
764 if(cbData == SOCKET_ERROR) {
765 vschannel_set_last_error(tls_ctx, WSAGetLastError());
766 return -1;
767 }
768 if(cbData == 0) {
769 // Peer closed the socket. Any buffered bytes are an incomplete record.
770 tls_ctx->stream_eof = TRUE;
771 return 0;
772 }
773 tls_ctx->recv_buf_len += (DWORD)cbData;
774 }
775}
776
777// vschannel_h2_close sends a close_notify alert and tears down the connection.
778void vschannel_h2_close(TlsContext *tls_ctx) {
779 if(tls_ctx->context_initialized) {
780 disconnect_from_server(tls_ctx);
781 tls_ctx->context_initialized = FALSE;
782 tls_ctx->socket = INVALID_SOCKET;
783 }
784 vschannel_cleanup(tls_ctx);
785}
786
787
788static SECURITY_STATUS create_credentials(TlsContext *tls_ctx) {
789 TimeStamp tsExpiry;
790 SECURITY_STATUS Status;
791
792 DWORD cSupportedAlgs = 0;
793 ALG_ID rgbSupportedAlgs[16];
794
795 PCCERT_CONTEXT pCertContext = NULL;
796
797 // Open the "MY" certificate store, which is where Internet Explorer
798 // stores its client certificates.
799 if(tls_ctx->cert_store == NULL) {
800 tls_ctx->cert_store = CertOpenSystemStore(0, L"MY");
801
802 if(!tls_ctx->cert_store) {
803 wprintf(L"Error 0x%x returned by CertOpenSystemStore\n",
804 GetLastError());
805 return SEC_E_NO_CREDENTIALS;
806 }
807 }
808
809 // Build Schannel credential structure. Currently, this sample only
810 // specifies the protocol to be used (and optionally the certificate,
811 // of course). Real applications may wish to specify other parameters
812 // as well.
813
814 ZeroMemory(&tls_ctx->schannel_cred, sizeof(tls_ctx->schannel_cred));
815
816 tls_ctx->schannel_cred.dwVersion = SCHANNEL_CRED_VERSION;
817 if(pCertContext)
818 {
819 tls_ctx->schannel_cred.cCreds = 1;
820 tls_ctx->schannel_cred.paCred = &pCertContext;
821 }
822
823 tls_ctx->schannel_cred.grbitEnabledProtocols = protocol;
824
825 if(aid_key_exch)
826 {
827 rgbSupportedAlgs[cSupportedAlgs++] = aid_key_exch;
828 }
829
830 if(cSupportedAlgs)
831 {
832 tls_ctx->schannel_cred.cSupportedAlgs = cSupportedAlgs;
833 tls_ctx->schannel_cred.palgSupportedAlgs = rgbSupportedAlgs;
834 }
835
836 tls_ctx->schannel_cred.dwFlags |= SCH_CRED_NO_DEFAULT_CREDS;
837 tls_ctx->schannel_cred.dwFlags |= SCH_CRED_MANUAL_CRED_VALIDATION;
838
839 // Keep certificate validation under the caller's control. The validated
840 // path runs explicit hostname/chain validation after the handshake.
841
842 // Create an SSPI credential.
843
844 Status = tls_ctx->sspi->AcquireCredentialsHandle(
845 NULL, // Name of principal
846 UNISP_NAME_W, // Name of package
847 SECPKG_CRED_OUTBOUND, // Flags indicating use
848 NULL, // Pointer to logon ID
849 &tls_ctx->schannel_cred, // Package specific data
850 NULL, // Pointer to GetKey() func
851 NULL, // Value to pass to GetKey()
852 &tls_ctx->h_client_creds, // (out) Cred Handle
853 &tsExpiry); // (out) Lifetime (optional)
854 if(Status != SEC_E_OK) {
855 wprintf(L"Error 0x%x returned by AcquireCredentialsHandle\n", Status);
856 goto cleanup;
857 }
858
859cleanup:
860
861 // Free the certificate context. Schannel has already made its own copy.
862
863 if(pCertContext) {
864 CertFreeCertificateContext(pCertContext);
865 }
866
867
868 return Status;
869}
870
871
872static INT connect_to_server(TlsContext *tls_ctx, LPWSTR host, INT port_number) {
873 SOCKET Socket;
874
875 SOCKADDR_STORAGE local_address = { 0 };
876 SOCKADDR_STORAGE remote_address = { 0 };
877
878 DWORD local_address_length = sizeof(local_address);
879 DWORD remote_address_length = sizeof(remote_address);
880
881 struct timeval tv;
882 tv.tv_sec = 60;
883 tv.tv_usec = 0;
884
885 Socket = socket(PF_INET, SOCK_STREAM, 0);
886 if(Socket == INVALID_SOCKET) {
887 INT err_code = WSAGetLastError();
888 vschannel_set_last_error(tls_ctx, err_code);
889 return err_code;
890 }
891
892 LPWSTR connect_name = use_proxy ? psz_proxy_server : host;
893
894 WCHAR service_name[10];
895 int res = wsprintf(service_name, L"%d", port_number);
896
897 if(WSAConnectByNameW(Socket,connect_name, service_name, &local_address_length,
898 &local_address, &remote_address_length, &remote_address, &tv, NULL) == FALSE) {
899 INT err_code = WSAGetLastError();
900 vschannel_set_last_error(tls_ctx, err_code);
901 closesocket(Socket);
902 return err_code;
903 }
904
905 if(use_proxy) {
906 BYTE pbMessage[200];
907 DWORD cbMessage;
908
909 // Build message for proxy server
910 strcpy(pbMessage, "CONNECT ");
911 strcat(pbMessage, host);
912 strcat(pbMessage, ":");
913 _itoa(port_number, pbMessage + strlen(pbMessage), 10);
914 strcat(pbMessage, " HTTP/1.0\r\nUser-Agent: webclient\r\n\r\n");
915 cbMessage = (DWORD)strlen(pbMessage);
916
917 // Send message to proxy server
918 if(send(Socket, pbMessage, cbMessage, 0) == SOCKET_ERROR) {
919 INT err_code = WSAGetLastError();
920 vschannel_set_last_error(tls_ctx, err_code);
921 return err_code;
922 }
923
924 // Receive message from proxy server
925 cbMessage = recv(Socket, pbMessage, 200, 0);
926 if(cbMessage == SOCKET_ERROR) {
927 INT err_code = WSAGetLastError();
928 vschannel_set_last_error(tls_ctx, err_code);
929 return err_code;
930 }
931
932 // this sample is limited but in normal use it
933 // should continue to receive until CR LF CR LF is received
934 }
935
936 tls_ctx->socket = Socket;
937
938 return SEC_E_OK;
939}
940
941
942static LONG disconnect_from_server(TlsContext *tls_ctx) {
943 DWORD dwType;
944 PBYTE pbMessage;
945 DWORD cbMessage;
946 DWORD cbData;
947
948 SecBufferDesc OutBuffer;
949 SecBuffer OutBuffers[1];
950 DWORD dwSSPIFlags;
951 DWORD dwSSPIOutFlags;
952 TimeStamp tsExpiry;
953 DWORD Status;
954
955 // Notify schannel that we are about to close the connection.
956
957 dwType = SCHANNEL_SHUTDOWN;
958
959 OutBuffers[0].pvBuffer = &dwType;
960 OutBuffers[0].BufferType = SECBUFFER_TOKEN;
961 OutBuffers[0].cbBuffer = sizeof(dwType);
962
963 OutBuffer.cBuffers = 1;
964 OutBuffer.pBuffers = OutBuffers;
965 OutBuffer.ulVersion = SECBUFFER_VERSION;
966
967 Status = tls_ctx->sspi->ApplyControlToken(&tls_ctx->h_context, &OutBuffer);
968
969 if(FAILED(Status)) {
970 wprintf(L"Error 0x%x returned by ApplyControlToken\n", Status);
971 goto cleanup;
972 }
973
974 // Build an SSL close notify message.
975
976 dwSSPIFlags = ISC_REQ_SEQUENCE_DETECT |
977 ISC_REQ_REPLAY_DETECT |
978 ISC_REQ_CONFIDENTIALITY |
979 ISC_RET_EXTENDED_ERROR |
980 ISC_REQ_ALLOCATE_MEMORY |
981 ISC_REQ_STREAM;
982
983 OutBuffers[0].pvBuffer = NULL;
984 OutBuffers[0].BufferType = SECBUFFER_TOKEN;
985 OutBuffers[0].cbBuffer = 0;
986
987 OutBuffer.cBuffers = 1;
988 OutBuffer.pBuffers = OutBuffers;
989 OutBuffer.ulVersion = SECBUFFER_VERSION;
990
991 Status = tls_ctx->sspi->InitializeSecurityContext(
992 &tls_ctx->h_client_creds, &tls_ctx->h_context, NULL, dwSSPIFlags, 0, SECURITY_NATIVE_DREP,
993 NULL, 0, &tls_ctx->h_context, &OutBuffer, &dwSSPIOutFlags, &tsExpiry);
994
995 if(FAILED(Status)) {
996 wprintf(L"Error 0x%x returned by InitializeSecurityContext\n", Status);
997 goto cleanup;
998 }
999
1000 pbMessage = OutBuffers[0].pvBuffer;
1001 cbMessage = OutBuffers[0].cbBuffer;
1002
1003 // Send the close notify message to the server.
1004
1005 if(pbMessage != NULL && cbMessage != 0) {
1006 cbData = send(tls_ctx->socket, pbMessage, cbMessage, 0);
1007 if(cbData == SOCKET_ERROR || cbData == 0) {
1008 Status = WSAGetLastError();
1009 wprintf(L"Error %d sending close notify\n", Status);
1010 goto cleanup;
1011 }
1012
1013 // Free output buffer.
1014 tls_ctx->sspi->FreeContextBuffer(pbMessage);
1015 }
1016
1017
1018cleanup:
1019
1020 // Free the security context.
1021 tls_ctx->sspi->DeleteSecurityContext(&tls_ctx->h_context);
1022
1023 // Close the socket.
1024 closesocket(tls_ctx->socket);
1025
1026 return Status;
1027}
1028
1029
1030static SECURITY_STATUS perform_client_handshake(TlsContext *tls_ctx, WCHAR *host, SecBuffer *pExtraData) {
1031 SecBufferDesc OutBuffer;
1032 SecBuffer OutBuffers[1];
1033 DWORD dwSSPIFlags;
1034 DWORD dwSSPIOutFlags;
1035 TimeStamp tsExpiry;
1036 SECURITY_STATUS scRet;
1037 DWORD cbData;
1038
1039 dwSSPIFlags = ISC_REQ_SEQUENCE_DETECT |
1040 ISC_REQ_REPLAY_DETECT |
1041 ISC_REQ_CONFIDENTIALITY |
1042 ISC_RET_EXTENDED_ERROR |
1043 ISC_REQ_ALLOCATE_MEMORY |
1044 ISC_REQ_STREAM;
1045
1046 //
1047 // Optionally advertise ALPN protocols in the ClientHello. SChannel takes
1048 // this as a SECBUFFER_APPLICATION_PROTOCOLS input buffer holding a
1049 // SEC_APPLICATION_PROTOCOLS record. The backing store is a 4-byte-aligned
1050 // unsigned long array so the struct cast is well aligned on every compiler.
1051 //
1052 SecBuffer InBuffers[1];
1053 SecBufferDesc InBuffer;
1054 SecBufferDesc *pInput = NULL;
1055 unsigned long alpn_store[80]; // 320 bytes; alpn_wire is at most 256
1056 if (tls_ctx->alpn_wire_len > 0) {
1057 SEC_APPLICATION_PROTOCOLS *protos = (SEC_APPLICATION_PROTOCOLS *)alpn_store;
1058 SEC_APPLICATION_PROTOCOL_LIST *list = &protos->ProtocolLists[0];
1059 unsigned long wlen = tls_ctx->alpn_wire_len;
1060
1061 list->ProtoNegoExt = SecApplicationProtocolNegotiationExt_ALPN;
1062 list->ProtocolListSize = (unsigned short)wlen;
1063 memcpy(list->ProtocolList, tls_ctx->alpn_wire, (size_t)wlen);
1064 protos->ProtocolListsSize =
1065 (unsigned long)(FIELD_OFFSET(SEC_APPLICATION_PROTOCOL_LIST, ProtocolList) + wlen);
1066
1067 InBuffers[0].pvBuffer = protos;
1068 InBuffers[0].cbBuffer =
1069 (unsigned long)(FIELD_OFFSET(SEC_APPLICATION_PROTOCOLS, ProtocolLists) + protos->ProtocolListsSize);
1070 InBuffers[0].BufferType = SECBUFFER_APPLICATION_PROTOCOLS;
1071
1072 InBuffer.cBuffers = 1;
1073 InBuffer.pBuffers = InBuffers;
1074 InBuffer.ulVersion = SECBUFFER_VERSION;
1075 pInput = &InBuffer;
1076 }
1077
1078 //
1079 // Initiate a ClientHello message and generate a token.
1080 //
1081
1082 OutBuffers[0].pvBuffer = NULL;
1083 OutBuffers[0].BufferType = SECBUFFER_TOKEN;
1084 OutBuffers[0].cbBuffer = 0;
1085
1086 OutBuffer.cBuffers = 1;
1087 OutBuffer.pBuffers = OutBuffers;
1088 OutBuffer.ulVersion = SECBUFFER_VERSION;
1089
1090 scRet = tls_ctx->sspi->InitializeSecurityContext(
1091 &tls_ctx->h_client_creds,
1092 NULL,
1093 host,
1094 dwSSPIFlags,
1095 0,
1096 SECURITY_NATIVE_DREP,
1097 pInput,
1098 0,
1099 &tls_ctx->h_context,
1100 &OutBuffer,
1101 &dwSSPIOutFlags,
1102 &tsExpiry);
1103
1104 if(scRet != SEC_I_CONTINUE_NEEDED)
1105 {
1106 wprintf(L"Error %d returned by InitializeSecurityContext (1)\n", scRet);
1107 return scRet;
1108 }
1109
1110 // Send response to server if there is one.
1111 if(OutBuffers[0].cbBuffer != 0 && OutBuffers[0].pvBuffer != NULL)
1112 {
1113 cbData = send(tls_ctx->socket, OutBuffers[0].pvBuffer, OutBuffers[0].cbBuffer, 0);
1114 if(cbData == SOCKET_ERROR || cbData == 0) {
1115 wprintf(L"Error %d sending data to server (1)\n", WSAGetLastError());
1116 tls_ctx->sspi->FreeContextBuffer(OutBuffers[0].pvBuffer);
1117 tls_ctx->sspi->DeleteSecurityContext(&tls_ctx->h_context);
1118 return SEC_E_INTERNAL_ERROR;
1119 }
1120
1121 // Free output buffer.
1122 tls_ctx->sspi->FreeContextBuffer(OutBuffers[0].pvBuffer);
1123 OutBuffers[0].pvBuffer = NULL;
1124 }
1125
1126 return client_handshake_loop(tls_ctx, TRUE, pExtraData);
1127}
1128
1129
1130static SECURITY_STATUS client_handshake_loop(TlsContext *tls_ctx, BOOL fDoInitialRead, SecBuffer *pExtraData) {
1131 SecBufferDesc InBuffer;
1132 SecBuffer InBuffers[2];
1133 SecBufferDesc OutBuffer;
1134 SecBuffer OutBuffers[1];
1135 DWORD dwSSPIFlags;
1136 DWORD dwSSPIOutFlags;
1137 TimeStamp tsExpiry;
1138 SECURITY_STATUS scRet;
1139 DWORD cbData;
1140
1141 PUCHAR IoBuffer;
1142 DWORD cbIoBuffer;
1143 BOOL fDoRead;
1144
1145
1146 dwSSPIFlags = ISC_REQ_SEQUENCE_DETECT |
1147 ISC_REQ_REPLAY_DETECT |
1148 ISC_REQ_CONFIDENTIALITY |
1149 ISC_RET_EXTENDED_ERROR |
1150 ISC_REQ_ALLOCATE_MEMORY |
1151 ISC_REQ_STREAM;
1152
1153 //
1154 // Allocate data buffer.
1155 //
1156
1157 IoBuffer = LocalAlloc(LPTR, IO_BUFFER_SIZE);
1158 if(IoBuffer == NULL)
1159 {
1160 wprintf(L"Out of memory (1)\n");
1161 return SEC_E_INTERNAL_ERROR;
1162 }
1163 cbIoBuffer = 0;
1164
1165 fDoRead = fDoInitialRead;
1166
1167
1168 //
1169 // Loop until the handshake is finished or an error occurs.
1170 //
1171
1172 scRet = SEC_I_CONTINUE_NEEDED;
1173
1174 while(scRet == SEC_I_CONTINUE_NEEDED ||
1175 scRet == SEC_E_INCOMPLETE_MESSAGE ||
1176 scRet == SEC_I_INCOMPLETE_CREDENTIALS) {
1177
1178 // Read data from server.
1179 if(0 == cbIoBuffer || scRet == SEC_E_INCOMPLETE_MESSAGE) {
1180 if(fDoRead) {
1181 cbData = recv(tls_ctx->socket,
1182 IoBuffer + cbIoBuffer,
1183 IO_BUFFER_SIZE - cbIoBuffer,
1184 0);
1185 if(cbData == SOCKET_ERROR) {
1186 wprintf(L"Error %d reading data from server\n", WSAGetLastError());
1187 scRet = SEC_E_INTERNAL_ERROR;
1188 break;
1189 }
1190 else if(cbData == 0) {
1191 wprintf(L"Server unexpectedly disconnected\n");
1192 scRet = SEC_E_INTERNAL_ERROR;
1193 break;
1194 }
1195
1196 cbIoBuffer += cbData;
1197 }
1198 else {
1199 fDoRead = TRUE;
1200 }
1201 }
1202
1203 // Set up the input buffers. Buffer 0 is used to pass in data
1204 // received from the server. Schannel will consume some or all
1205 // of this. Leftover data (if any) will be placed in buffer 1 and
1206 // given a buffer type of SECBUFFER_EXTRA.
1207
1208 InBuffers[0].pvBuffer = IoBuffer;
1209 InBuffers[0].cbBuffer = cbIoBuffer;
1210 InBuffers[0].BufferType = SECBUFFER_TOKEN;
1211
1212 InBuffers[1].pvBuffer = NULL;
1213 InBuffers[1].cbBuffer = 0;
1214 InBuffers[1].BufferType = SECBUFFER_EMPTY;
1215
1216 InBuffer.cBuffers = 2;
1217 InBuffer.pBuffers = InBuffers;
1218 InBuffer.ulVersion = SECBUFFER_VERSION;
1219
1220 // Set up the output buffers. These are initialized to NULL
1221 // so as to make it less likely we'll attempt to free random
1222 // garbage later.
1223
1224 OutBuffers[0].pvBuffer = NULL;
1225 OutBuffers[0].BufferType= SECBUFFER_TOKEN;
1226 OutBuffers[0].cbBuffer = 0;
1227
1228 OutBuffer.cBuffers = 1;
1229 OutBuffer.pBuffers = OutBuffers;
1230 OutBuffer.ulVersion = SECBUFFER_VERSION;
1231
1232 // Call InitializeSecurityContext.
1233
1234 scRet = tls_ctx->sspi->InitializeSecurityContext(
1235 &tls_ctx->h_client_creds, &tls_ctx->h_context, NULL, dwSSPIFlags, 0, SECURITY_NATIVE_DREP,
1236 &InBuffer, 0, NULL, &OutBuffer, &dwSSPIOutFlags, &tsExpiry);
1237
1238 // If InitializeSecurityContext was successful (or if the error was
1239 // one of the special extended ones), send the contends of the output
1240 // buffer to the server.
1241
1242 if(scRet == SEC_E_OK ||
1243 scRet == SEC_I_CONTINUE_NEEDED ||
1244 FAILED(scRet) && (dwSSPIOutFlags & ISC_RET_EXTENDED_ERROR)) {
1245 if(OutBuffers[0].cbBuffer != 0 && OutBuffers[0].pvBuffer != NULL) {
1246 cbData = send(tls_ctx->socket,
1247 OutBuffers[0].pvBuffer,
1248 OutBuffers[0].cbBuffer,
1249 0);
1250 if(cbData == SOCKET_ERROR || cbData == 0) {
1251 wprintf(L"Error %d sending data to server (2)\n",
1252 WSAGetLastError());
1253 tls_ctx->sspi->FreeContextBuffer(OutBuffers[0].pvBuffer);
1254 tls_ctx->sspi->DeleteSecurityContext(&tls_ctx->h_context);
1255 return SEC_E_INTERNAL_ERROR;
1256 }
1257
1258 // Free output buffer.
1259 tls_ctx->sspi->FreeContextBuffer(OutBuffers[0].pvBuffer);
1260 OutBuffers[0].pvBuffer = NULL;
1261 }
1262 }
1263
1264 // If InitializeSecurityContext returned SEC_E_INCOMPLETE_MESSAGE,
1265 // then we need to read more data from the server and try again.
1266 if(scRet == SEC_E_INCOMPLETE_MESSAGE) {
1267 continue;
1268 }
1269
1270 // If InitializeSecurityContext returned SEC_E_OK, then the
1271 // handshake completed successfully.
1272
1273 if(scRet == SEC_E_OK) {
1274 // If the "extra" buffer contains data, this is encrypted application
1275 // protocol layer stuff. It needs to be saved. The application layer
1276 // will later decrypt it with DecryptMessage.
1277
1278 if(InBuffers[1].BufferType == SECBUFFER_EXTRA)
1279 {
1280 pExtraData->pvBuffer = LocalAlloc(LPTR, InBuffers[1].cbBuffer);
1281 if(pExtraData->pvBuffer == NULL) {
1282 wprintf(L"Out of memory (2)\n");
1283 return SEC_E_INTERNAL_ERROR;
1284 }
1285
1286 MoveMemory(pExtraData->pvBuffer,
1287 IoBuffer + (cbIoBuffer - InBuffers[1].cbBuffer),
1288 InBuffers[1].cbBuffer);
1289
1290 pExtraData->cbBuffer = InBuffers[1].cbBuffer;
1291 pExtraData->BufferType = SECBUFFER_TOKEN;
1292
1293 // wprintf(L"%d bytes of app data was bundled with handshake data\n", pExtraData->cbBuffer);
1294 }
1295 else {
1296 pExtraData->pvBuffer = NULL;
1297 pExtraData->cbBuffer = 0;
1298 pExtraData->BufferType = SECBUFFER_EMPTY;
1299 }
1300
1301 // Bail out to quit
1302 break;
1303 }
1304
1305 // Check for fatal error.
1306 if(FAILED(scRet)) {
1307 wprintf(L"Error 0x%x returned by InitializeSecurityContext (2)\n", scRet);
1308 break;
1309 }
1310
1311 // If InitializeSecurityContext returned SEC_I_INCOMPLETE_CREDENTIALS,
1312 // then the server just requested client authentication.
1313 if(scRet == SEC_I_INCOMPLETE_CREDENTIALS) {
1314 // Busted. The server has requested client authentication and
1315 // the credential we supplied didn't contain a client certificate.
1316
1317 // This function will read the list of trusted certificate
1318 // authorities ("issuers") that was received from the server
1319 // and attempt to find a suitable client certificate that
1320 // was issued by one of these. If this function is successful,
1321 // then we will connect using the new certificate. Otherwise,
1322 // we will attempt to connect anonymously (using our current
1323 // credentials).
1324
1325 get_new_client_credentials(tls_ctx);
1326
1327 // Go around again.
1328 fDoRead = FALSE;
1329 scRet = SEC_I_CONTINUE_NEEDED;
1330 continue;
1331 }
1332
1333 // Copy any leftover data from the "extra" buffer, and go around
1334 // again.
1335
1336 if ( InBuffers[1].BufferType == SECBUFFER_EXTRA ) {
1337 MoveMemory(IoBuffer,
1338 IoBuffer + (cbIoBuffer - InBuffers[1].cbBuffer),
1339 InBuffers[1].cbBuffer);
1340
1341 cbIoBuffer = InBuffers[1].cbBuffer;
1342 }
1343 else {
1344 cbIoBuffer = 0;
1345 }
1346 }
1347
1348 // Delete the security context in the case of a fatal error.
1349 if(FAILED(scRet)) {
1350 tls_ctx->sspi->DeleteSecurityContext(&tls_ctx->h_context);
1351 }
1352
1353 LocalFree(IoBuffer);
1354
1355 return scRet;
1356}
1357
1358
1359static SECURITY_STATUS https_make_request(TlsContext *tls_ctx, CHAR *req, DWORD req_len, CHAR **out, int *length, vschannel_allocator afn) {
1360 SecPkgContext_StreamSizes Sizes;
1361 SECURITY_STATUS scRet;
1362 SecBufferDesc Message;
1363 SecBuffer Buffers[4];
1364 SecBuffer *pDataBuffer;
1365 SecBuffer *pExtraBuffer;
1366 SecBuffer ExtraBuffer;
1367
1368 PBYTE pbIoBuffer;
1369 DWORD cbIoBuffer;
1370 DWORD cbIoBufferLength;
1371 PBYTE pbMessage;
1372 DWORD cbMessage;
1373
1374 INT cbData;
1375 INT i;
1376 DWORD req_offset;
1377 DWORD chunk_len;
1378 DWORD to_send;
1379 DWORD sent;
1380
1381
1382 // Read stream encryption properties.
1383 scRet = tls_ctx->sspi->QueryContextAttributes(&tls_ctx->h_context, SECPKG_ATTR_STREAM_SIZES, &Sizes);
1384 if(scRet != SEC_E_OK) {
1385 wprintf(L"Error 0x%x reading SECPKG_ATTR_STREAM_SIZES\n", scRet);
1386 return scRet;
1387 }
1388
1389 // Allocate a working buffer. The plaintext sent to EncryptMessage
1390 // should never be more than 'Sizes.cbMaximumMessage', so a buffer
1391 // size of this plus the header and trailer sizes should be safe enough.
1392 cbIoBufferLength = Sizes.cbHeader + Sizes.cbMaximumMessage + Sizes.cbTrailer;
1393
1394 pbIoBuffer = LocalAlloc(LPTR, cbIoBufferLength);
1395 if(pbIoBuffer == NULL) {
1396 wprintf(L"Out of memory (2)\n");
1397 return SEC_E_INTERNAL_ERROR;
1398 }
1399
1400 // Build and send HTTP request in chunks no larger than cbMaximumMessage.
1401 // EncryptMessage expects plaintext <= cbMaximumMessage.
1402 pbMessage = pbIoBuffer + Sizes.cbHeader;
1403 req_offset = 0;
1404 while(req_offset < req_len) {
1405 chunk_len = req_len - req_offset;
1406 if(chunk_len > Sizes.cbMaximumMessage) {
1407 chunk_len = Sizes.cbMaximumMessage;
1408 }
1409
1410 memcpy(pbMessage, req + req_offset, chunk_len);
1411 cbMessage = chunk_len;
1412
1413 Buffers[0].pvBuffer = pbIoBuffer;
1414 Buffers[0].cbBuffer = Sizes.cbHeader;
1415 Buffers[0].BufferType = SECBUFFER_STREAM_HEADER;
1416
1417 Buffers[1].pvBuffer = pbMessage;
1418 Buffers[1].cbBuffer = cbMessage;
1419 Buffers[1].BufferType = SECBUFFER_DATA;
1420
1421 Buffers[2].pvBuffer = pbMessage + cbMessage;
1422 Buffers[2].cbBuffer = Sizes.cbTrailer;
1423 Buffers[2].BufferType = SECBUFFER_STREAM_TRAILER;
1424
1425 Buffers[3].BufferType = SECBUFFER_EMPTY;
1426
1427 Message.ulVersion = SECBUFFER_VERSION;
1428 Message.cBuffers = 4;
1429 Message.pBuffers = Buffers;
1430
1431 scRet = tls_ctx->sspi->EncryptMessage(&tls_ctx->h_context, 0, &Message, 0);
1432 if(FAILED(scRet)) {
1433 wprintf(L"Error 0x%x returned by EncryptMessage\n", scRet);
1434 return scRet;
1435 }
1436
1437 // Send all encrypted bytes for this chunk.
1438 to_send = Buffers[0].cbBuffer + Buffers[1].cbBuffer + Buffers[2].cbBuffer;
1439 sent = 0;
1440 while(sent < to_send) {
1441 cbData = send(tls_ctx->socket, (char*)pbIoBuffer + sent, (int)(to_send - sent), 0);
1442 if(cbData == SOCKET_ERROR || cbData == 0) {
1443 wprintf(L"Error %d sending data to server (3)\n", WSAGetLastError());
1444 tls_ctx->sspi->DeleteSecurityContext(&tls_ctx->h_context);
1445 return SEC_E_INTERNAL_ERROR;
1446 }
1447 sent += (DWORD)cbData;
1448 }
1449
1450 req_offset += chunk_len;
1451 }
1452
1453 // Read data from server until done.
1454 INT buff_size = vsc_init_resp_buff_size;
1455 cbIoBuffer = 0;
1456 while(TRUE){
1457 // Read some data.
1458 if(0 == cbIoBuffer || scRet == SEC_E_INCOMPLETE_MESSAGE) {
1459 cbData = recv(tls_ctx->socket, pbIoBuffer + cbIoBuffer, cbIoBufferLength - cbIoBuffer, 0);
1460 if(cbData == SOCKET_ERROR) {
1461 wprintf(L"Error %d reading data from server\n", WSAGetLastError());
1462 scRet = SEC_E_INTERNAL_ERROR;
1463 break;
1464 }
1465 else if(cbData == 0) {
1466 // Server disconnected.
1467 if(cbIoBuffer) {
1468 wprintf(L"Server unexpectedly disconnected\n");
1469 scRet = SEC_E_INTERNAL_ERROR;
1470 return scRet;
1471 }
1472 else {
1473 break;
1474 }
1475 }
1476 else {
1477 cbIoBuffer += cbData;
1478 }
1479 }
1480
1481 // Attempt to decrypt the received data.
1482 Buffers[0].pvBuffer = pbIoBuffer;
1483 Buffers[0].cbBuffer = cbIoBuffer;
1484 Buffers[0].BufferType = SECBUFFER_DATA;
1485
1486 Buffers[1].BufferType = SECBUFFER_EMPTY;
1487 Buffers[2].BufferType = SECBUFFER_EMPTY;
1488 Buffers[3].BufferType = SECBUFFER_EMPTY;
1489
1490 Message.ulVersion = SECBUFFER_VERSION;
1491 Message.cBuffers = 4;
1492 Message.pBuffers = Buffers;
1493
1494 scRet = tls_ctx->sspi->DecryptMessage(&tls_ctx->h_context, &Message, 0, NULL);
1495
1496 if(scRet == SEC_E_INCOMPLETE_MESSAGE) {
1497 // The input buffer contains only a fragment of an
1498 // encrypted record. Loop around and read some more
1499 // data.
1500 continue;
1501 }
1502
1503 // Server signalled end of session
1504 if(scRet == SEC_I_CONTEXT_EXPIRED) {
1505 break;
1506 }
1507
1508 if( scRet != SEC_E_OK &&
1509 scRet != SEC_I_RENEGOTIATE &&
1510 scRet != SEC_I_CONTEXT_EXPIRED)
1511 {
1512 wprintf(L"Error 0x%x returned by DecryptMessage\n", scRet);
1513 return scRet;
1514 }
1515
1516 // Locate data and (optional) extra buffers.
1517 pDataBuffer = NULL;
1518 pExtraBuffer = NULL;
1519 for(i = 1; i < 4; i++) {
1520 if(pDataBuffer == NULL && Buffers[i].BufferType == SECBUFFER_DATA)
1521 {
1522 pDataBuffer = &Buffers[i];
1523 // wprintf(L"Buffers[%d].BufferType = SECBUFFER_DATA\n",i);
1524 }
1525 if(pExtraBuffer == NULL && Buffers[i].BufferType == SECBUFFER_EXTRA)
1526 {
1527 pExtraBuffer = &Buffers[i];
1528 }
1529 }
1530
1531 // increase buffer size if we need
1532 int required_length = *length+(int)pDataBuffer->cbBuffer;
1533 if( required_length > buff_size ) {
1534 CHAR *a = afn(*out, required_length);
1535 if( a == NULL ) {
1536 scRet = SEC_E_INTERNAL_ERROR;
1537 return scRet;
1538 }
1539 *out = a;
1540 buff_size = required_length;
1541 }
1542 // Copy the decrypted data to our output buffer
1543 memcpy(*out+*length, pDataBuffer->pvBuffer, (int)pDataBuffer->cbBuffer);
1544 *length += (int)pDataBuffer->cbBuffer;
1545
1546 // Move any "extra" data to the input buffer.
1547 if(pExtraBuffer) {
1548 MoveMemory(pbIoBuffer, pExtraBuffer->pvBuffer, pExtraBuffer->cbBuffer);
1549 cbIoBuffer = pExtraBuffer->cbBuffer;
1550 }
1551 else {
1552 cbIoBuffer = 0;
1553 }
1554
1555 if(scRet == SEC_I_RENEGOTIATE)
1556 {
1557 // The server wants to perform another handshake sequence.
1558 scRet = client_handshake_loop(tls_ctx, FALSE, &ExtraBuffer);
1559 if(scRet != SEC_E_OK) {
1560 return scRet;
1561 }
1562
1563 // Move any "extra" data to the input buffer.
1564 if(ExtraBuffer.pvBuffer)
1565 {
1566 MoveMemory(pbIoBuffer, ExtraBuffer.pvBuffer, ExtraBuffer.cbBuffer);
1567 cbIoBuffer = ExtraBuffer.cbBuffer;
1568 }
1569 }
1570 }
1571
1572 return SEC_E_OK;
1573}
1574
1575
1576static DWORD verify_server_certificate( PCCERT_CONTEXT pServerCert, LPWSTR host, DWORD dwCertFlags) {
1577 HTTPSPolicyCallbackData polHttps;
1578 CERT_CHAIN_POLICY_PARA PolicyPara;
1579 CERT_CHAIN_POLICY_STATUS PolicyStatus;
1580 CERT_CHAIN_PARA ChainPara;
1581 PCCERT_CHAIN_CONTEXT pChainContext = NULL;
1582
1583 CHAR *rgszUsages[] = { szOID_PKIX_KP_SERVER_AUTH,
1584 szOID_SERVER_GATED_CRYPTO,
1585 szOID_SGC_NETSCAPE };
1586 DWORD cUsages = sizeof(rgszUsages) / sizeof(CHAR*);
1587
1588 PWSTR pwszServerName = NULL;
1589 DWORD cchServerName;
1590 DWORD Status;
1591
1592 if(pServerCert == NULL)
1593 {
1594 Status = SEC_E_WRONG_PRINCIPAL;
1595 goto cleanup;
1596 }
1597
1598 if(host == NULL || wcslen(host) == 0) {
1599 Status = SEC_E_WRONG_PRINCIPAL;
1600 goto cleanup;
1601 }
1602
1603 // Build certificate chain.
1604
1605 ZeroMemory(&ChainPara, sizeof(ChainPara));
1606 ChainPara.cbSize = sizeof(ChainPara);
1607 ChainPara.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR;
1608 ChainPara.RequestedUsage.Usage.cUsageIdentifier = cUsages;
1609 ChainPara.RequestedUsage.Usage.rgpszUsageIdentifier = rgszUsages;
1610
1611 // Best-effort TLS revocation check: detect a positively revoked leaf
1612 // certificate, but let policy evaluation ignore unknown/offline status.
1613 if(!CertGetCertificateChain(NULL, pServerCert, NULL, pServerCert->hCertStore, &ChainPara,
1614 CERT_CHAIN_CACHE_END_CERT |
1615 CERT_CHAIN_REVOCATION_CHECK_END_CERT |
1616 CERT_CHAIN_REVOCATION_ACCUMULATIVE_TIMEOUT,
1617 NULL, &pChainContext)) {
1618 Status = GetLastError();
1619 wprintf(L"Error 0x%x returned by CertGetCertificateChain!\n", Status);
1620 goto cleanup;
1621 }
1622
1623 // Validate certificate chain.
1624 ZeroMemory(&polHttps, sizeof(HTTPSPolicyCallbackData));
1625 polHttps.cbStruct = sizeof(HTTPSPolicyCallbackData);
1626 polHttps.dwAuthType = AUTHTYPE_SERVER;
1627 polHttps.fdwChecks = dwCertFlags;
1628 polHttps.pwszServerName = host;
1629
1630 memset(&PolicyPara, 0, sizeof(PolicyPara));
1631 PolicyPara.cbSize = sizeof(PolicyPara);
1632 PolicyPara.dwFlags = CERT_CHAIN_POLICY_IGNORE_ALL_REV_UNKNOWN_FLAGS;
1633 PolicyPara.pvExtraPolicyPara = &polHttps;
1634
1635 memset(&PolicyStatus, 0, sizeof(PolicyStatus));
1636 PolicyStatus.cbSize = sizeof(PolicyStatus);
1637
1638 if(!CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_SSL, pChainContext, &PolicyPara, &PolicyStatus)){
1639 Status = GetLastError();
1640 wprintf(L"Error 0x%x returned by CertVerifyCertificateChainPolicy!\n", Status);
1641 goto cleanup;
1642 }
1643
1644 if(PolicyStatus.dwError) {
1645 Status = PolicyStatus.dwError;
1646 goto cleanup;
1647 }
1648
1649
1650 Status = SEC_E_OK;
1651
1652cleanup:
1653
1654 if(pChainContext)
1655 {
1656 CertFreeCertificateChain(pChainContext);
1657 }
1658
1659 if(pwszServerName)
1660 {
1661 LocalFree(pwszServerName);
1662 }
1663
1664 return Status;
1665}
1666
1667
1668static void get_new_client_credentials(TlsContext *tls_ctx) {
1669 CredHandle hCreds;
1670 SecPkgContext_IssuerListInfoEx IssuerListInfo;
1671 PCCERT_CHAIN_CONTEXT pChainContext;
1672 CERT_CHAIN_FIND_BY_ISSUER_PARA FindByIssuerPara;
1673 PCCERT_CONTEXT pCertContext;
1674 TimeStamp tsExpiry;
1675 SECURITY_STATUS Status;
1676
1677 // Read list of trusted issuers from schannel.
1678 Status = tls_ctx->sspi->QueryContextAttributes(&tls_ctx->h_context, SECPKG_ATTR_ISSUER_LIST_EX, (PVOID)&IssuerListInfo);
1679 if(Status != SEC_E_OK) {
1680 wprintf(L"Error 0x%x querying issuer list info\n", Status);
1681 return;
1682 }
1683
1684 // Enumerate the client certificates.
1685
1686 ZeroMemory(&FindByIssuerPara, sizeof(FindByIssuerPara));
1687
1688 FindByIssuerPara.cbSize = sizeof(FindByIssuerPara);
1689 FindByIssuerPara.pszUsageIdentifier = szOID_PKIX_KP_CLIENT_AUTH;
1690 FindByIssuerPara.dwKeySpec = 0;
1691 FindByIssuerPara.cIssuer = IssuerListInfo.cIssuers;
1692 FindByIssuerPara.rgIssuer = IssuerListInfo.aIssuers;
1693
1694 pChainContext = NULL;
1695
1696 while(TRUE) {
1697 // Find a certificate chain.
1698 pChainContext = CertFindChainInStore(tls_ctx->cert_store,
1699 X509_ASN_ENCODING,
1700 0,
1701 CERT_CHAIN_FIND_BY_ISSUER,
1702 &FindByIssuerPara,
1703 pChainContext);
1704 if(pChainContext == NULL) {
1705 wprintf(L"Error 0x%x finding cert chain\n", GetLastError());
1706 break;
1707 }
1708
1709 // Get pointer to leaf certificate context.
1710 pCertContext = pChainContext->rgpChain[0]->rgpElement[0]->pCertContext;
1711
1712 // Create schannel credential.
1713 tls_ctx->schannel_cred.dwVersion = SCHANNEL_CRED_VERSION;
1714 tls_ctx->schannel_cred.cCreds = 1;
1715 tls_ctx->schannel_cred.paCred = &pCertContext;
1716
1717 Status = tls_ctx->sspi->AcquireCredentialsHandle(
1718 NULL, // Name of principal
1719 UNISP_NAME_W, // Name of package
1720 SECPKG_CRED_OUTBOUND, // Flags indicating use
1721 NULL, // Pointer to logon ID
1722 &tls_ctx->schannel_cred, // Package specific data
1723 NULL, // Pointer to GetKey() func
1724 NULL, // Value to pass to GetKey()
1725 &hCreds, // (out) Cred Handle
1726 &tsExpiry); // (out) Lifetime (optional)
1727 if(Status != SEC_E_OK) {
1728 wprintf(L"Error 0x%x returned by AcquireCredentialsHandle\n", Status);
1729 continue;
1730 }
1731
1732 // Destroy the old credentials.
1733 tls_ctx->sspi->FreeCredentialsHandle(&tls_ctx->h_client_creds);
1734
1735 tls_ctx->h_client_creds = hCreds;
1736
1737 //
1738 // As you can see, this sample code maintains a single credential
1739 // handle, replacing it as necessary. This is a little unusual.
1740 //
1741 // Many applications maintain a global credential handle that's
1742 // anonymous (that is, it doesn't contain a client certificate),
1743 // which is used to connect to all servers. If a particular server
1744 // should require client authentication, then a new credential
1745 // is created for use when connecting to that server. The global
1746 // anonymous credential is retained for future connections to
1747 // other servers.
1748 //
1749 // Maintaining a single anonymous credential that's used whenever
1750 // possible is most efficient, since creating new credentials all
1751 // the time is rather expensive.
1752 //
1753
1754 break;
1755 }
1756}
1757