curl_sspi: Added Curl_sspi_version function
[platform/upstream/curl.git] / lib / curl_schannel.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 2012, Marc Hoersken, <info@marc-hoersken.de>, et al.
9  *
10  * This software is licensed as described in the file COPYING, which
11  * you should have received as part of this distribution. The terms
12  * are also available at http://curl.haxx.se/docs/copyright.html.
13  *
14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15  * copies of the Software, and permit persons to whom the Software is
16  * furnished to do so, under the terms of the COPYING file.
17  *
18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19  * KIND, either express or implied.
20  *
21  ***************************************************************************/
22
23 /*
24  * Source file for all SChannel-specific code for the TLS/SSL layer. No code
25  * but sslgen.c should ever call or use these functions.
26  *
27  */
28
29 /*
30  * Based upon the PolarSSL implementation in polarssl.c and polarssl.h:
31  *   Copyright (C) 2010, 2011, Hoi-Ho Chan, <hoiho.chan@gmail.com>
32  *
33  * Based upon the CyaSSL implementation in cyassl.c and cyassl.h:
34  *   Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
35  *
36  * Thanks for code and inspiration!
37  */
38
39 /*
40  * TODO list for TLS/SSL implementation:
41  * - implement write buffering
42  * - implement SSL/TLS shutdown
43  * - implement client certificate authentication
44  * - implement custom server certificate validation
45  * - implement cipher/algorithm option
46  *
47  * Related articles on MSDN:
48  * - Getting a Certificate for Schannel
49  *   http://msdn.microsoft.com/en-us/library/windows/desktop/aa375447.aspx
50  * - Specifying Schannel Ciphers and Cipher Strengths
51  *   http://msdn.microsoft.com/en-us/library/windows/desktop/aa380161.aspx
52  */
53
54 #include "setup.h"
55
56 #ifdef USE_WINDOWS_SSPI
57 #ifdef USE_SCHANNEL
58
59 #include "curl_sspi.h"
60 #include "curl_schannel.h"
61 #include "sslgen.h"
62 #include "sendf.h"
63 #include "connect.h" /* for the connect timeout */
64 #include "select.h" /* for the socket readyness */
65 #include "inet_pton.h" /* for IP addr SNI check */
66
67 #define _MPRINTF_REPLACE /* use our functions only */
68 #include <curl/mprintf.h>
69 #include "curl_memory.h"
70 /* The last #include file should be: */
71 #include "memdebug.h"
72
73 /* Uncomment to force verbose output
74  * #define infof(x, y, ...) printf(y, __VA_ARGS__)
75  * #define failf(x, y, ...) printf(y, __VA_ARGS__)
76  */
77
78 static Curl_recv schannel_recv;
79 static Curl_send schannel_send;
80
81 static CURLcode
82 schannel_connect_step1(struct connectdata *conn, int sockindex) {
83   ssize_t write = -1;
84   struct SessionHandle *data = conn->data;
85   struct ssl_connect_data* connssl = &conn->ssl[sockindex];
86   SecBuffer outbuf;
87   SecBufferDesc outbuf_desc;
88   SCHANNEL_CRED schannel_cred;
89   SECURITY_STATUS sspi_status = SEC_E_OK;
90   curl_schannel_cred *old_cred = NULL;
91   char *sspi_msg = NULL;
92   struct in_addr addr;
93 #ifdef ENABLE_IPV6
94   struct in6_addr addr6;
95 #endif
96
97   infof(data, "schannel: connecting to %s:%d (step 1/3)\n",
98         conn->host.name, conn->remote_port);
99
100   /* check for an existing re-usable credential handle */
101   if(!Curl_ssl_getsessionid(conn, (void**)&old_cred, NULL)) {
102     connssl->cred = old_cred;
103     infof(data, "schannel: re-using existing credential handle\n");
104   }
105   else {
106     /* setup Schannel API options */
107     memset(&schannel_cred, 0, sizeof(schannel_cred));
108     schannel_cred.dwVersion = SCHANNEL_CRED_VERSION;
109
110     if(data->set.ssl.verifypeer) {
111       schannel_cred.dwFlags = SCH_CRED_AUTO_CRED_VALIDATION |
112                               SCH_CRED_REVOCATION_CHECK_CHAIN;
113       infof(data, "schannel: checking server certificate and revocation\n");
114     }
115     else {
116       schannel_cred.dwFlags = SCH_CRED_MANUAL_CRED_VALIDATION |
117                               SCH_CRED_IGNORE_NO_REVOCATION_CHECK |
118                               SCH_CRED_IGNORE_REVOCATION_OFFLINE;
119       infof(data, "schannel: disable server certificate and revocation checks\n");
120     }
121
122     if(Curl_inet_pton(AF_INET, conn->host.name, &addr) ||
123 #ifdef ENABLE_IPV6
124        Curl_inet_pton(AF_INET6, conn->host.name, &addr6) ||
125 #endif
126        data->set.ssl.verifyhost < 2) {
127       schannel_cred.dwFlags |= SCH_CRED_NO_SERVERNAME_CHECK;
128       infof(data, "schannel: using IP address, disable SNI servername check\n");
129     }
130
131     switch(data->set.ssl.version) {
132       case CURL_SSLVERSION_TLSv1:
133         schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1_0_CLIENT |
134                                               SP_PROT_TLS1_1_CLIENT |
135                                               SP_PROT_TLS1_2_CLIENT;
136         break;
137       case CURL_SSLVERSION_SSLv3:
138         schannel_cred.grbitEnabledProtocols = SP_PROT_SSL3_CLIENT;
139         break;
140       case CURL_SSLVERSION_SSLv2:
141         schannel_cred.grbitEnabledProtocols = SP_PROT_SSL2_CLIENT;
142         break;
143     }
144
145     /* allocate memory for the re-usable credential handle */
146     connssl->cred = malloc(sizeof(curl_schannel_cred));
147     if (!connssl->cred) {
148       failf(data, "schannel: unable to allocate memory");
149       return CURLE_OUT_OF_MEMORY;
150     }
151     memset(connssl->cred, 0, sizeof(curl_schannel_cred));
152
153     /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa374716.aspx */
154     sspi_status = s_pSecFn->AcquireCredentialsHandle(NULL,
155       UNISP_NAME, SECPKG_CRED_OUTBOUND, NULL, &schannel_cred, NULL, NULL,
156       &connssl->cred->cred_handle, &connssl->cred->time_stamp);
157
158     if(sspi_status != SEC_E_OK) {
159       sspi_msg = Curl_sspi_status_msg(sspi_status);
160       if(sspi_status == SEC_E_WRONG_PRINCIPAL)
161         failf(data, "schannel: SNI or certificate check failed: %s\n",
162               sspi_msg);
163       else
164         failf(data, "schannel: AcquireCredentialsHandleA failed: %s\n",
165               sspi_msg);
166       free(sspi_msg);
167       free(connssl->cred);
168       connssl->cred = NULL;
169       return CURLE_SSL_CONNECT_ERROR;
170     }
171   }
172
173   /* setup output buffer */
174   outbuf.pvBuffer = NULL;
175   outbuf.cbBuffer = 0;
176   outbuf.BufferType = SECBUFFER_EMPTY;
177
178   outbuf_desc.pBuffers = &outbuf;
179   outbuf_desc.cBuffers = 1;
180   outbuf_desc.ulVersion = SECBUFFER_VERSION;
181
182   /* setup request flags */
183   connssl->req_flags = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT |
184                        ISC_REQ_CONFIDENTIALITY | ISC_REQ_EXTENDED_ERROR |
185                        ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM;
186
187   /* allocate memory for the security context handle */
188   connssl->ctxt = malloc(sizeof(curl_schannel_ctxt));
189   if (!connssl->ctxt) {
190     failf(data, "schannel: unable to allocate memory");
191     return CURLE_OUT_OF_MEMORY;
192   }
193   memset(connssl->ctxt, 0, sizeof(curl_schannel_ctxt));
194
195   /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa375924.aspx */
196   sspi_status = s_pSecFn->InitializeSecurityContext(
197     &connssl->cred->cred_handle, NULL, conn->host.name,
198     connssl->req_flags, 0, 0, NULL, 0, &connssl->ctxt->ctxt_handle,
199     &outbuf_desc, &connssl->ret_flags, &connssl->ctxt->time_stamp);
200
201   if(sspi_status != SEC_I_CONTINUE_NEEDED) {
202     sspi_msg = Curl_sspi_status_msg(sspi_status);
203     if(sspi_status == SEC_E_WRONG_PRINCIPAL)
204       failf(data, "schannel: SNI or certificate check failed: %s\n",
205             sspi_msg);
206     else
207       failf(data, "schannel: initial InitializeSecurityContextA failed: %s\n",
208             sspi_msg);
209     free(sspi_msg);
210     free(connssl->ctxt);
211     connssl->ctxt = NULL;
212     return CURLE_SSL_CONNECT_ERROR;
213   }
214
215   infof(data, "schannel: sending initial handshake data: %d ...\n",
216         outbuf.cbBuffer);
217
218   /* send initial handshake data which is now stored in output buffer */
219   write = swrite(conn->sock[sockindex], outbuf.pvBuffer, outbuf.cbBuffer);
220   s_pSecFn->FreeContextBuffer(outbuf.pvBuffer);
221   if(write != outbuf.cbBuffer) {
222     failf(data, "schannel: failed to send initial handshake data: %d\n", write);
223     return CURLE_SSL_CONNECT_ERROR;
224   }
225
226   infof(data, "schannel: sent initial handshake data: %d\n", write);
227
228   /* continue to second handshake step */
229   connssl->connecting_state = ssl_connect_2;
230
231   return CURLE_OK;
232 }
233
234 static CURLcode
235 schannel_connect_step2(struct connectdata *conn, int sockindex) {
236   int i;
237   ssize_t read = -1, write = -1;
238   struct SessionHandle *data = conn->data;
239   struct ssl_connect_data* connssl = &conn->ssl[sockindex];
240   SecBuffer outbuf[2];
241   SecBufferDesc outbuf_desc;
242   SecBuffer inbuf[2];
243   SecBufferDesc inbuf_desc;
244   SECURITY_STATUS sspi_status = SEC_E_OK;
245   char *sspi_msg = NULL;
246
247   infof(data, "schannel: connecting to %s:%d (step 2/3)\n",
248         conn->host.name, conn->remote_port);
249
250   /* buffer to store previously received and encrypted data */
251   if(connssl->encdata_buffer == NULL) {
252     connssl->encdata_offset = 0;
253     connssl->encdata_length = 4096;
254     connssl->encdata_buffer = malloc(connssl->encdata_length);
255     if(connssl->encdata_buffer == NULL) {
256       failf(data, "schannel: unable to allocate memory");
257       return CURLE_OUT_OF_MEMORY;
258     }
259   }
260
261   /* read encrypted handshake data from socket */
262   read = sread(conn->sock[sockindex],
263                connssl->encdata_buffer + connssl->encdata_offset,
264                connssl->encdata_length - connssl->encdata_offset);
265   if(read > 0) {
266     /* increase encrypted data buffer offset */
267     connssl->encdata_offset += read;
268   }
269   else if(connssl->connecting_state != ssl_connect_2_writing) {
270     if(read < 0) {
271       connssl->connecting_state = ssl_connect_2_reading;
272       infof(data, "schannel: failed to receive handshake, need more data\n");
273       return CURLE_OK;
274     }
275     else if(read == 0) {
276       failf(data, "schannel: failed to receive handshake, connection failed\n");
277       return CURLE_SSL_CONNECT_ERROR;
278     }
279   }
280
281   infof(data, "schannel: encrypted data buffer %d/%d\n",
282     connssl->encdata_offset, connssl->encdata_length);
283
284   /* setup input buffers */
285   inbuf[0].pvBuffer = malloc(connssl->encdata_offset);
286   inbuf[0].cbBuffer = connssl->encdata_offset;
287   inbuf[0].BufferType = SECBUFFER_TOKEN;
288
289   inbuf[1].pvBuffer = NULL;
290   inbuf[1].cbBuffer = 0;
291   inbuf[1].BufferType = SECBUFFER_EMPTY;
292
293   inbuf_desc.pBuffers = &inbuf[0];
294   inbuf_desc.cBuffers = 2;
295   inbuf_desc.ulVersion = SECBUFFER_VERSION;
296
297   /* setup output buffers */
298   outbuf[0].pvBuffer = NULL;
299   outbuf[0].cbBuffer = 0;
300   outbuf[0].BufferType = SECBUFFER_TOKEN;
301
302   outbuf[1].pvBuffer = NULL;
303   outbuf[1].cbBuffer = 0;
304   outbuf[1].BufferType = SECBUFFER_ALERT;
305
306   outbuf_desc.pBuffers = &outbuf[0];
307   outbuf_desc.cBuffers = 2;
308   outbuf_desc.ulVersion = SECBUFFER_VERSION;
309
310   if(inbuf[0].pvBuffer == NULL) {
311     failf(data, "schannel: unable to allocate memory");
312     return CURLE_OUT_OF_MEMORY;
313   }
314
315   /* copy received handshake data into input buffer */
316   memcpy(inbuf[0].pvBuffer, connssl->encdata_buffer, connssl->encdata_offset);
317
318   /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa375924.aspx */
319   sspi_status = s_pSecFn->InitializeSecurityContext(
320     &connssl->cred->cred_handle, &connssl->ctxt->ctxt_handle,
321     conn->host.name, connssl->req_flags, 0, 0, &inbuf_desc, 0, NULL,
322     &outbuf_desc, &connssl->ret_flags, &connssl->ctxt->time_stamp);
323
324   /* free buffer for received handshake data */
325   free(inbuf[0].pvBuffer);
326
327   /* check if the handshake was incomplete */
328   if(sspi_status == SEC_E_INCOMPLETE_MESSAGE) {
329     connssl->connecting_state = ssl_connect_2_reading;
330     infof(data, "schannel: received incomplete message, need more data\n");
331     return CURLE_OK;
332   }
333
334   /* check if the handshake needs to be continued */
335   if(sspi_status == SEC_I_CONTINUE_NEEDED || sspi_status == SEC_E_OK) {
336     for(i = 0; i < 2; i++) {
337       /* search for handshake tokens that need to be send */
338       if(outbuf[i].BufferType == SECBUFFER_TOKEN && outbuf[i].cbBuffer > 0) {
339         infof(data, "schannel: sending next handshake data: %d ...\n",
340               outbuf[i].cbBuffer);
341
342         /* send handshake token to server */
343         write = swrite(conn->sock[sockindex],
344                        outbuf[i].pvBuffer, outbuf[i].cbBuffer);
345         if(write != outbuf[i].cbBuffer) {
346           failf(data, "schannel: failed to send next handshake data: %d\n",
347                 write);
348           return CURLE_SSL_CONNECT_ERROR;
349         }
350       }
351
352       /* free obsolete buffer */
353       if(outbuf[i].pvBuffer != NULL) {
354         s_pSecFn->FreeContextBuffer(outbuf[i].pvBuffer);
355       }
356     }
357   }
358   else {
359     sspi_msg = Curl_sspi_status_msg(sspi_status);
360     if(sspi_status == SEC_E_WRONG_PRINCIPAL)
361       failf(data, "schannel: SNI or certificate check failed: %s\n",
362             sspi_msg);
363     else
364       failf(data, "schannel: next InitializeSecurityContextA failed: %s\n",
365             sspi_msg);
366     free(sspi_msg);
367     return CURLE_SSL_CONNECT_ERROR;
368   }
369
370   /* check if there was additional remaining encrypted data */
371   if(inbuf[1].BufferType == SECBUFFER_EXTRA && inbuf[1].cbBuffer > 0) {
372     infof(data, "schannel: encrypted data length: %d\n", inbuf[1].cbBuffer);
373
374     /* check if the remaining data is less than the total amount
375      * and therefore begins after the already processed data
376      */
377     if(connssl->encdata_offset > inbuf[1].cbBuffer) {
378       memmove(connssl->encdata_buffer,
379               (connssl->encdata_buffer + connssl->encdata_offset) -
380                 inbuf[1].cbBuffer, inbuf[1].cbBuffer);
381       connssl->encdata_offset = inbuf[1].cbBuffer;
382     }
383   }
384   else {
385     connssl->encdata_offset = 0;
386   }
387
388   /* check if the handshake needs to be continued */
389   if(sspi_status == SEC_I_CONTINUE_NEEDED) {
390     connssl->connecting_state = ssl_connect_2_reading;
391     return CURLE_OK;
392   }
393
394   /* check if the handshake is complete */
395   if(sspi_status == SEC_E_OK) {
396     connssl->connecting_state = ssl_connect_3;
397     infof(data, "schannel: handshake complete\n");
398   }
399
400   return CURLE_OK;
401 }
402
403 static CURLcode
404 schannel_connect_step3(struct connectdata *conn, int sockindex) {
405   CURLcode retcode = CURLE_OK;
406   struct SessionHandle *data = conn->data;
407   struct ssl_connect_data *connssl = &conn->ssl[sockindex];
408   curl_schannel_cred *old_cred = NULL;
409   int incache;
410
411   DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
412
413   infof(data, "schannel: connecting to %s:%d (step 3/3)\n",
414         conn->host.name, conn->remote_port);
415
416   /* check if the required context attributes are met */
417   if(connssl->ret_flags != connssl->req_flags) {
418     if(!(connssl->ret_flags & ISC_RET_SEQUENCE_DETECT))
419       failf(data, "schannel: failed to setup sequence detection\n");
420     if(!(connssl->ret_flags & ISC_RET_REPLAY_DETECT))
421       failf(data, "schannel: failed to setup replay detection\n");
422     if(!(connssl->ret_flags & ISC_RET_CONFIDENTIALITY))
423       failf(data, "schannel: failed to setup confidentiality\n");
424     if(!(connssl->ret_flags & ISC_RET_EXTENDED_ERROR))
425       failf(data, "schannel: failed to setup extended errors\n");
426     if(!(connssl->ret_flags & ISC_RET_ALLOCATED_MEMORY))
427       failf(data, "schannel: failed to setup memory allocation\n");
428     if(!(connssl->ret_flags & ISC_RET_STREAM))
429       failf(data, "schannel: failed to setup stream orientation\n");
430     return CURLE_SSL_CONNECT_ERROR;
431   }
432
433   /* save the current session data for possible re-use */
434   incache = !(Curl_ssl_getsessionid(conn, (void**)&old_cred, NULL));
435   if(incache) {
436     if(old_cred != connssl->cred) {
437       infof(data, "schannel: old credential handle is stale, removing\n");
438       Curl_ssl_delsessionid(conn, (void*)old_cred);
439       incache = FALSE;
440     }
441   }
442   if(!incache) {
443     retcode = Curl_ssl_addsessionid(conn, (void*)connssl->cred,
444                                     sizeof(curl_schannel_cred));
445     if(retcode) {
446       failf(data, "schannel: failed to store credential handle\n");
447       return retcode;
448     }
449     else {
450       infof(data, "schannel: stored crendential handle\n");
451     }
452   }
453
454   connssl->connecting_state = ssl_connect_done;
455
456   return CURLE_OK;
457 }
458
459 static CURLcode
460 schannel_connect_common(struct connectdata *conn, int sockindex,
461                         bool nonblocking, bool *done) {
462   CURLcode retcode;
463   struct SessionHandle *data = conn->data;
464   struct ssl_connect_data *connssl = &conn->ssl[sockindex];
465   curl_socket_t sockfd = conn->sock[sockindex];
466   long timeout_ms;
467   int what;
468
469   /* check if the connection has already been established */
470   if(ssl_connection_complete == connssl->state) {
471     *done = TRUE;
472     return CURLE_OK;
473   }
474
475   if(ssl_connect_1 == connssl->connecting_state) {
476     /* check out how much more time we're allowed */
477     timeout_ms = Curl_timeleft(data, NULL, TRUE);
478
479     if(timeout_ms < 0) {
480       /* no need to continue if time already is up */
481       failf(data, "SSL connection timeout");
482       return CURLE_OPERATION_TIMEDOUT;
483     }
484
485     retcode = schannel_connect_step1(conn, sockindex);
486     if(retcode)
487       return retcode;
488   }
489
490   while(ssl_connect_2 == connssl->connecting_state ||
491         ssl_connect_2_reading == connssl->connecting_state ||
492         ssl_connect_2_writing == connssl->connecting_state) {
493
494     /* check out how much more time we're allowed */
495     timeout_ms = Curl_timeleft(data, NULL, TRUE);
496
497     if(timeout_ms < 0) {
498       /* no need to continue if time already is up */
499       failf(data, "SSL connection timeout");
500       return CURLE_OPERATION_TIMEDOUT;
501     }
502
503     /* if ssl is expecting something, check if it's available. */
504     if(connssl->connecting_state == ssl_connect_2_reading
505        || connssl->connecting_state == ssl_connect_2_writing) {
506
507       curl_socket_t writefd = ssl_connect_2_writing ==
508         connssl->connecting_state ? sockfd : CURL_SOCKET_BAD;
509       curl_socket_t readfd = ssl_connect_2_reading ==
510         connssl->connecting_state ? sockfd : CURL_SOCKET_BAD;
511
512       what = Curl_socket_ready(readfd, writefd, nonblocking ? 0 : timeout_ms);
513       if(what < 0) {
514         /* fatal error */
515         failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
516         return CURLE_SSL_CONNECT_ERROR;
517       }
518       else if(0 == what) {
519         if(nonblocking) {
520           *done = FALSE;
521           return CURLE_OK;
522         }
523         else {
524           /* timeout */
525           failf(data, "SSL connection timeout");
526           return CURLE_OPERATION_TIMEDOUT;
527         }
528       }
529       /* socket is readable or writable */
530     }
531
532     /* Run transaction, and return to the caller if it failed or if
533      * this connection is part of a multi handle and this loop would
534      * execute again. This permits the owner of a multi handle to
535      * abort a connection attempt before step2 has completed while
536      * ensuring that a client using select() or epoll() will always
537      * have a valid fdset to wait on.
538      */
539     retcode = schannel_connect_step2(conn, sockindex);
540     if(retcode || (nonblocking &&
541                    (ssl_connect_2 == connssl->connecting_state ||
542                     ssl_connect_2_reading == connssl->connecting_state ||
543                     ssl_connect_2_writing == connssl->connecting_state)))
544       return retcode;
545
546   } /* repeat step2 until all transactions are done. */
547
548   if(ssl_connect_3 == connssl->connecting_state) {
549     retcode = schannel_connect_step3(conn, sockindex);
550     if(retcode)
551       return retcode;
552   }
553
554   if(ssl_connect_done == connssl->connecting_state) {
555     connssl->state = ssl_connection_complete;
556     conn->recv[sockindex] = schannel_recv;
557     conn->send[sockindex] = schannel_send;
558     *done = TRUE;
559   }
560   else
561     *done = FALSE;
562
563   /* reset our connection state machine */
564   connssl->connecting_state = ssl_connect_1;
565
566   return CURLE_OK;
567 }
568
569 static ssize_t
570 schannel_send(struct connectdata *conn, int sockindex,
571               const void *buf, size_t len, CURLcode *err) {
572   ssize_t ret = -1;
573   size_t data_len = 0;
574   unsigned char *data = NULL;
575   struct ssl_connect_data *connssl = &conn->ssl[sockindex];
576   SecBuffer outbuf[4];
577   SecBufferDesc outbuf_desc;
578   SECURITY_STATUS sspi_status = SEC_E_OK;
579
580   /* check if the maximum stream sizes were queried */
581   if(connssl->stream_sizes.cbMaximumMessage == 0) {
582     sspi_status = s_pSecFn->QueryContextAttributes(
583                               &connssl->ctxt->ctxt_handle,
584                               SECPKG_ATTR_STREAM_SIZES, &connssl->stream_sizes);
585     if(sspi_status != SEC_E_OK) {
586       *err = CURLE_SEND_ERROR;
587       return -1;
588     }
589   }
590
591   /* check if the buffer is longer than the maximum message length */
592   if(len > connssl->stream_sizes.cbMaximumMessage) {
593     *err = CURLE_SEND_ERROR;
594     return -1;
595   }
596
597   /* calculate the complete message length and allocate a buffer for it */
598   data_len = connssl->stream_sizes.cbHeader + len +
599               connssl->stream_sizes.cbTrailer;
600   data = (unsigned char*) malloc(data_len);
601   if(data == NULL) {
602     *err = CURLE_OUT_OF_MEMORY;
603     return -1;
604   }
605
606   /* setup output buffers (header, data, trailer, empty) */
607   outbuf[0].pvBuffer = data;
608   outbuf[0].cbBuffer = connssl->stream_sizes.cbHeader;
609   outbuf[0].BufferType = SECBUFFER_STREAM_HEADER;
610
611   outbuf[1].pvBuffer = data + connssl->stream_sizes.cbHeader;
612   outbuf[1].cbBuffer = len;
613   outbuf[1].BufferType = SECBUFFER_DATA;
614
615   outbuf[2].pvBuffer = data + connssl->stream_sizes.cbHeader + len;
616   outbuf[2].cbBuffer = connssl->stream_sizes.cbTrailer;
617   outbuf[2].BufferType = SECBUFFER_STREAM_TRAILER;
618
619   outbuf[3].pvBuffer = NULL;
620   outbuf[3].cbBuffer = 0;
621   outbuf[3].BufferType = SECBUFFER_EMPTY;
622
623   outbuf_desc.pBuffers = &outbuf[0];
624   outbuf_desc.cBuffers = 4;
625   outbuf_desc.ulVersion = SECBUFFER_VERSION;
626
627   /* copy data into output buffer */
628   memcpy(outbuf[1].pvBuffer, buf, len);
629
630   /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa375390.aspx */
631   sspi_status = s_pSecFn->EncryptMessage(&connssl->ctxt->ctxt_handle, 0,
632                                          &outbuf_desc, 0);
633
634   /* check if the message was encrypted */
635   if(sspi_status == SEC_E_OK) {
636     /* send the encrypted message including header, data and trailer */
637     len = outbuf[0].cbBuffer + outbuf[1].cbBuffer + outbuf[2].cbBuffer;
638     ret = swrite(conn->sock[sockindex], data, len);
639     /* TODO: implement write buffering */
640   }
641   else if(sspi_status == SEC_E_INSUFFICIENT_MEMORY) {
642     *err = CURLE_OUT_OF_MEMORY;
643   }
644   else{
645     *err = CURLE_SEND_ERROR;
646   }
647
648   free(data);
649
650   return ret;
651 }
652
653 static ssize_t
654 schannel_recv(struct connectdata *conn, int sockindex,
655               char *buf, size_t len, CURLcode *err) {
656   size_t size = 0;
657   ssize_t read = 0, ret = -1;
658   CURLcode retcode;
659   struct SessionHandle *data = conn->data;
660   struct ssl_connect_data *connssl = &conn->ssl[sockindex];
661   bool done = FALSE;
662   SecBuffer inbuf[4];
663   SecBufferDesc inbuf_desc;
664   SECURITY_STATUS sspi_status = SEC_E_OK;
665   char *sspi_msg = NULL;
666
667   infof(data, "schannel: client wants to read %d\n", len);
668   *err = CURLE_OK;
669
670   /* buffer to store previously received and decrypted data */
671   if(connssl->decdata_buffer == NULL) {
672     connssl->decdata_offset = 0;
673     connssl->decdata_length = 4096;
674     connssl->decdata_buffer = malloc(connssl->decdata_length);
675     if(connssl->decdata_buffer == NULL) {
676       failf(data, "schannel: unable to allocate memory");
677       return CURLE_OUT_OF_MEMORY;
678     }
679   }
680
681   /* increase buffer in order to fit the requested amount of data */
682   while(connssl->encdata_length - connssl->encdata_offset < 2048 ||
683         connssl->encdata_length < len) {
684     /* increase internal encrypted data buffer */
685     connssl->encdata_length += 2048;
686     connssl->encdata_buffer = realloc(connssl->encdata_buffer,
687                                       connssl->encdata_length);
688     if(connssl->encdata_buffer == NULL) {
689       failf(data, "schannel: unable to re-allocate memory");
690       *err = CURLE_OUT_OF_MEMORY;
691       return -1;
692     }
693   }
694
695   /* read encrypted data from socket */
696   infof(data, "schannel: encrypted data buffer %d/%d\n",
697         connssl->encdata_offset, connssl->encdata_length);
698   size = connssl->encdata_length - connssl->encdata_offset;
699   if(size > 0) {
700     read = sread(conn->sock[sockindex],
701                  connssl->encdata_buffer + connssl->encdata_offset, size);
702     infof(data, "schannel: encrypted data received %d\n", read);
703
704     /* check for received data */
705     if(read > 0) {
706       /* increase encrypted data buffer offset */
707       connssl->encdata_offset += read;
708     }
709     else if(connssl->encdata_offset == 0) {
710       if(read == 0)
711         ret = 0;
712       else
713         *err = CURLE_AGAIN;
714     }
715   }
716
717   infof(data, "schannel: encrypted data buffer %d/%d\n",
718     connssl->encdata_offset, connssl->encdata_length);
719
720   /* check if we still have some data in our buffers */
721   while(connssl->encdata_offset > 0 && sspi_status == SEC_E_OK) {
722     /* prepare data buffer for DecryptMessage call */
723     inbuf[0].pvBuffer = connssl->encdata_buffer;
724     inbuf[0].cbBuffer = connssl->encdata_offset;
725     inbuf[0].BufferType = SECBUFFER_DATA;
726
727     /* we need 3 more empty input buffers for possible output */
728     inbuf[1].pvBuffer = NULL;
729     inbuf[1].cbBuffer = 0;
730     inbuf[1].BufferType = SECBUFFER_EMPTY;
731
732     inbuf[2].pvBuffer = NULL;
733     inbuf[2].cbBuffer = 0;
734     inbuf[2].BufferType = SECBUFFER_EMPTY;
735
736     inbuf[3].pvBuffer = NULL;
737     inbuf[3].cbBuffer = 0;
738     inbuf[3].BufferType = SECBUFFER_EMPTY;
739
740     inbuf_desc.pBuffers = &inbuf[0];
741     inbuf_desc.cBuffers = 4;
742     inbuf_desc.ulVersion = SECBUFFER_VERSION;
743
744     /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa375348.aspx */
745     sspi_status = s_pSecFn->DecryptMessage(&connssl->ctxt->ctxt_handle,
746                                            &inbuf_desc, 0, NULL);
747
748     /* check if we need more data */
749     if(sspi_status == SEC_E_INCOMPLETE_MESSAGE) {
750       infof(data, "schannel: failed to decrypt data, need more data\n");
751       *err = CURLE_AGAIN;
752       return -1;
753     }
754
755     /* check if everything went fine (server may want to renegotiate context) */
756     if(sspi_status == SEC_E_OK || sspi_status == SEC_I_RENEGOTIATE ||
757                                   sspi_status == SEC_I_CONTEXT_EXPIRED) {
758       /* check for successfully decrypted data */
759       if(inbuf[1].BufferType == SECBUFFER_DATA) {
760         infof(data, "schannel: decrypted data length: %d\n", inbuf[1].cbBuffer);
761
762         /* increase buffer in order to fit the received amount of data */
763         size = inbuf[1].cbBuffer > 2048 ? inbuf[1].cbBuffer : 2048;
764         while(connssl->decdata_length - connssl->decdata_offset < size ||
765               connssl->decdata_length < len) {
766           /* increase internal decrypted data buffer */
767           connssl->decdata_length += size;
768           connssl->decdata_buffer = realloc(connssl->decdata_buffer,
769                                             connssl->decdata_length);
770           if(connssl->decdata_buffer == NULL) {
771             failf(data, "schannel: unable to re-allocate memory");
772             *err = CURLE_OUT_OF_MEMORY;
773             return -1;
774           }
775         }
776
777         /* copy decrypted data to internal buffer */
778         size = inbuf[1].cbBuffer;
779         if(size > 0) {
780           memcpy(connssl->decdata_buffer + connssl->decdata_offset,
781                  inbuf[1].pvBuffer, size);
782           connssl->decdata_offset += size;
783         }
784
785         infof(data, "schannel: decrypted data added: %d\n", size);
786         infof(data, "schannel: decrypted data cached: %d/%d\n",
787               connssl->decdata_offset, connssl->decdata_length);
788       }
789
790       /* check for remaining encrypted data */
791       if(inbuf[3].BufferType == SECBUFFER_EXTRA && inbuf[3].cbBuffer > 0) {
792         infof(data, "schannel: encrypted data length: %d\n", inbuf[3].cbBuffer);
793
794         /* check if the remaining data is less than the total amount
795          * and therefore begins after the already processed data
796         */
797         if(connssl->encdata_offset > inbuf[3].cbBuffer) {
798           /* move remaining encrypted data forward to the beginning of buffer */
799           memmove(connssl->encdata_buffer,
800                   (connssl->encdata_buffer + connssl->encdata_offset) -
801                     inbuf[3].cbBuffer, inbuf[3].cbBuffer);
802           connssl->encdata_offset = inbuf[3].cbBuffer;
803         }
804
805         infof(data, "schannel: encrypted data cached: %d/%d\n",
806               connssl->encdata_offset, connssl->encdata_length);
807       }
808       else{
809         /* reset encrypted buffer offset, because there is no data remaining */
810         connssl->encdata_offset = 0;
811       }
812     }
813
814     /* check if server wants to renegotiate the connection context */
815     if(sspi_status == SEC_I_RENEGOTIATE) {
816       infof(data, "schannel: client needs to renegotiate with server\n");
817
818       /* begin renegotiation */
819       connssl->state = ssl_connection_negotiating;
820       connssl->connecting_state = ssl_connect_2_writing;
821       retcode = schannel_connect_common(conn, sockindex, FALSE, &done);
822       if(retcode)
823         *err = retcode;
824       else /* now retry receiving data */
825         return schannel_recv(conn, sockindex, buf, len, err);
826     }
827   }
828
829   /* copy requested decrypted data to supplied buffer */
830   size = len < connssl->decdata_offset ? len : connssl->decdata_offset;
831   if(size > 0) {
832     memcpy(buf, connssl->decdata_buffer, size);
833     ret = size;
834
835     /* move remaining decrypted data forward to the beginning of buffer */
836     memmove(connssl->decdata_buffer, connssl->decdata_buffer + size,
837             connssl->decdata_offset - size);
838     connssl->decdata_offset -= size;
839   }
840
841   /* reduce internal buffer length to reduce memory usage */
842   if(connssl->encdata_length > 4096) {
843     connssl->encdata_length = connssl->encdata_offset > 4096 ?
844                               connssl->encdata_offset : 4096;
845     connssl->encdata_buffer = realloc(connssl->encdata_buffer,
846                                       connssl->encdata_length);
847   }
848   if(connssl->decdata_length > 4096) {
849     connssl->decdata_length = connssl->decdata_offset > 4096 ?
850                               connssl->decdata_offset : 4096;
851     connssl->decdata_buffer = realloc(connssl->decdata_buffer,
852                                       connssl->decdata_length);
853   }
854
855   /* check if the server closed the connection */
856   if(ret <= 0 && ( /* special check for Windows 2000 Professional */
857       sspi_status == SEC_I_CONTEXT_EXPIRED || (sspi_status == SEC_E_OK &&
858         connssl->encdata_offset > 0 && connssl->encdata_buffer[0] == 0x15))) {
859     infof(data, "schannel: server closed the connection\n");
860     *err = CURLE_OK;
861     return 0;
862   }
863
864   /* check if something went wrong and we need to return an error */
865   if(ret < 0 && sspi_status != SEC_E_OK) {
866     sspi_msg = Curl_sspi_status_msg(sspi_status);
867     infof(data, "schannel: failed to read data from server: %s\n", sspi_msg);
868     free(sspi_msg);
869     *err = CURLE_RECV_ERROR;
870     return -1;
871   }
872
873   return ret;
874 }
875
876 CURLcode
877 Curl_schannel_connect_nonblocking(struct connectdata *conn, int sockindex,
878                                   bool *done) {
879   return schannel_connect_common(conn, sockindex, TRUE, done);
880 }
881
882 CURLcode
883 Curl_schannel_connect(struct connectdata *conn, int sockindex) {
884   CURLcode retcode;
885   bool done = FALSE;
886
887   retcode = schannel_connect_common(conn, sockindex, FALSE, &done);
888   if(retcode)
889     return retcode;
890
891   DEBUGASSERT(done);
892
893   return CURLE_OK;
894 }
895
896 bool Curl_schannel_data_pending(const struct connectdata *conn, int sockindex) {
897   const struct ssl_connect_data *connssl = &conn->ssl[sockindex];
898
899   if(connssl->use) /* SSL is in use */
900     return (connssl->encdata_offset > 0 ||
901             connssl->decdata_offset > 0 ) ? TRUE : FALSE;
902   else
903     return FALSE;
904 }
905
906 void Curl_schannel_close(struct connectdata *conn, int sockindex) {
907   struct SessionHandle *data = conn->data;
908   struct ssl_connect_data *connssl = &conn->ssl[sockindex];
909
910   infof(data, "schannel: Closing connection with %s:%d\n",
911         conn->host.name, conn->remote_port);
912
913   /* free SSPI Schannel API security context handle */
914   if(connssl->ctxt) {
915     s_pSecFn->DeleteSecurityContext(&connssl->ctxt->ctxt_handle);
916     free(connssl->ctxt);
917     connssl->ctxt = NULL;
918   }
919
920   /* free internal buffer for received encrypted data */
921   if(connssl->encdata_buffer != NULL) {
922     free(connssl->encdata_buffer);
923     connssl->encdata_buffer = NULL;
924     connssl->encdata_length = 0;
925     connssl->encdata_offset = 0;
926   }
927
928   /* free internal buffer for received decrypted data */
929   if(connssl->decdata_buffer != NULL) {
930     free(connssl->decdata_buffer);
931     connssl->decdata_buffer = NULL;
932     connssl->decdata_length = 0;
933     connssl->decdata_offset = 0;
934   }
935 }
936
937 int Curl_schannel_shutdown(struct connectdata *conn, int sockindex) {
938   return CURLE_NOT_BUILT_IN; /* TODO: implement SSL/TLS shutdown */
939 }
940
941 void Curl_schannel_session_free(void *ptr) {
942   curl_schannel_cred *cred = ptr;
943
944   if(cred) {
945     s_pSecFn->FreeCredentialsHandle(&cred->cred_handle);
946     free(cred);
947   }
948 }
949
950 int Curl_schannel_init() {
951   return (Curl_sspi_global_init() == CURLE_OK ? 1 : 0);
952 }
953
954 void Curl_schannel_cleanup() {
955   Curl_sspi_global_cleanup();
956 }
957
958 size_t Curl_schannel_version(char *buffer, size_t size)
959 {
960   char* version = Curl_sspi_version();
961   size = snprintf(buffer, size, "Schannel-%s", version);
962   free(version);
963   return size;
964 }
965
966 #endif /* USE_SCHANNEL */
967 #endif /* USE_WINDOWS_SSPI */