schannel: Don't treat encrypted partial record as pending data
authorJay Satiro <raysatiro@yahoo.com>
Thu, 6 Apr 2017 07:27:28 +0000 (03:27 -0400)
committerJay Satiro <raysatiro@yahoo.com>
Sun, 23 Apr 2017 02:39:40 +0000 (22:39 -0400)
- Track when the cached encrypted data contains only a partial record
  that can't be decrypted without more data (SEC_E_INCOMPLETE_MESSAGE).

- Change Curl_schannel_data_pending to return false in such a case.

Other SSL libraries have pending data functions that behave similarly.

Ref: https://github.com/curl/curl/pull/1387

Closes https://github.com/curl/curl/pull/1392

lib/urldata.h
lib/vtls/schannel.c

index 34e18ecde55780b140ee4152a2a67e1b190f3428..d4a4a2306526c7f863875426f99d52436a329a70 100644 (file)
@@ -333,6 +333,11 @@ struct ssl_connect_data {
   size_t encdata_length, decdata_length;
   size_t encdata_offset, decdata_offset;
   unsigned char *encdata_buffer, *decdata_buffer;
+  /* encdata_is_incomplete: if encdata contains only a partial record that
+     can't be decrypted without another Curl_read_plain (that is, status is
+     SEC_E_INCOMPLETE_MESSAGE) then set this true. after Curl_read_plain writes
+     more bytes into encdata then set this back to false. */
+  bool encdata_is_incomplete;
   unsigned long req_flags, ret_flags;
   CURLcode recv_unrecoverable_err; /* schannel_recv had an unrecoverable err */
   bool recv_sspi_close_notify; /* true if connection closed by close_notify */
index c9b513230af708786da9e3103932c26d079d5399..d20f30d897d6bcae751de5012b83e0f2d2bb1580 100644 (file)
@@ -432,6 +432,7 @@ schannel_connect_step1(struct connectdata *conn, int sockindex)
   connssl->recv_unrecoverable_err = CURLE_OK;
   connssl->recv_sspi_close_notify = false;
   connssl->recv_connection_closed = false;
+  connssl->encdata_is_incomplete = false;
 
   /* continue to second handshake step */
   connssl->connecting_state = ssl_connect_2;
@@ -480,6 +481,7 @@ schannel_connect_step2(struct connectdata *conn, int sockindex)
 
   /* buffer to store previously received and encrypted data */
   if(connssl->encdata_buffer == NULL) {
+    connssl->encdata_is_incomplete = false;
     connssl->encdata_offset = 0;
     connssl->encdata_length = CURL_SCHANNEL_BUFFER_INIT_SIZE;
     connssl->encdata_buffer = malloc(connssl->encdata_length);
@@ -532,6 +534,8 @@ schannel_connect_step2(struct connectdata *conn, int sockindex)
 
       /* increase encrypted data buffer offset */
       connssl->encdata_offset += nread;
+      connssl->encdata_is_incomplete = false;
+      infof(data, "schannel: encrypted data got %zd\n", nread);
     }
 
     infof(data, "schannel: encrypted data buffer: offset %zu length %zu\n",
@@ -576,6 +580,7 @@ schannel_connect_step2(struct connectdata *conn, int sockindex)
 
     /* check if the handshake was incomplete */
     if(sspi_status == SEC_E_INCOMPLETE_MESSAGE) {
+      connssl->encdata_is_incomplete = true;
       connssl->connecting_state = ssl_connect_2_reading;
       infof(data, "schannel: received incomplete message, need more data\n");
       return CURLE_OK;
@@ -1177,6 +1182,7 @@ schannel_recv(struct connectdata *conn, int sockindex,
     }
     else if(nread > 0) {
       connssl->encdata_offset += (size_t)nread;
+      connssl->encdata_is_incomplete = false;
       infof(data, "schannel: encrypted data got %zd\n", nread);
     }
   }
@@ -1313,6 +1319,7 @@ schannel_recv(struct connectdata *conn, int sockindex,
       }
     }
     else if(sspi_status == SEC_E_INCOMPLETE_MESSAGE) {
+      connssl->encdata_is_incomplete = true;
       if(!*err)
         *err = CURLE_AGAIN;
       infof(data, "schannel: failed to decrypt data, need more data\n");
@@ -1414,8 +1421,8 @@ bool Curl_schannel_data_pending(const struct connectdata *conn, int sockindex)
   const struct ssl_connect_data *connssl = &conn->ssl[sockindex];
 
   if(connssl->use) /* SSL/TLS is in use */
-    return (connssl->encdata_offset > 0 ||
-            connssl->decdata_offset > 0) ? TRUE : FALSE;
+    return (connssl->decdata_offset > 0 ||
+            (connssl->encdata_offset > 0 && !connssl->encdata_is_incomplete));
   else
     return FALSE;
 }
@@ -1518,6 +1525,7 @@ int Curl_schannel_shutdown(struct connectdata *conn, int sockindex)
     Curl_safefree(connssl->encdata_buffer);
     connssl->encdata_length = 0;
     connssl->encdata_offset = 0;
+    connssl->encdata_is_incomplete = false;
   }
 
   /* free internal buffer for received decrypted data */