1 /***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 2012 - 2015, Marc Hoersken, <info@marc-hoersken.de>
9 * Copyright (C) 2012, Mark Salisbury, <mark.salisbury@hp.com>
10 * Copyright (C) 2012 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al.
11 *
12 * This software is licensed as described in the file COPYING, which
13 * you should have received as part of this distribution. The terms
14 * are also available at http://curl.haxx.se/docs/copyright.html.
15 *
16 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
17 * copies of the Software, and permit persons to whom the Software is
18 * furnished to do so, under the terms of the COPYING file.
19 *
20 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
21 * KIND, either express or implied.
22 *
23 ***************************************************************************/
24
25 /*
26 * Source file for all SChannel-specific code for the TLS/SSL layer. No code
27 * but vtls.c should ever call or use these functions.
28 *
29 */
30
31 /*
32 * Based upon the PolarSSL implementation in polarssl.c and polarssl.h:
33 * Copyright (C) 2010, 2011, Hoi-Ho Chan, <hoiho.chan@gmail.com>
34 *
35 * Based upon the CyaSSL implementation in cyassl.c and cyassl.h:
36 * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
37 *
38 * Thanks for code and inspiration!
39 */
40
41 #include "curl_setup.h"
42
43 #ifdef USE_SCHANNEL
44
45 #ifndef USE_WINDOWS_SSPI
46 # error "Can't compile SCHANNEL support without SSPI."
47 #endif
48
49 #include "curl_sspi.h"
50 #include "schannel.h"
51 #include "vtls.h"
52 #include "sendf.h"
53 #include "connect.h" /* for the connect timeout */
54 #include "strerror.h"
55 #include "select.h" /* for the socket readyness */
56 #include "inet_pton.h" /* for IP addr SNI check */
57 #include "curl_multibyte.h"
58 #include "warnless.h"
59 #include "curl_printf.h"
60 #include "curl_memory.h"
61 /* The last #include file should be: */
62 #include "memdebug.h"
63
64 /* Uncomment to force verbose output
65 * #define infof(x, y, ...) printf(y, __VA_ARGS__)
66 * #define failf(x, y, ...) printf(y, __VA_ARGS__)
67 */
68
69 static Curl_recv schannel_recv;
70 static Curl_send schannel_send;
71
72 #ifdef _WIN32_WCE
73 static CURLcode verify_certificate(struct connectdata *conn, int sockindex);
74 #endif
75
InitSecBuffer(SecBuffer * buffer,unsigned long BufType,void * BufDataPtr,unsigned long BufByteSize)76 static void InitSecBuffer(SecBuffer *buffer, unsigned long BufType,
77 void *BufDataPtr, unsigned long BufByteSize)
78 {
79 buffer->cbBuffer = BufByteSize;
80 buffer->BufferType = BufType;
81 buffer->pvBuffer = BufDataPtr;
82 }
83
InitSecBufferDesc(SecBufferDesc * desc,SecBuffer * BufArr,unsigned long NumArrElem)84 static void InitSecBufferDesc(SecBufferDesc *desc, SecBuffer *BufArr,
85 unsigned long NumArrElem)
86 {
87 desc->ulVersion = SECBUFFER_VERSION;
88 desc->pBuffers = BufArr;
89 desc->cBuffers = NumArrElem;
90 }
91
92 static CURLcode
schannel_connect_step1(struct connectdata * conn,int sockindex)93 schannel_connect_step1(struct connectdata *conn, int sockindex)
94 {
95 ssize_t written = -1;
96 struct SessionHandle *data = conn->data;
97 struct ssl_connect_data *connssl = &conn->ssl[sockindex];
98 SecBuffer outbuf;
99 SecBufferDesc outbuf_desc;
100 SCHANNEL_CRED schannel_cred;
101 SECURITY_STATUS sspi_status = SEC_E_OK;
102 struct curl_schannel_cred *old_cred = NULL;
103 struct in_addr addr;
104 #ifdef ENABLE_IPV6
105 struct in6_addr addr6;
106 #endif
107 TCHAR *host_name;
108 CURLcode result;
109
110 infof(data, "schannel: SSL/TLS connection with %s port %hu (step 1/3)\n",
111 conn->host.name, conn->remote_port);
112
113 /* check for an existing re-usable credential handle */
114 if(!Curl_ssl_getsessionid(conn, (void **)&old_cred, NULL)) {
115 connssl->cred = old_cred;
116 infof(data, "schannel: re-using existing credential handle\n");
117 }
118 else {
119 /* setup Schannel API options */
120 memset(&schannel_cred, 0, sizeof(schannel_cred));
121 schannel_cred.dwVersion = SCHANNEL_CRED_VERSION;
122
123 if(data->set.ssl.verifypeer) {
124 #ifdef _WIN32_WCE
125 /* certificate validation on CE doesn't seem to work right; we'll
126 do it following a more manual process. */
127 schannel_cred.dwFlags = SCH_CRED_MANUAL_CRED_VALIDATION |
128 SCH_CRED_IGNORE_NO_REVOCATION_CHECK |
129 SCH_CRED_IGNORE_REVOCATION_OFFLINE;
130 #else
131 schannel_cred.dwFlags = SCH_CRED_AUTO_CRED_VALIDATION |
132 SCH_CRED_REVOCATION_CHECK_CHAIN;
133 #endif
134 infof(data, "schannel: checking server certificate revocation\n");
135 }
136 else {
137 schannel_cred.dwFlags = SCH_CRED_MANUAL_CRED_VALIDATION |
138 SCH_CRED_IGNORE_NO_REVOCATION_CHECK |
139 SCH_CRED_IGNORE_REVOCATION_OFFLINE;
140 infof(data, "schannel: disable server certificate revocation checks\n");
141 }
142
143 if(!data->set.ssl.verifyhost) {
144 schannel_cred.dwFlags |= SCH_CRED_NO_SERVERNAME_CHECK;
145 infof(data, "schannel: verifyhost setting prevents Schannel from "
146 "comparing the supplied target name with the subject "
147 "names in server certificates. Also disables SNI.\n");
148 }
149
150 switch(data->set.ssl.version) {
151 default:
152 case CURL_SSLVERSION_DEFAULT:
153 case CURL_SSLVERSION_TLSv1:
154 schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1_0_CLIENT |
155 SP_PROT_TLS1_1_CLIENT |
156 SP_PROT_TLS1_2_CLIENT;
157 break;
158 case CURL_SSLVERSION_TLSv1_0:
159 schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1_0_CLIENT;
160 break;
161 case CURL_SSLVERSION_TLSv1_1:
162 schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1_1_CLIENT;
163 break;
164 case CURL_SSLVERSION_TLSv1_2:
165 schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1_2_CLIENT;
166 break;
167 case CURL_SSLVERSION_SSLv3:
168 schannel_cred.grbitEnabledProtocols = SP_PROT_SSL3_CLIENT;
169 break;
170 case CURL_SSLVERSION_SSLv2:
171 schannel_cred.grbitEnabledProtocols = SP_PROT_SSL2_CLIENT;
172 break;
173 }
174
175 /* allocate memory for the re-usable credential handle */
176 connssl->cred = (struct curl_schannel_cred *)
177 malloc(sizeof(struct curl_schannel_cred));
178 if(!connssl->cred) {
179 failf(data, "schannel: unable to allocate memory");
180 return CURLE_OUT_OF_MEMORY;
181 }
182 memset(connssl->cred, 0, sizeof(struct curl_schannel_cred));
183
184 /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa374716.aspx */
185 sspi_status =
186 s_pSecFn->AcquireCredentialsHandle(NULL, (TCHAR *)UNISP_NAME,
187 SECPKG_CRED_OUTBOUND, NULL,
188 &schannel_cred, NULL, NULL,
189 &connssl->cred->cred_handle,
190 &connssl->cred->time_stamp);
191
192 if(sspi_status != SEC_E_OK) {
193 if(sspi_status == SEC_E_WRONG_PRINCIPAL)
194 failf(data, "schannel: SNI or certificate check failed: %s",
195 Curl_sspi_strerror(conn, sspi_status));
196 else
197 failf(data, "schannel: AcquireCredentialsHandle failed: %s",
198 Curl_sspi_strerror(conn, sspi_status));
199 Curl_safefree(connssl->cred);
200 return CURLE_SSL_CONNECT_ERROR;
201 }
202 }
203
204 /* Warn if SNI is disabled due to use of an IP address */
205 if(Curl_inet_pton(AF_INET, conn->host.name, &addr)
206 #ifdef ENABLE_IPV6
207 || Curl_inet_pton(AF_INET6, conn->host.name, &addr6)
208 #endif
209 ) {
210 infof(data, "schannel: using IP address, SNI is not supported by OS.\n");
211 }
212
213 /* setup output buffer */
214 InitSecBuffer(&outbuf, SECBUFFER_EMPTY, NULL, 0);
215 InitSecBufferDesc(&outbuf_desc, &outbuf, 1);
216
217 /* setup request flags */
218 connssl->req_flags = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT |
219 ISC_REQ_CONFIDENTIALITY | ISC_REQ_ALLOCATE_MEMORY |
220 ISC_REQ_STREAM;
221
222 /* allocate memory for the security context handle */
223 connssl->ctxt = (struct curl_schannel_ctxt *)
224 malloc(sizeof(struct curl_schannel_ctxt));
225 if(!connssl->ctxt) {
226 failf(data, "schannel: unable to allocate memory");
227 return CURLE_OUT_OF_MEMORY;
228 }
229 memset(connssl->ctxt, 0, sizeof(struct curl_schannel_ctxt));
230
231 host_name = Curl_convert_UTF8_to_tchar(conn->host.name);
232 if(!host_name)
233 return CURLE_OUT_OF_MEMORY;
234
235 /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa375924.aspx */
236
237 sspi_status = s_pSecFn->InitializeSecurityContext(
238 &connssl->cred->cred_handle, NULL, host_name,
239 connssl->req_flags, 0, 0, NULL, 0, &connssl->ctxt->ctxt_handle,
240 &outbuf_desc, &connssl->ret_flags, &connssl->ctxt->time_stamp);
241
242 Curl_unicodefree(host_name);
243
244 if(sspi_status != SEC_I_CONTINUE_NEEDED) {
245 if(sspi_status == SEC_E_WRONG_PRINCIPAL)
246 failf(data, "schannel: SNI or certificate check failed: %s",
247 Curl_sspi_strerror(conn, sspi_status));
248 else
249 failf(data, "schannel: initial InitializeSecurityContext failed: %s",
250 Curl_sspi_strerror(conn, sspi_status));
251 Curl_safefree(connssl->ctxt);
252 return CURLE_SSL_CONNECT_ERROR;
253 }
254
255 infof(data, "schannel: sending initial handshake data: "
256 "sending %lu bytes...\n", outbuf.cbBuffer);
257
258 /* send initial handshake data which is now stored in output buffer */
259 result = Curl_write_plain(conn, conn->sock[sockindex], outbuf.pvBuffer,
260 outbuf.cbBuffer, &written);
261 s_pSecFn->FreeContextBuffer(outbuf.pvBuffer);
262 if((result != CURLE_OK) || (outbuf.cbBuffer != (size_t) written)) {
263 failf(data, "schannel: failed to send initial handshake data: "
264 "sent %zd of %lu bytes", written, outbuf.cbBuffer);
265 return CURLE_SSL_CONNECT_ERROR;
266 }
267
268 infof(data, "schannel: sent initial handshake data: "
269 "sent %zd bytes\n", written);
270
271 connssl->recv_unrecoverable_err = CURLE_OK;
272 connssl->recv_sspi_close_notify = false;
273 connssl->recv_connection_closed = false;
274
275 /* continue to second handshake step */
276 connssl->connecting_state = ssl_connect_2;
277
278 return CURLE_OK;
279 }
280
281 static CURLcode
schannel_connect_step2(struct connectdata * conn,int sockindex)282 schannel_connect_step2(struct connectdata *conn, int sockindex)
283 {
284 int i;
285 ssize_t nread = -1, written = -1;
286 struct SessionHandle *data = conn->data;
287 struct ssl_connect_data *connssl = &conn->ssl[sockindex];
288 unsigned char *reallocated_buffer;
289 size_t reallocated_length;
290 SecBuffer outbuf[3];
291 SecBufferDesc outbuf_desc;
292 SecBuffer inbuf[2];
293 SecBufferDesc inbuf_desc;
294 SECURITY_STATUS sspi_status = SEC_E_OK;
295 TCHAR *host_name;
296 CURLcode result;
297 bool doread;
298
299 doread = (connssl->connecting_state != ssl_connect_2_writing) ? TRUE : FALSE;
300
301 infof(data, "schannel: SSL/TLS connection with %s port %hu (step 2/3)\n",
302 conn->host.name, conn->remote_port);
303
304 if(!connssl->cred || !connssl->ctxt)
305 return CURLE_SSL_CONNECT_ERROR;
306
307 /* buffer to store previously received and decrypted data */
308 if(connssl->decdata_buffer == NULL) {
309 connssl->decdata_offset = 0;
310 connssl->decdata_length = CURL_SCHANNEL_BUFFER_INIT_SIZE;
311 connssl->decdata_buffer = malloc(connssl->decdata_length);
312 if(connssl->decdata_buffer == NULL) {
313 failf(data, "schannel: unable to allocate memory");
314 return CURLE_OUT_OF_MEMORY;
315 }
316 }
317
318 /* buffer to store previously received and encrypted data */
319 if(connssl->encdata_buffer == NULL) {
320 connssl->encdata_offset = 0;
321 connssl->encdata_length = CURL_SCHANNEL_BUFFER_INIT_SIZE;
322 connssl->encdata_buffer = malloc(connssl->encdata_length);
323 if(connssl->encdata_buffer == NULL) {
324 failf(data, "schannel: unable to allocate memory");
325 return CURLE_OUT_OF_MEMORY;
326 }
327 }
328
329 /* if we need a bigger buffer to read a full message, increase buffer now */
330 if(connssl->encdata_length - connssl->encdata_offset <
331 CURL_SCHANNEL_BUFFER_FREE_SIZE) {
332 /* increase internal encrypted data buffer */
333 reallocated_length = connssl->encdata_offset +
334 CURL_SCHANNEL_BUFFER_FREE_SIZE;
335 reallocated_buffer = realloc(connssl->encdata_buffer,
336 reallocated_length);
337
338 if(reallocated_buffer == NULL) {
339 failf(data, "schannel: unable to re-allocate memory");
340 return CURLE_OUT_OF_MEMORY;
341 }
342 else {
343 connssl->encdata_buffer = reallocated_buffer;
344 connssl->encdata_length = reallocated_length;
345 }
346 }
347
348 for(;;) {
349 if(doread) {
350 /* read encrypted handshake data from socket */
351 result = Curl_read_plain(conn->sock[sockindex],
352 (char *) (connssl->encdata_buffer +
353 connssl->encdata_offset),
354 connssl->encdata_length -
355 connssl->encdata_offset,
356 &nread);
357 if(result == CURLE_AGAIN) {
358 if(connssl->connecting_state != ssl_connect_2_writing)
359 connssl->connecting_state = ssl_connect_2_reading;
360 infof(data, "schannel: failed to receive handshake, "
361 "need more data\n");
362 return CURLE_OK;
363 }
364 else if((result != CURLE_OK) || (nread == 0)) {
365 failf(data, "schannel: failed to receive handshake, "
366 "SSL/TLS connection failed");
367 return CURLE_SSL_CONNECT_ERROR;
368 }
369
370 /* increase encrypted data buffer offset */
371 connssl->encdata_offset += nread;
372 }
373
374 infof(data, "schannel: encrypted data buffer: offset %zu length %zu\n",
375 connssl->encdata_offset, connssl->encdata_length);
376
377 /* setup input buffers */
378 InitSecBuffer(&inbuf[0], SECBUFFER_TOKEN, malloc(connssl->encdata_offset),
379 curlx_uztoul(connssl->encdata_offset));
380 InitSecBuffer(&inbuf[1], SECBUFFER_EMPTY, NULL, 0);
381 InitSecBufferDesc(&inbuf_desc, inbuf, 2);
382
383 /* setup output buffers */
384 InitSecBuffer(&outbuf[0], SECBUFFER_TOKEN, NULL, 0);
385 InitSecBuffer(&outbuf[1], SECBUFFER_ALERT, NULL, 0);
386 InitSecBuffer(&outbuf[2], SECBUFFER_EMPTY, NULL, 0);
387 InitSecBufferDesc(&outbuf_desc, outbuf, 3);
388
389 if(inbuf[0].pvBuffer == NULL) {
390 failf(data, "schannel: unable to allocate memory");
391 return CURLE_OUT_OF_MEMORY;
392 }
393
394 /* copy received handshake data into input buffer */
395 memcpy(inbuf[0].pvBuffer, connssl->encdata_buffer,
396 connssl->encdata_offset);
397
398 host_name = Curl_convert_UTF8_to_tchar(conn->host.name);
399 if(!host_name)
400 return CURLE_OUT_OF_MEMORY;
401
402 /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa375924.aspx */
403
404 sspi_status = s_pSecFn->InitializeSecurityContext(
405 &connssl->cred->cred_handle, &connssl->ctxt->ctxt_handle,
406 host_name, connssl->req_flags, 0, 0, &inbuf_desc, 0, NULL,
407 &outbuf_desc, &connssl->ret_flags, &connssl->ctxt->time_stamp);
408
409 Curl_unicodefree(host_name);
410
411 /* free buffer for received handshake data */
412 Curl_safefree(inbuf[0].pvBuffer);
413
414 /* check if the handshake was incomplete */
415 if(sspi_status == SEC_E_INCOMPLETE_MESSAGE) {
416 connssl->connecting_state = ssl_connect_2_reading;
417 infof(data, "schannel: received incomplete message, need more data\n");
418 return CURLE_OK;
419 }
420
421 /* If the server has requested a client certificate, attempt to continue
422 the handshake without one. This will allow connections to servers which
423 request a client certificate but do not require it. */
424 if(sspi_status == SEC_I_INCOMPLETE_CREDENTIALS &&
425 !(connssl->req_flags & ISC_REQ_USE_SUPPLIED_CREDS)) {
426 connssl->req_flags |= ISC_REQ_USE_SUPPLIED_CREDS;
427 connssl->connecting_state = ssl_connect_2_writing;
428 infof(data, "schannel: a client certificate has been requested\n");
429 return CURLE_OK;
430 }
431
432 /* check if the handshake needs to be continued */
433 if(sspi_status == SEC_I_CONTINUE_NEEDED || sspi_status == SEC_E_OK) {
434 for(i = 0; i < 3; i++) {
435 /* search for handshake tokens that need to be send */
436 if(outbuf[i].BufferType == SECBUFFER_TOKEN && outbuf[i].cbBuffer > 0) {
437 infof(data, "schannel: sending next handshake data: "
438 "sending %lu bytes...\n", outbuf[i].cbBuffer);
439
440 /* send handshake token to server */
441 result = Curl_write_plain(conn, conn->sock[sockindex],
442 outbuf[i].pvBuffer, outbuf[i].cbBuffer,
443 &written);
444 if((result != CURLE_OK) ||
445 (outbuf[i].cbBuffer != (size_t) written)) {
446 failf(data, "schannel: failed to send next handshake data: "
447 "sent %zd of %lu bytes", written, outbuf[i].cbBuffer);
448 return CURLE_SSL_CONNECT_ERROR;
449 }
450 }
451
452 /* free obsolete buffer */
453 if(outbuf[i].pvBuffer != NULL) {
454 s_pSecFn->FreeContextBuffer(outbuf[i].pvBuffer);
455 }
456 }
457 }
458 else {
459 if(sspi_status == SEC_E_WRONG_PRINCIPAL)
460 failf(data, "schannel: SNI or certificate check failed: %s",
461 Curl_sspi_strerror(conn, sspi_status));
462 else
463 failf(data, "schannel: next InitializeSecurityContext failed: %s",
464 Curl_sspi_strerror(conn, sspi_status));
465 return CURLE_SSL_CONNECT_ERROR;
466 }
467
468 /* check if there was additional remaining encrypted data */
469 if(inbuf[1].BufferType == SECBUFFER_EXTRA && inbuf[1].cbBuffer > 0) {
470 infof(data, "schannel: encrypted data length: %lu\n", inbuf[1].cbBuffer);
471 /*
472 There are two cases where we could be getting extra data here:
473 1) If we're renegotiating a connection and the handshake is already
474 complete (from the server perspective), it can encrypted app data
475 (not handshake data) in an extra buffer at this point.
476 2) (sspi_status == SEC_I_CONTINUE_NEEDED) We are negotiating a
477 connection and this extra data is part of the handshake.
478 We should process the data immediately; waiting for the socket to
479 be ready may fail since the server is done sending handshake data.
480 */
481 /* check if the remaining data is less than the total amount
482 and therefore begins after the already processed data */
483 if(connssl->encdata_offset > inbuf[1].cbBuffer) {
484 memmove(connssl->encdata_buffer,
485 (connssl->encdata_buffer + connssl->encdata_offset) -
486 inbuf[1].cbBuffer, inbuf[1].cbBuffer);
487 connssl->encdata_offset = inbuf[1].cbBuffer;
488 if(sspi_status == SEC_I_CONTINUE_NEEDED) {
489 doread = FALSE;
490 continue;
491 }
492 }
493 }
494 else {
495 connssl->encdata_offset = 0;
496 }
497 break;
498 }
499
500 /* check if the handshake needs to be continued */
501 if(sspi_status == SEC_I_CONTINUE_NEEDED) {
502 connssl->connecting_state = ssl_connect_2_reading;
503 return CURLE_OK;
504 }
505
506 /* check if the handshake is complete */
507 if(sspi_status == SEC_E_OK) {
508 connssl->connecting_state = ssl_connect_3;
509 infof(data, "schannel: SSL/TLS handshake complete\n");
510 }
511
512 #ifdef _WIN32_WCE
513 /* Windows CE doesn't do any server certificate validation.
514 We have to do it manually. */
515 if(data->set.ssl.verifypeer)
516 return verify_certificate(conn, sockindex);
517 #endif
518
519 return CURLE_OK;
520 }
521
522 static CURLcode
schannel_connect_step3(struct connectdata * conn,int sockindex)523 schannel_connect_step3(struct connectdata *conn, int sockindex)
524 {
525 CURLcode result = CURLE_OK;
526 struct SessionHandle *data = conn->data;
527 struct ssl_connect_data *connssl = &conn->ssl[sockindex];
528 struct curl_schannel_cred *old_cred = NULL;
529 bool incache;
530
531 DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
532
533 infof(data, "schannel: SSL/TLS connection with %s port %hu (step 3/3)\n",
534 conn->host.name, conn->remote_port);
535
536 if(!connssl->cred)
537 return CURLE_SSL_CONNECT_ERROR;
538
539 /* check if the required context attributes are met */
540 if(connssl->ret_flags != connssl->req_flags) {
541 if(!(connssl->ret_flags & ISC_RET_SEQUENCE_DETECT))
542 failf(data, "schannel: failed to setup sequence detection");
543 if(!(connssl->ret_flags & ISC_RET_REPLAY_DETECT))
544 failf(data, "schannel: failed to setup replay detection");
545 if(!(connssl->ret_flags & ISC_RET_CONFIDENTIALITY))
546 failf(data, "schannel: failed to setup confidentiality");
547 if(!(connssl->ret_flags & ISC_RET_ALLOCATED_MEMORY))
548 failf(data, "schannel: failed to setup memory allocation");
549 if(!(connssl->ret_flags & ISC_RET_STREAM))
550 failf(data, "schannel: failed to setup stream orientation");
551 return CURLE_SSL_CONNECT_ERROR;
552 }
553
554 /* increment the reference counter of the credential/session handle */
555 if(connssl->cred && connssl->ctxt) {
556 connssl->cred->refcount++;
557 infof(data, "schannel: incremented credential handle refcount = %d\n",
558 connssl->cred->refcount);
559 }
560
561 /* save the current session data for possible re-use */
562 incache = !(Curl_ssl_getsessionid(conn, (void **)&old_cred, NULL));
563 if(incache) {
564 if(old_cred != connssl->cred) {
565 infof(data, "schannel: old credential handle is stale, removing\n");
566 Curl_ssl_delsessionid(conn, (void *)old_cred);
567 incache = FALSE;
568 }
569 }
570
571 if(!incache) {
572 result = Curl_ssl_addsessionid(conn, (void *)connssl->cred,
573 sizeof(struct curl_schannel_cred));
574 if(result) {
575 failf(data, "schannel: failed to store credential handle");
576 return result;
577 }
578 else {
579 connssl->cred->cached = TRUE;
580 infof(data, "schannel: stored credential handle in session cache\n");
581 }
582 }
583
584 connssl->connecting_state = ssl_connect_done;
585
586 return CURLE_OK;
587 }
588
589 static CURLcode
schannel_connect_common(struct connectdata * conn,int sockindex,bool nonblocking,bool * done)590 schannel_connect_common(struct connectdata *conn, int sockindex,
591 bool nonblocking, bool *done)
592 {
593 CURLcode result;
594 struct SessionHandle *data = conn->data;
595 struct ssl_connect_data *connssl = &conn->ssl[sockindex];
596 curl_socket_t sockfd = conn->sock[sockindex];
597 long timeout_ms;
598 int what;
599
600 /* check if the connection has already been established */
601 if(ssl_connection_complete == connssl->state) {
602 *done = TRUE;
603 return CURLE_OK;
604 }
605
606 if(ssl_connect_1 == connssl->connecting_state) {
607 /* check out how much more time we're allowed */
608 timeout_ms = Curl_timeleft(data, NULL, TRUE);
609
610 if(timeout_ms < 0) {
611 /* no need to continue if time already is up */
612 failf(data, "SSL/TLS connection timeout");
613 return CURLE_OPERATION_TIMEDOUT;
614 }
615
616 result = schannel_connect_step1(conn, sockindex);
617 if(result)
618 return result;
619 }
620
621 while(ssl_connect_2 == connssl->connecting_state ||
622 ssl_connect_2_reading == connssl->connecting_state ||
623 ssl_connect_2_writing == connssl->connecting_state) {
624
625 /* check out how much more time we're allowed */
626 timeout_ms = Curl_timeleft(data, NULL, TRUE);
627
628 if(timeout_ms < 0) {
629 /* no need to continue if time already is up */
630 failf(data, "SSL/TLS connection timeout");
631 return CURLE_OPERATION_TIMEDOUT;
632 }
633
634 /* if ssl is expecting something, check if it's available. */
635 if(connssl->connecting_state == ssl_connect_2_reading
636 || connssl->connecting_state == ssl_connect_2_writing) {
637
638 curl_socket_t writefd = ssl_connect_2_writing ==
639 connssl->connecting_state ? sockfd : CURL_SOCKET_BAD;
640 curl_socket_t readfd = ssl_connect_2_reading ==
641 connssl->connecting_state ? sockfd : CURL_SOCKET_BAD;
642
643 what = Curl_socket_ready(readfd, writefd, nonblocking ? 0 : timeout_ms);
644 if(what < 0) {
645 /* fatal error */
646 failf(data, "select/poll on SSL/TLS socket, errno: %d", SOCKERRNO);
647 return CURLE_SSL_CONNECT_ERROR;
648 }
649 else if(0 == what) {
650 if(nonblocking) {
651 *done = FALSE;
652 return CURLE_OK;
653 }
654 else {
655 /* timeout */
656 failf(data, "SSL/TLS connection timeout");
657 return CURLE_OPERATION_TIMEDOUT;
658 }
659 }
660 /* socket is readable or writable */
661 }
662
663 /* Run transaction, and return to the caller if it failed or if
664 * this connection is part of a multi handle and this loop would
665 * execute again. This permits the owner of a multi handle to
666 * abort a connection attempt before step2 has completed while
667 * ensuring that a client using select() or epoll() will always
668 * have a valid fdset to wait on.
669 */
670 result = schannel_connect_step2(conn, sockindex);
671 if(result || (nonblocking &&
672 (ssl_connect_2 == connssl->connecting_state ||
673 ssl_connect_2_reading == connssl->connecting_state ||
674 ssl_connect_2_writing == connssl->connecting_state)))
675 return result;
676
677 } /* repeat step2 until all transactions are done. */
678
679 if(ssl_connect_3 == connssl->connecting_state) {
680 result = schannel_connect_step3(conn, sockindex);
681 if(result)
682 return result;
683 }
684
685 if(ssl_connect_done == connssl->connecting_state) {
686 connssl->state = ssl_connection_complete;
687 conn->recv[sockindex] = schannel_recv;
688 conn->send[sockindex] = schannel_send;
689 *done = TRUE;
690 }
691 else
692 *done = FALSE;
693
694 /* reset our connection state machine */
695 connssl->connecting_state = ssl_connect_1;
696
697 return CURLE_OK;
698 }
699
700 static ssize_t
schannel_send(struct connectdata * conn,int sockindex,const void * buf,size_t len,CURLcode * err)701 schannel_send(struct connectdata *conn, int sockindex,
702 const void *buf, size_t len, CURLcode *err)
703 {
704 ssize_t written = -1;
705 size_t data_len = 0;
706 unsigned char *data = NULL;
707 struct ssl_connect_data *connssl = &conn->ssl[sockindex];
708 SecBuffer outbuf[4];
709 SecBufferDesc outbuf_desc;
710 SECURITY_STATUS sspi_status = SEC_E_OK;
711 CURLcode result;
712
713 /* check if the maximum stream sizes were queried */
714 if(connssl->stream_sizes.cbMaximumMessage == 0) {
715 sspi_status = s_pSecFn->QueryContextAttributes(
716 &connssl->ctxt->ctxt_handle,
717 SECPKG_ATTR_STREAM_SIZES,
718 &connssl->stream_sizes);
719 if(sspi_status != SEC_E_OK) {
720 *err = CURLE_SEND_ERROR;
721 return -1;
722 }
723 }
724
725 /* check if the buffer is longer than the maximum message length */
726 if(len > connssl->stream_sizes.cbMaximumMessage) {
727 *err = CURLE_SEND_ERROR;
728 return -1;
729 }
730
731 /* calculate the complete message length and allocate a buffer for it */
732 data_len = connssl->stream_sizes.cbHeader + len +
733 connssl->stream_sizes.cbTrailer;
734 data = (unsigned char *) malloc(data_len);
735 if(data == NULL) {
736 *err = CURLE_OUT_OF_MEMORY;
737 return -1;
738 }
739
740 /* setup output buffers (header, data, trailer, empty) */
741 InitSecBuffer(&outbuf[0], SECBUFFER_STREAM_HEADER,
742 data, connssl->stream_sizes.cbHeader);
743 InitSecBuffer(&outbuf[1], SECBUFFER_DATA,
744 data + connssl->stream_sizes.cbHeader, curlx_uztoul(len));
745 InitSecBuffer(&outbuf[2], SECBUFFER_STREAM_TRAILER,
746 data + connssl->stream_sizes.cbHeader + len,
747 connssl->stream_sizes.cbTrailer);
748 InitSecBuffer(&outbuf[3], SECBUFFER_EMPTY, NULL, 0);
749 InitSecBufferDesc(&outbuf_desc, outbuf, 4);
750
751 /* copy data into output buffer */
752 memcpy(outbuf[1].pvBuffer, buf, len);
753
754 /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa375390.aspx */
755 sspi_status = s_pSecFn->EncryptMessage(&connssl->ctxt->ctxt_handle, 0,
756 &outbuf_desc, 0);
757
758 /* check if the message was encrypted */
759 if(sspi_status == SEC_E_OK) {
760 written = 0;
761
762 /* send the encrypted message including header, data and trailer */
763 len = outbuf[0].cbBuffer + outbuf[1].cbBuffer + outbuf[2].cbBuffer;
764
765 /*
766 It's important to send the full message which includes the header,
767 encrypted payload, and trailer. Until the client receives all the
768 data a coherent message has not been delivered and the client
769 can't read any of it.
770
771 If we wanted to buffer the unwritten encrypted bytes, we would
772 tell the client that all data it has requested to be sent has been
773 sent. The unwritten encrypted bytes would be the first bytes to
774 send on the next invocation.
775 Here's the catch with this - if we tell the client that all the
776 bytes have been sent, will the client call this method again to
777 send the buffered data? Looking at who calls this function, it
778 seems the answer is NO.
779 */
780
781 /* send entire message or fail */
782 while(len > (size_t)written) {
783 ssize_t this_write;
784 long timeleft;
785 int what;
786
787 this_write = 0;
788
789 timeleft = Curl_timeleft(conn->data, NULL, FALSE);
790 if(timeleft < 0) {
791 /* we already got the timeout */
792 failf(conn->data, "schannel: timed out sending data "
793 "(bytes sent: %zd)", written);
794 *err = CURLE_OPERATION_TIMEDOUT;
795 written = -1;
796 break;
797 }
798
799 what = Curl_socket_ready(CURL_SOCKET_BAD, conn->sock[sockindex],
800 timeleft);
801 if(what < 0) {
802 /* fatal error */
803 failf(conn->data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
804 *err = CURLE_SEND_ERROR;
805 written = -1;
806 break;
807 }
808 else if(0 == what) {
809 failf(conn->data, "schannel: timed out sending data "
810 "(bytes sent: %zd)", written);
811 *err = CURLE_OPERATION_TIMEDOUT;
812 written = -1;
813 break;
814 }
815 /* socket is writable */
816
817 result = Curl_write_plain(conn, conn->sock[sockindex], data + written,
818 len - written, &this_write);
819 if(result == CURLE_AGAIN)
820 continue;
821 else if(result != CURLE_OK) {
822 *err = result;
823 written = -1;
824 break;
825 }
826
827 written += this_write;
828 }
829 }
830 else if(sspi_status == SEC_E_INSUFFICIENT_MEMORY) {
831 *err = CURLE_OUT_OF_MEMORY;
832 }
833 else{
834 *err = CURLE_SEND_ERROR;
835 }
836
837 Curl_safefree(data);
838
839 if(len == (size_t)written)
840 /* Encrypted message including header, data and trailer entirely sent.
841 The return value is the number of unencrypted bytes that were sent. */
842 written = outbuf[1].cbBuffer;
843
844 return written;
845 }
846
847 static ssize_t
schannel_recv(struct connectdata * conn,int sockindex,char * buf,size_t len,CURLcode * err)848 schannel_recv(struct connectdata *conn, int sockindex,
849 char *buf, size_t len, CURLcode *err)
850 {
851 size_t size = 0;
852 ssize_t nread = -1;
853 struct SessionHandle *data = conn->data;
854 struct ssl_connect_data *connssl = &conn->ssl[sockindex];
855 unsigned char *reallocated_buffer;
856 size_t reallocated_length;
857 bool done = FALSE;
858 SecBuffer inbuf[4];
859 SecBufferDesc inbuf_desc;
860 SECURITY_STATUS sspi_status = SEC_E_OK;
861 /* we want the length of the encrypted buffer to be at least large enough
862 that it can hold all the bytes requested and some TLS record overhead. */
863 size_t min_encdata_length = len + CURL_SCHANNEL_BUFFER_FREE_SIZE;
864
865 /****************************************************************************
866 * Don't return or set connssl->recv_unrecoverable_err unless in the cleanup.
867 * The pattern for return error is set *err, optional infof, goto cleanup.
868 *
869 * Our priority is to always return as much decrypted data to the caller as
870 * possible, even if an error occurs. The state of the decrypted buffer must
871 * always be valid. Transfer of decrypted data to the caller's buffer is
872 * handled in the cleanup.
873 */
874
875 infof(data, "schannel: client wants to read %zu bytes\n", len);
876 *err = CURLE_OK;
877
878 if(len && len <= connssl->decdata_offset) {
879 infof(data, "schannel: enough decrypted data is already available\n");
880 goto cleanup;
881 }
882 else if(connssl->recv_unrecoverable_err) {
883 *err = connssl->recv_unrecoverable_err;
884 infof(data, "schannel: an unrecoverable error occurred in a prior call\n");
885 goto cleanup;
886 }
887 else if(connssl->recv_sspi_close_notify) {
888 /* once a server has indicated shutdown there is no more encrypted data */
889 infof(data, "schannel: server indicated shutdown in a prior call\n");
890 goto cleanup;
891 }
892 else if(!len) {
893 /* It's debatable what to return when !len. Regardless we can't return
894 immediately because there may be data to decrypt (in the case we want to
895 decrypt all encrypted cached data) so handle !len later in cleanup.
896 */
897 ; /* do nothing */
898 }
899 else if(!connssl->recv_connection_closed) {
900 /* increase enc buffer in order to fit the requested amount of data */
901 size = connssl->encdata_length - connssl->encdata_offset;
902 if(size < CURL_SCHANNEL_BUFFER_FREE_SIZE ||
903 connssl->encdata_length < min_encdata_length) {
904 reallocated_length = connssl->encdata_offset +
905 CURL_SCHANNEL_BUFFER_FREE_SIZE;
906 if(reallocated_length < min_encdata_length) {
907 reallocated_length = min_encdata_length;
908 }
909 reallocated_buffer = realloc(connssl->encdata_buffer,
910 reallocated_length);
911 if(reallocated_buffer == NULL) {
912 *err = CURLE_OUT_OF_MEMORY;
913 failf(data, "schannel: unable to re-allocate memory");
914 goto cleanup;
915 }
916
917 connssl->encdata_buffer = reallocated_buffer;
918 connssl->encdata_length = reallocated_length;
919 size = connssl->encdata_length - connssl->encdata_offset;
920 infof(data, "schannel: encdata_buffer resized %zu\n",
921 connssl->encdata_length);
922 }
923
924 infof(data, "schannel: encrypted data buffer: offset %zu length %zu\n",
925 connssl->encdata_offset, connssl->encdata_length);
926
927 /* read encrypted data from socket */
928 *err = Curl_read_plain(conn->sock[sockindex],
929 (char *)(connssl->encdata_buffer +
930 connssl->encdata_offset),
931 size, &nread);
932 if(*err) {
933 nread = -1;
934 if(*err == CURLE_AGAIN)
935 infof(data, "schannel: Curl_read_plain returned CURLE_AGAIN\n");
936 else if(*err == CURLE_RECV_ERROR)
937 infof(data, "schannel: Curl_read_plain returned CURLE_RECV_ERROR\n");
938 else
939 infof(data, "schannel: Curl_read_plain returned error %d\n", *err);
940 }
941 else if(nread == 0) {
942 connssl->recv_connection_closed = true;
943 infof(data, "schannel: server closed the connection\n");
944 }
945 else if(nread > 0) {
946 connssl->encdata_offset += (size_t)nread;
947 infof(data, "schannel: encrypted data got %zd\n", nread);
948 }
949 }
950
951 infof(data, "schannel: encrypted data buffer: offset %zu length %zu\n",
952 connssl->encdata_offset, connssl->encdata_length);
953
954 /* decrypt loop */
955 while(connssl->encdata_offset > 0 && sspi_status == SEC_E_OK &&
956 (!len || connssl->decdata_offset < len ||
957 connssl->recv_connection_closed)) {
958 /* prepare data buffer for DecryptMessage call */
959 InitSecBuffer(&inbuf[0], SECBUFFER_DATA, connssl->encdata_buffer,
960 curlx_uztoul(connssl->encdata_offset));
961
962 /* we need 3 more empty input buffers for possible output */
963 InitSecBuffer(&inbuf[1], SECBUFFER_EMPTY, NULL, 0);
964 InitSecBuffer(&inbuf[2], SECBUFFER_EMPTY, NULL, 0);
965 InitSecBuffer(&inbuf[3], SECBUFFER_EMPTY, NULL, 0);
966 InitSecBufferDesc(&inbuf_desc, inbuf, 4);
967
968 /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa375348.aspx */
969 sspi_status = s_pSecFn->DecryptMessage(&connssl->ctxt->ctxt_handle,
970 &inbuf_desc, 0, NULL);
971
972 /* check if everything went fine (server may want to renegotiate
973 or shutdown the connection context) */
974 if(sspi_status == SEC_E_OK || sspi_status == SEC_I_RENEGOTIATE ||
975 sspi_status == SEC_I_CONTEXT_EXPIRED) {
976 /* check for successfully decrypted data, even before actual
977 renegotiation or shutdown of the connection context */
978 if(inbuf[1].BufferType == SECBUFFER_DATA) {
979 infof(data, "schannel: decrypted data length: %lu\n",
980 inbuf[1].cbBuffer);
981
982 /* increase buffer in order to fit the received amount of data */
983 size = inbuf[1].cbBuffer > CURL_SCHANNEL_BUFFER_FREE_SIZE ?
984 inbuf[1].cbBuffer : CURL_SCHANNEL_BUFFER_FREE_SIZE;
985 if(connssl->decdata_length - connssl->decdata_offset < size ||
986 connssl->decdata_length < len) {
987 /* increase internal decrypted data buffer */
988 reallocated_length = connssl->decdata_offset + size;
989 /* make sure that the requested amount of data fits */
990 if(reallocated_length < len) {
991 reallocated_length = len;
992 }
993 reallocated_buffer = realloc(connssl->decdata_buffer,
994 reallocated_length);
995 if(reallocated_buffer == NULL) {
996 *err = CURLE_OUT_OF_MEMORY;
997 failf(data, "schannel: unable to re-allocate memory");
998 goto cleanup;
999 }
1000 connssl->decdata_buffer = reallocated_buffer;
1001 connssl->decdata_length = reallocated_length;
1002 }
1003
1004 /* copy decrypted data to internal buffer */
1005 size = inbuf[1].cbBuffer;
1006 if(size) {
1007 memcpy(connssl->decdata_buffer + connssl->decdata_offset,
1008 inbuf[1].pvBuffer, size);
1009 connssl->decdata_offset += size;
1010 }
1011
1012 infof(data, "schannel: decrypted data added: %zu\n", size);
1013 infof(data, "schannel: decrypted data cached: offset %zu length %zu\n",
1014 connssl->decdata_offset, connssl->decdata_length);
1015 }
1016
1017 /* check for remaining encrypted data */
1018 if(inbuf[3].BufferType == SECBUFFER_EXTRA && inbuf[3].cbBuffer > 0) {
1019 infof(data, "schannel: encrypted data length: %lu\n",
1020 inbuf[3].cbBuffer);
1021
1022 /* check if the remaining data is less than the total amount
1023 * and therefore begins after the already processed data
1024 */
1025 if(connssl->encdata_offset > inbuf[3].cbBuffer) {
1026 /* move remaining encrypted data forward to the beginning of
1027 buffer */
1028 memmove(connssl->encdata_buffer,
1029 (connssl->encdata_buffer + connssl->encdata_offset) -
1030 inbuf[3].cbBuffer, inbuf[3].cbBuffer);
1031 connssl->encdata_offset = inbuf[3].cbBuffer;
1032 }
1033
1034 infof(data, "schannel: encrypted data cached: offset %zu length %zu\n",
1035 connssl->encdata_offset, connssl->encdata_length);
1036 }
1037 else {
1038 /* reset encrypted buffer offset, because there is no data remaining */
1039 connssl->encdata_offset = 0;
1040 }
1041
1042 /* check if server wants to renegotiate the connection context */
1043 if(sspi_status == SEC_I_RENEGOTIATE) {
1044 infof(data, "schannel: remote party requests renegotiation\n");
1045 if(*err && *err != CURLE_AGAIN) {
1046 infof(data, "schannel: can't renogotiate, an error is pending\n");
1047 goto cleanup;
1048 }
1049 if(connssl->encdata_offset) {
1050 *err = CURLE_RECV_ERROR;
1051 infof(data, "schannel: can't renogotiate, "
1052 "encrypted data available\n");
1053 goto cleanup;
1054 }
1055 /* begin renegotiation */
1056 infof(data, "schannel: renegotiating SSL/TLS connection\n");
1057 connssl->state = ssl_connection_negotiating;
1058 connssl->connecting_state = ssl_connect_2_writing;
1059 *err = schannel_connect_common(conn, sockindex, FALSE, &done);
1060 if(*err) {
1061 infof(data, "schannel: renegotiation failed\n");
1062 goto cleanup;
1063 }
1064 /* now retry receiving data */
1065 sspi_status = SEC_E_OK;
1066 infof(data, "schannel: SSL/TLS connection renegotiated\n");
1067 continue;
1068 }
1069 /* check if the server closed the connection */
1070 else if(sspi_status == SEC_I_CONTEXT_EXPIRED) {
1071 /* In Windows 2000 SEC_I_CONTEXT_EXPIRED (close_notify) is not
1072 returned so we have to work around that in cleanup. */
1073 connssl->recv_sspi_close_notify = true;
1074 if(!connssl->recv_connection_closed) {
1075 connssl->recv_connection_closed = true;
1076 infof(data, "schannel: server closed the connection\n");
1077 }
1078 goto cleanup;
1079 }
1080 }
1081 else if(sspi_status == SEC_E_INCOMPLETE_MESSAGE) {
1082 if(!*err)
1083 *err = CURLE_AGAIN;
1084 infof(data, "schannel: failed to decrypt data, need more data\n");
1085 goto cleanup;
1086 }
1087 else {
1088 *err = CURLE_RECV_ERROR;
1089 infof(data, "schannel: failed to read data from server: %s\n",
1090 Curl_sspi_strerror(conn, sspi_status));
1091 goto cleanup;
1092 }
1093 }
1094
1095 infof(data, "schannel: encrypted data buffer: offset %zu length %zu\n",
1096 connssl->encdata_offset, connssl->encdata_length);
1097
1098 infof(data, "schannel: decrypted data buffer: offset %zu length %zu\n",
1099 connssl->decdata_offset, connssl->decdata_length);
1100
1101 cleanup:
1102 /* Warning- there is no guarantee the encdata state is valid at this point */
1103 infof(data, "schannel: schannel_recv cleanup\n");
1104
1105 /* Error if the connection has closed without a close_notify.
1106 Behavior here is a matter of debate. We don't want to be vulnerable to a
1107 truncation attack however there's some browser precedent for ignoring the
1108 close_notify for compatibility reasons.
1109 Additionally, Windows 2000 (v5.0) is a special case since it seems it doesn't
1110 return close_notify. In that case if the connection was closed we assume it
1111 was graceful (close_notify) since there doesn't seem to be a way to tell.
1112 */
1113 if(len && !connssl->decdata_offset && connssl->recv_connection_closed &&
1114 !connssl->recv_sspi_close_notify) {
1115 DWORD winver_full, winver_major, winver_minor;
1116 winver_full = GetVersion();
1117 winver_major = (DWORD)(LOBYTE(LOWORD(winver_full)));
1118 winver_minor = (DWORD)(HIBYTE(LOWORD(winver_full)));
1119
1120 if(winver_major == 5 && winver_minor == 0 && sspi_status == SEC_E_OK)
1121 connssl->recv_sspi_close_notify = true;
1122 else {
1123 *err = CURLE_RECV_ERROR;
1124 infof(data, "schannel: server closed abruptly (missing close_notify)\n");
1125 }
1126 }
1127
1128 /* Any error other than CURLE_AGAIN is an unrecoverable error. */
1129 if(*err && *err != CURLE_AGAIN)
1130 connssl->recv_unrecoverable_err = *err;
1131
1132 size = len < connssl->decdata_offset ? len : connssl->decdata_offset;
1133 if(size) {
1134 memcpy(buf, connssl->decdata_buffer, size);
1135 memmove(connssl->decdata_buffer, connssl->decdata_buffer + size,
1136 connssl->decdata_offset - size);
1137 connssl->decdata_offset -= size;
1138
1139 infof(data, "schannel: decrypted data returned %zu\n", size);
1140 infof(data, "schannel: decrypted data buffer: offset %zu length %zu\n",
1141 connssl->decdata_offset, connssl->decdata_length);
1142 *err = CURLE_OK;
1143 return (ssize_t)size;
1144 }
1145
1146 if(!*err && !connssl->recv_connection_closed)
1147 *err = CURLE_AGAIN;
1148
1149 /* It's debatable what to return when !len. We could return whatever error we
1150 got from decryption but instead we override here so the return is consistent.
1151 */
1152 if(!len)
1153 *err = CURLE_OK;
1154
1155 return *err ? -1 : 0;
1156 }
1157
1158 CURLcode
Curl_schannel_connect_nonblocking(struct connectdata * conn,int sockindex,bool * done)1159 Curl_schannel_connect_nonblocking(struct connectdata *conn, int sockindex,
1160 bool *done)
1161 {
1162 return schannel_connect_common(conn, sockindex, TRUE, done);
1163 }
1164
1165 CURLcode
Curl_schannel_connect(struct connectdata * conn,int sockindex)1166 Curl_schannel_connect(struct connectdata *conn, int sockindex)
1167 {
1168 CURLcode result;
1169 bool done = FALSE;
1170
1171 result = schannel_connect_common(conn, sockindex, FALSE, &done);
1172 if(result)
1173 return result;
1174
1175 DEBUGASSERT(done);
1176
1177 return CURLE_OK;
1178 }
1179
Curl_schannel_data_pending(const struct connectdata * conn,int sockindex)1180 bool Curl_schannel_data_pending(const struct connectdata *conn, int sockindex)
1181 {
1182 const struct ssl_connect_data *connssl = &conn->ssl[sockindex];
1183
1184 if(connssl->use) /* SSL/TLS is in use */
1185 return (connssl->encdata_offset > 0 ||
1186 connssl->decdata_offset > 0 ) ? TRUE : FALSE;
1187 else
1188 return FALSE;
1189 }
1190
Curl_schannel_close(struct connectdata * conn,int sockindex)1191 void Curl_schannel_close(struct connectdata *conn, int sockindex)
1192 {
1193 if(conn->ssl[sockindex].use)
1194 /* if the SSL/TLS channel hasn't been shut down yet, do that now. */
1195 Curl_ssl_shutdown(conn, sockindex);
1196 }
1197
Curl_schannel_shutdown(struct connectdata * conn,int sockindex)1198 int Curl_schannel_shutdown(struct connectdata *conn, int sockindex)
1199 {
1200 /* See http://msdn.microsoft.com/en-us/library/windows/desktop/aa380138.aspx
1201 * Shutting Down an Schannel Connection
1202 */
1203 struct SessionHandle *data = conn->data;
1204 struct ssl_connect_data *connssl = &conn->ssl[sockindex];
1205
1206 infof(data, "schannel: shutting down SSL/TLS connection with %s port %hu\n",
1207 conn->host.name, conn->remote_port);
1208
1209 if(connssl->cred && connssl->ctxt) {
1210 SecBufferDesc BuffDesc;
1211 SecBuffer Buffer;
1212 SECURITY_STATUS sspi_status;
1213 SecBuffer outbuf;
1214 SecBufferDesc outbuf_desc;
1215 CURLcode result;
1216 TCHAR *host_name;
1217 DWORD dwshut = SCHANNEL_SHUTDOWN;
1218
1219 InitSecBuffer(&Buffer, SECBUFFER_TOKEN, &dwshut, sizeof(dwshut));
1220 InitSecBufferDesc(&BuffDesc, &Buffer, 1);
1221
1222 sspi_status = s_pSecFn->ApplyControlToken(&connssl->ctxt->ctxt_handle,
1223 &BuffDesc);
1224
1225 if(sspi_status != SEC_E_OK)
1226 failf(data, "schannel: ApplyControlToken failure: %s",
1227 Curl_sspi_strerror(conn, sspi_status));
1228
1229 host_name = Curl_convert_UTF8_to_tchar(conn->host.name);
1230 if(!host_name)
1231 return CURLE_OUT_OF_MEMORY;
1232
1233 /* setup output buffer */
1234 InitSecBuffer(&outbuf, SECBUFFER_EMPTY, NULL, 0);
1235 InitSecBufferDesc(&outbuf_desc, &outbuf, 1);
1236
1237 sspi_status = s_pSecFn->InitializeSecurityContext(
1238 &connssl->cred->cred_handle,
1239 &connssl->ctxt->ctxt_handle,
1240 host_name,
1241 connssl->req_flags,
1242 0,
1243 0,
1244 NULL,
1245 0,
1246 &connssl->ctxt->ctxt_handle,
1247 &outbuf_desc,
1248 &connssl->ret_flags,
1249 &connssl->ctxt->time_stamp);
1250
1251 Curl_unicodefree(host_name);
1252
1253 if((sspi_status == SEC_E_OK) || (sspi_status == SEC_I_CONTEXT_EXPIRED)) {
1254 /* send close message which is in output buffer */
1255 ssize_t written;
1256 result = Curl_write_plain(conn, conn->sock[sockindex], outbuf.pvBuffer,
1257 outbuf.cbBuffer, &written);
1258
1259 s_pSecFn->FreeContextBuffer(outbuf.pvBuffer);
1260 if((result != CURLE_OK) || (outbuf.cbBuffer != (size_t) written)) {
1261 infof(data, "schannel: failed to send close msg: %s"
1262 " (bytes written: %zd)\n", curl_easy_strerror(result), written);
1263 }
1264 }
1265 }
1266
1267 /* free SSPI Schannel API security context handle */
1268 if(connssl->ctxt) {
1269 infof(data, "schannel: clear security context handle\n");
1270 s_pSecFn->DeleteSecurityContext(&connssl->ctxt->ctxt_handle);
1271 Curl_safefree(connssl->ctxt);
1272 }
1273
1274 /* free SSPI Schannel API credential handle */
1275 if(connssl->cred) {
1276 /* decrement the reference counter of the credential/session handle */
1277 if(connssl->cred->refcount > 0) {
1278 connssl->cred->refcount--;
1279 infof(data, "schannel: decremented credential handle refcount = %d\n",
1280 connssl->cred->refcount);
1281 }
1282
1283 /* if the handle was not cached and the refcount is zero */
1284 if(!connssl->cred->cached && connssl->cred->refcount == 0) {
1285 infof(data, "schannel: clear credential handle\n");
1286 s_pSecFn->FreeCredentialsHandle(&connssl->cred->cred_handle);
1287 Curl_safefree(connssl->cred);
1288 }
1289 }
1290
1291 /* free internal buffer for received encrypted data */
1292 if(connssl->encdata_buffer != NULL) {
1293 Curl_safefree(connssl->encdata_buffer);
1294 connssl->encdata_length = 0;
1295 connssl->encdata_offset = 0;
1296 }
1297
1298 /* free internal buffer for received decrypted data */
1299 if(connssl->decdata_buffer != NULL) {
1300 Curl_safefree(connssl->decdata_buffer);
1301 connssl->decdata_length = 0;
1302 connssl->decdata_offset = 0;
1303 }
1304
1305 return CURLE_OK;
1306 }
1307
Curl_schannel_session_free(void * ptr)1308 void Curl_schannel_session_free(void *ptr)
1309 {
1310 struct curl_schannel_cred *cred = ptr;
1311
1312 if(cred && cred->cached) {
1313 if(cred->refcount == 0) {
1314 s_pSecFn->FreeCredentialsHandle(&cred->cred_handle);
1315 Curl_safefree(cred);
1316 }
1317 else {
1318 cred->cached = FALSE;
1319 }
1320 }
1321 }
1322
Curl_schannel_init(void)1323 int Curl_schannel_init(void)
1324 {
1325 return (Curl_sspi_global_init() == CURLE_OK ? 1 : 0);
1326 }
1327
Curl_schannel_cleanup(void)1328 void Curl_schannel_cleanup(void)
1329 {
1330 Curl_sspi_global_cleanup();
1331 }
1332
Curl_schannel_version(char * buffer,size_t size)1333 size_t Curl_schannel_version(char *buffer, size_t size)
1334 {
1335 size = snprintf(buffer, size, "WinSSL");
1336
1337 return size;
1338 }
1339
Curl_schannel_random(unsigned char * entropy,size_t length)1340 int Curl_schannel_random(unsigned char *entropy, size_t length)
1341 {
1342 HCRYPTPROV hCryptProv = 0;
1343
1344 if(!CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL,
1345 CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
1346 return 1;
1347
1348 if(!CryptGenRandom(hCryptProv, (DWORD)length, entropy)) {
1349 CryptReleaseContext(hCryptProv, 0UL);
1350 return 1;
1351 }
1352
1353 CryptReleaseContext(hCryptProv, 0UL);
1354 return 0;
1355 }
1356
1357 #ifdef _WIN32_WCE
verify_certificate(struct connectdata * conn,int sockindex)1358 static CURLcode verify_certificate(struct connectdata *conn, int sockindex)
1359 {
1360 SECURITY_STATUS status;
1361 struct SessionHandle *data = conn->data;
1362 struct ssl_connect_data *connssl = &conn->ssl[sockindex];
1363 CURLcode result = CURLE_OK;
1364 CERT_CONTEXT *pCertContextServer = NULL;
1365 const CERT_CHAIN_CONTEXT *pChainContext = NULL;
1366
1367 status = s_pSecFn->QueryContextAttributes(&connssl->ctxt->ctxt_handle,
1368 SECPKG_ATTR_REMOTE_CERT_CONTEXT,
1369 &pCertContextServer);
1370
1371 if((status != SEC_E_OK) || (pCertContextServer == NULL)) {
1372 failf(data, "schannel: Failed to read remote certificate context: %s",
1373 Curl_sspi_strerror(conn, status));
1374 result = CURLE_PEER_FAILED_VERIFICATION;
1375 }
1376
1377 if(result == CURLE_OK) {
1378 CERT_CHAIN_PARA ChainPara;
1379 memset(&ChainPara, 0, sizeof(ChainPara));
1380 ChainPara.cbSize = sizeof(ChainPara);
1381
1382 if(!CertGetCertificateChain(NULL,
1383 pCertContextServer,
1384 NULL,
1385 pCertContextServer->hCertStore,
1386 &ChainPara,
1387 0,
1388 NULL,
1389 &pChainContext)) {
1390 failf(data, "schannel: CertGetCertificateChain failed: %s",
1391 Curl_sspi_strerror(conn, GetLastError()));
1392 pChainContext = NULL;
1393 result = CURLE_PEER_FAILED_VERIFICATION;
1394 }
1395
1396 if(result == CURLE_OK) {
1397 CERT_SIMPLE_CHAIN *pSimpleChain = pChainContext->rgpChain[0];
1398 DWORD dwTrustErrorMask = ~(DWORD)(CERT_TRUST_IS_NOT_TIME_NESTED|
1399 CERT_TRUST_REVOCATION_STATUS_UNKNOWN);
1400 dwTrustErrorMask &= pSimpleChain->TrustStatus.dwErrorStatus;
1401 if(dwTrustErrorMask) {
1402 if(dwTrustErrorMask & CERT_TRUST_IS_PARTIAL_CHAIN)
1403 failf(data, "schannel: CertGetCertificateChain trust error"
1404 " CERT_TRUST_IS_PARTIAL_CHAIN");
1405 if(dwTrustErrorMask & CERT_TRUST_IS_UNTRUSTED_ROOT)
1406 failf(data, "schannel: CertGetCertificateChain trust error"
1407 " CERT_TRUST_IS_UNTRUSTED_ROOT");
1408 if(dwTrustErrorMask & CERT_TRUST_IS_NOT_TIME_VALID)
1409 failf(data, "schannel: CertGetCertificateChain trust error"
1410 " CERT_TRUST_IS_NOT_TIME_VALID");
1411 failf(data, "schannel: CertGetCertificateChain error mask: 0x%08x",
1412 dwTrustErrorMask);
1413 result = CURLE_PEER_FAILED_VERIFICATION;
1414 }
1415 }
1416 }
1417
1418 if(result == CURLE_OK) {
1419 if(data->set.ssl.verifyhost) {
1420 TCHAR cert_hostname_buff[128];
1421 xcharp_u hostname;
1422 xcharp_u cert_hostname;
1423 DWORD len;
1424
1425 cert_hostname.const_tchar_ptr = cert_hostname_buff;
1426 hostname.tchar_ptr = Curl_convert_UTF8_to_tchar(conn->host.name);
1427
1428 len = CertGetNameString(pCertContextServer,
1429 CERT_NAME_DNS_TYPE,
1430 0,
1431 NULL,
1432 cert_hostname.tchar_ptr,
1433 128);
1434 if(len > 0 && *cert_hostname.tchar_ptr == '*') {
1435 /* this is a wildcard cert. try matching the last len - 1 chars */
1436 int hostname_len = strlen(conn->host.name);
1437 cert_hostname.tchar_ptr++;
1438 if(_tcsicmp(cert_hostname.const_tchar_ptr,
1439 hostname.const_tchar_ptr + hostname_len - len + 2) != 0)
1440 result = CURLE_PEER_FAILED_VERIFICATION;
1441 }
1442 else if(len == 0 || _tcsicmp(hostname.const_tchar_ptr,
1443 cert_hostname.const_tchar_ptr) != 0) {
1444 result = CURLE_PEER_FAILED_VERIFICATION;
1445 }
1446 if(result == CURLE_PEER_FAILED_VERIFICATION) {
1447 char *_cert_hostname;
1448 _cert_hostname = Curl_convert_tchar_to_UTF8(cert_hostname.tchar_ptr);
1449 failf(data, "schannel: CertGetNameString() certificate hostname "
1450 "(%s) did not match connection (%s)",
1451 _cert_hostname, conn->host.name);
1452 Curl_unicodefree(_cert_hostname);
1453 }
1454 Curl_unicodefree(hostname.tchar_ptr);
1455 }
1456 }
1457
1458 if(pChainContext)
1459 CertFreeCertificateChain(pChainContext);
1460
1461 if(pCertContextServer)
1462 CertFreeCertificateContext(pCertContextServer);
1463
1464 return result;
1465 }
1466 #endif /* _WIN32_WCE */
1467
1468 #endif /* USE_SCHANNEL */
1469