]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
SignatureAlgorithms: force-enable GOST signatures for GOST KX
authorDmitry Eremin-Solenikov <dbaryshkov@gmail.com>
Fri, 8 Nov 2019 23:01:22 +0000 (02:01 +0300)
committerDmitry Eremin-Solenikov <dbaryshkov@gmail.com>
Fri, 27 Dec 2019 22:06:57 +0000 (01:06 +0300)
SChannel-based clients can not send GOST identifiers as a part of
SignatureAlgorithms extension. To mitigate this forcefully enable GOST
signature algorithms if client sends GOST ciphersuite.

Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
lib/auth/cert.c
lib/ext/signature.c
lib/ext/signature.h
lib/tls-sig.c
lib/tls13/certificate_request.c
lib/tls13/certificate_verify.c

index fabd7c8a4138ed64f9aa41cf8f9356ff85810f76..3073a33d3400ec44f133c5760df43aad870bb4f1 100644 (file)
@@ -1516,7 +1516,7 @@ int cert_select_sign_algorithm(gnutls_session_t session,
                return 0;
        }
 
-       algo = _gnutls_session_get_sign_algo(session, cert, pkey, 0);
+       algo = _gnutls_session_get_sign_algo(session, cert, pkey, 0, cs->kx_algorithm);
        if (algo == GNUTLS_SIGN_UNKNOWN)
                return gnutls_assert_val(GNUTLS_E_INCOMPATIBLE_SIG_WITH_KEY);
 
index 3f3652f51e1d0890e61da7a3c2b066a87227f430..bb350f5863e0ea0f1cee957709be21e49efe1187 100644 (file)
 #include <algorithms.h>
 #include <abstract_int.h>
 
