]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
schannel: handle Curl_conn_cf_send() errors better
authorDaniel Stenberg <daniel@haxx.se>
Tue, 28 Oct 2025 08:05:25 +0000 (09:05 +0100)
committerDaniel Stenberg <daniel@haxx.se>
Tue, 28 Oct 2025 22:26:33 +0000 (23:26 +0100)
Avoid a resource leak in the error path.

Reported-by: Joshua Rogers
Closes #19265

lib/vtls/schannel.c

index 0561d6fbddfaa8e4c1ad0cce96dfc27615bbe1d0..376d1a67eae371965f5e40586b32ad2cf904a1c5 100644 (file)
@@ -1113,6 +1113,41 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
   return CURLE_OK;
 }
 
+static CURLcode schannel_error(struct Curl_easy *data,
+                               SECURITY_STATUS sspi_status)
+{
+  char buffer[STRERROR_LEN];
+  switch(sspi_status) {
+  case SEC_E_INSUFFICIENT_MEMORY:
+    failf(data, "schannel: next InitializeSecurityContext failed: %s",
+          Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer)));
+    return CURLE_OUT_OF_MEMORY;
+  case SEC_E_WRONG_PRINCIPAL:
+    failf(data, "schannel: SNI or certificate check failed: %s",
+          Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer)));
+    return CURLE_PEER_FAILED_VERIFICATION;
+  case SEC_E_UNTRUSTED_ROOT:
+    failf(data, "schannel: %s",
+          Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer)));
+    return CURLE_PEER_FAILED_VERIFICATION;
+#if 0
+  case SEC_E_INVALID_HANDLE:
+  case SEC_E_INVALID_TOKEN:
+  case SEC_E_LOGON_DENIED:
+  case SEC_E_TARGET_UNKNOWN:
+  case SEC_E_NO_AUTHENTICATING_AUTHORITY:
+  case SEC_E_INTERNAL_ERROR:
+  case SEC_E_NO_CREDENTIALS:
+  case SEC_E_UNSUPPORTED_FUNCTION:
+  case SEC_E_APPLICATION_PROTOCOL_MISMATCH:
+#endif
+  default:
+    failf(data, "schannel: next InitializeSecurityContext failed: %s",
+          Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer)));
+    return CURLE_SSL_CONNECT_ERROR;
+  }
+}
+
 static CURLcode
 schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
 {
@@ -1266,28 +1301,18 @@ schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
     Curl_safefree(inbuf[0].pvBuffer);
 
     /* check if the handshake was incomplete */
-    if(sspi_status == SEC_E_INCOMPLETE_MESSAGE) {
+    switch(sspi_status) {
+    case SEC_E_INCOMPLETE_MESSAGE:
       backend->encdata_is_incomplete = TRUE;
       connssl->io_need = CURL_SSL_IO_NEED_RECV;
       DEBUGF(infof(data,
                    "schannel: received incomplete message, need more data"));
       return CURLE_OK;
-    }
 
-    /* If the server has requested a client certificate, attempt to continue
-       the handshake without one. This will allow connections to servers which
-       request a client certificate but do not require it. */
-    if(sspi_status == SEC_I_INCOMPLETE_CREDENTIALS &&
-       !(backend->req_flags & ISC_REQ_USE_SUPPLIED_CREDS)) {
-      backend->req_flags |= ISC_REQ_USE_SUPPLIED_CREDS;
-      connssl->io_need = CURL_SSL_IO_NEED_SEND;
-      DEBUGF(infof(data,
-                   "schannel: a client certificate has been requested"));
-      return CURLE_OK;
-    }
-
-    /* check if the handshake needs to be continued */
-    if(sspi_status == SEC_I_CONTINUE_NEEDED || sspi_status == SEC_E_OK) {
+    case SEC_I_CONTINUE_NEEDED:
+    case SEC_E_OK:
+      /* check if the handshake needs to be continued */
+      result = CURLE_OK;
       for(i = 0; i < 3; i++) {
         /* search for handshake tokens that need to be send */
         if(outbuf[i].BufferType == SECBUFFER_TOKEN && outbuf[i].cbBuffer > 0) {
@@ -1301,47 +1326,35 @@ schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
           if(result || (outbuf[i].cbBuffer != written)) {
             failf(data, "schannel: failed to send next handshake data: "
                   "sent %zu of %lu bytes", written, outbuf[i].cbBuffer);
-            return CURLE_SSL_CONNECT_ERROR;
+            result = CURLE_SSL_CONNECT_ERROR;
           }
         }
-
+      }
+      for(i = 0; i < 3; i++) {
         /* free obsolete buffer */
-        if(outbuf[i].pvBuffer) {
+        if(outbuf[i].pvBuffer)
           Curl_pSecFn->FreeContextBuffer(outbuf[i].pvBuffer);
-        }
       }
-    }
-    else {
-      char buffer[STRERROR_LEN];
-      switch(sspi_status) {
-      case SEC_E_INSUFFICIENT_MEMORY:
-        failf(data, "schannel: next InitializeSecurityContext failed: %s",
-              Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer)));
-        return CURLE_OUT_OF_MEMORY;
-      case SEC_E_WRONG_PRINCIPAL:
-        failf(data, "schannel: SNI or certificate check failed: %s",
-              Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer)));
-        return CURLE_PEER_FAILED_VERIFICATION;
-      case SEC_E_UNTRUSTED_ROOT:
-        failf(data, "schannel: %s",
-              Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer)));
-        return CURLE_PEER_FAILED_VERIFICATION;
-#if 0
-      case SEC_E_INVALID_HANDLE:
-      case SEC_E_INVALID_TOKEN:
-      case SEC_E_LOGON_DENIED:
-      case SEC_E_TARGET_UNKNOWN:
-      case SEC_E_NO_AUTHENTICATING_AUTHORITY:
-      case SEC_E_INTERNAL_ERROR:
-      case SEC_E_NO_CREDENTIALS:
-      case SEC_E_UNSUPPORTED_FUNCTION:
-      case SEC_E_APPLICATION_PROTOCOL_MISMATCH:
-#endif
-      default:
-        failf(data, "schannel: next InitializeSecurityContext failed: %s",
-              Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer)));
-        return CURLE_SSL_CONNECT_ERROR;
+      if(result)
+        return result;
+      break;
+
+    case SEC_I_INCOMPLETE_CREDENTIALS:
+      if(!(backend->req_flags & ISC_REQ_USE_SUPPLIED_CREDS)) {
+        /* If the server has requested a client certificate, attempt to
+           continue the handshake without one. This will allow connections to
+           servers which request a client certificate but do not require
+           it. */
+        backend->req_flags |= ISC_REQ_USE_SUPPLIED_CREDS;
+        connssl->io_need = CURL_SSL_IO_NEED_SEND;
+        DEBUGF(infof(data,
+                     "schannel: a client certificate has been requested"));
+        return CURLE_OK;
       }
+      FALLTHROUGH();
+
+    default:
+      return schannel_error(data, sspi_status);
     }
 
     /* check if there was additional remaining encrypted data */