]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
schannel: ban server ALPN change during recv renegotiation
authorJay Satiro <raysatiro@yahoo.com>
Fri, 9 Sep 2022 19:33:47 +0000 (15:33 -0400)
committerJay Satiro <raysatiro@yahoo.com>
Mon, 26 Sep 2022 07:26:49 +0000 (03:26 -0400)
By the time schannel_recv is renegotiating the connection, libcurl has
already decided on a protocol and it is too late for the server to
select a protocol via ALPN except for the originally selected protocol.

Ref: https://github.com/curl/curl/issues/9451

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

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

index 25b46b23c02b63c650385421709bbee89bb82a4f..50928aee6157641cd276ea94734a37fb66aaf677 100644 (file)
@@ -1314,6 +1314,7 @@ schannel_connect_step1(struct Curl_easy *data, struct connectdata *conn,
   backend->recv_unrecoverable_err = CURLE_OK;
   backend->recv_sspi_close_notify = false;
   backend->recv_connection_closed = false;
+  backend->recv_renegotiating = false;
   backend->encdata_is_incomplete = false;
 
   /* continue to second handshake step */
@@ -1713,6 +1714,7 @@ schannel_connect_step3(struct Curl_easy *data, struct connectdata *conn,
 
     if(alpn_result.ProtoNegoStatus ==
        SecApplicationProtocolNegotiationStatus_Success) {
+      unsigned char alpn = 0;
 
       infof(data, VTLS_INFOF_ALPN_ACCEPTED_LEN_1STR,
             alpn_result.ProtocolIdSize, alpn_result.ProtocolId);
@@ -1720,20 +1722,33 @@ schannel_connect_step3(struct Curl_easy *data, struct connectdata *conn,
 #ifdef USE_HTTP2
       if(alpn_result.ProtocolIdSize == ALPN_H2_LENGTH &&
          !memcmp(ALPN_H2, alpn_result.ProtocolId, ALPN_H2_LENGTH)) {
-        conn->alpn = CURL_HTTP_VERSION_2;
+        alpn = CURL_HTTP_VERSION_2;
       }
       else
 #endif
         if(alpn_result.ProtocolIdSize == ALPN_HTTP_1_1_LENGTH &&
            !memcmp(ALPN_HTTP_1_1, alpn_result.ProtocolId,
                    ALPN_HTTP_1_1_LENGTH)) {
-          conn->alpn = CURL_HTTP_VERSION_1_1;
+          alpn = CURL_HTTP_VERSION_1_1;
         }
+      if(backend->recv_renegotiating) {
+        if(alpn != conn->alpn) {
+          failf(data, "schannel: server selected an ALPN protocol too late");
+          return CURLE_SSL_CONNECT_ERROR;
+        }
+      }
+      else
+        conn->alpn = alpn;
+    }
+    else {
+      if(!backend->recv_renegotiating)
+        infof(data, VTLS_INFOF_NO_ALPN);
+    }
+
+    if(!backend->recv_renegotiating) {
+      Curl_multiuse_state(data, conn->alpn == CURL_HTTP_VERSION_2 ?
+                          BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
     }
-    else
-      infof(data, VTLS_INFOF_NO_ALPN);
-    Curl_multiuse_state(data, conn->alpn == CURL_HTTP_VERSION_2 ?
-                        BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
   }
 #endif
 
@@ -2293,7 +2308,9 @@ schannel_recv(struct Curl_easy *data, int sockindex,
         infof(data, "schannel: renegotiating SSL/TLS connection");
         connssl->state = ssl_connection_negotiating;
         connssl->connecting_state = ssl_connect_2_writing;
+        backend->recv_renegotiating = true;
         *err = schannel_connect_common(data, conn, sockindex, FALSE, &done);
+        backend->recv_renegotiating = false;
         if(*err) {
           infof(data, "schannel: renegotiation failed");
           goto cleanup;
index b2d222ac2411a55240c8df7ed23d482465c4e63b..000d1e7b3052c82184bd42c686eac0b3015c6b33 100644 (file)
@@ -179,6 +179,7 @@ struct ssl_backend_data {
   CURLcode recv_unrecoverable_err; /* schannel_recv had an unrecoverable err */
   bool recv_sspi_close_notify; /* true if connection closed by close_notify */
   bool recv_connection_closed; /* true if connection closed, regardless how */
+  bool recv_renegotiating;     /* true if recv is doing renegotiation */
   bool use_alpn; /* true if ALPN is used for this connection */
 #ifdef HAS_MANUAL_VERIFY_API
   bool use_manual_cred_validation; /* true if manual cred validation is used */