+/*
+ * Some (all SChannel) clients fail to send proper SigAlgs due to Micro$oft crazyness.
+ * Patch the extension for them.
+ */
+#ifdef ENABLE_GOST
+#define GOST_SIG_FIXUP_SCHANNEL
+#endif
+
 static int _gnutls_signature_algorithm_recv_params(gnutls_session_t
                                                   session,
                                                   const uint8_t * data,
@@ -265,6 +273,23 @@ _gnutls_signature_algorithm_send_params(gnutls_session_t session,
        return 0;
 }
 
+#ifdef GOST_SIG_FIXUP_SCHANNEL
+static bool
+is_gost_sig_present(sig_ext_st *priv)
+{
+       unsigned i;
+       const gnutls_sign_entry_st *se;
+
+       for (i = 0; i < priv->sign_algorithms_size; i++) {
+               se = _gnutls_sign_to_entry(priv->sign_algorithms[i]);
+               if (se != NULL && _sign_is_gost(se))
+                       return true;
+       }
+
+       return false;
+}
+#endif
+
 /* Returns a requested by the peer signature algorithm that
  * matches the given certificate's public key algorithm.
  *
@@ -277,7 +302,8 @@ gnutls_sign_algorithm_t
 _gnutls_session_get_sign_algo(gnutls_session_t session,
                              gnutls_pcert_st * cert,
                              gnutls_privkey_t privkey,
-                             unsigned client_cert)
+                             unsigned client_cert,
+                             gnutls_kx_algorithm_t kx_algorithm)
 {
        unsigned i;
        int ret;
@@ -296,9 +322,45 @@ _gnutls_session_get_sign_algo(gnutls_session_t session,
            _gnutls_hello_ext_get_priv(session,
                                        GNUTLS_EXTENSION_SIGNATURE_ALGORITHMS,
                                        &epriv);
-       priv = epriv;
+       if (ret < 0)
+               priv = NULL;
+       else
+               priv = epriv;
+
+#ifdef GOST_SIG_FIXUP_SCHANNEL
+       /*
+        * Some (all SChannel) clients fail to send proper SigAlgs due to Micro$oft crazyness.
+        * If we are negotiating GOST KX (because we have received GOST
+        * ciphersuites) and if we have received no GOST SignatureAlgorithms,
+        * assume that the client could not send them and continue negotiation
+        * as if correct algorithm was sent.
+        */
+       if (_gnutls_kx_is_vko_gost(kx_algorithm) &&
+           (!priv ||
+            !is_gost_sig_present(priv) ||
+            !_gnutls_version_has_selectable_sighash(ver))) {
+               gnutls_digest_algorithm_t dig;
+
+               _gnutls_handshake_log("EXT[%p]: GOST KX, but no GOST SigAlgs received, patching up.", session);
+
+               if (cert_algo == GNUTLS_PK_GOST_01)
+                       dig = GNUTLS_DIG_GOSTR_94;
+               else if (cert_algo == GNUTLS_PK_GOST_12_256)
+                       dig = GNUTLS_DIG_STREEBOG_256;
+               else if (cert_algo == GNUTLS_PK_GOST_12_512)
+                       dig = GNUTLS_DIG_STREEBOG_512;
+               else
+                       dig = GNUTLS_DIG_SHA1;
+
+               ret = gnutls_pk_to_sign(cert_algo, dig);
+
+               if (!client_cert && _gnutls_session_sign_algo_enabled(session, ret) < 0)
+                       goto fail;
+               return ret;
+       }
+#endif
 
-       if (ret < 0 || !_gnutls_version_has_selectable_sighash(ver)) {
+       if (!priv || !_gnutls_version_has_selectable_sighash(ver)) {
                /* none set, allow SHA-1 only */
                ret = gnutls_pk_to_sign(cert_algo, GNUTLS_DIG_SHA1);
 
index a6448f34b6695bddd7a0d582ee5cdd6279cc0355..ef42763cfe498f141dcdff623b9162d8cb712853 100644 (file)
@@ -34,7 +34,8 @@ gnutls_sign_algorithm_t
 _gnutls_session_get_sign_algo(gnutls_session_t session,
                              gnutls_pcert_st * cert,
                              gnutls_privkey_t privkey,
-                             unsigned client_cert);
+                             unsigned client_cert,
+                             gnutls_kx_algorithm_t kx_algorithm);
 int _gnutls_sign_algorithm_parse_data(gnutls_session_t session,
                                      const uint8_t * data,
                                      size_t data_size);
index 80514430ab39b684a1827d09347b5c4aa6ce9dd8..779e02c18f09820535e986f7a360b4c410584a86 100644 (file)
@@ -627,7 +627,7 @@ _gnutls_handshake_sign_crt_vrfy12(gnutls_session_t session,
        const gnutls_sign_entry_st *se;
        int ret;
 
-       sign_algo = _gnutls_session_get_sign_algo(session, cert, pkey, 1);
+       sign_algo = _gnutls_session_get_sign_algo(session, cert, pkey, 1, GNUTLS_KX_UNKNOWN);
        if (sign_algo == GNUTLS_SIGN_UNKNOWN) {
                gnutls_assert();
                return GNUTLS_E_UNWANTED_ALGORITHM;
index d56ce4273828b6bb05b901a56d9df5119001b87a..58fdbbc1871ce021d546c3558ce619cfc36c0a48 100644 (file)
@@ -187,7 +187,7 @@ int _gnutls13_recv_certificate_request_int(gnutls_session_t session, gnutls_buff
        if (apr_cert_list_length > 0) {
                gnutls_sign_algorithm_t algo;
 
-               algo = _gnutls_session_get_sign_algo(session, &apr_cert_list[0], apr_pkey, 0);
+               algo = _gnutls_session_get_sign_algo(session, &apr_cert_list[0], apr_pkey, 0, GNUTLS_KX_UNKNOWN);
                if (algo == GNUTLS_SIGN_UNKNOWN) {
                        _gnutls_handshake_log("HSK[%p]: rejecting client auth because of no suitable signature algorithm\n", session);
                        _gnutls_selected_certs_deinit(session);
index 6c3617c026fde533b3eada7d33e9b023d3b81315..45ff6facfce9f4cc8fbf339b47cf148bc18bf925 100644 (file)
@@ -188,7 +188,7 @@ int _gnutls13_send_certificate_verify(gnutls_session_t session, unsigned again)
                }
 
                if (server) {
-                       algo = _gnutls_session_get_sign_algo(session, &apr_cert_list[0], apr_pkey, 0);
+                       algo = _gnutls_session_get_sign_algo(session, &apr_cert_list[0], apr_pkey, 0, GNUTLS_KX_UNKNOWN);
                        if (algo == GNUTLS_SIGN_UNKNOWN)
                                return gnutls_assert_val(GNUTLS_E_INCOMPATIBLE_SIG_WITH_KEY);