lib/crypto: add legacy_gnutls_server_end_point_cb() if needed
authorStefan Metzmacher <metze@samba.org>
Tue, 5 Mar 2024 08:55:47 +0000 (09:55 +0100)
committerAndrew Bartlett <abartlet@samba.org>
Tue, 23 Apr 2024 23:50:33 +0000 (23:50 +0000)
gnutls_session_channel_binding(GNUTLS_CB_TLS_SERVER_END_POINT)
is only available with gnutls 3.7.2, but we still want to
support older gnutls versions and that's easily doable...

BUG: https://bugzilla.samba.org/show_bug.cgi?id=15621

Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
lib/crypto/gnutls_helpers.h
lib/crypto/gnutls_server_end_point_cb.c [new file with mode: 0644]
lib/crypto/wscript
wscript_configure_system_gnutls

index 0362d5ee78220b1e6fe8ee34f98f42e7bd5583bb..6699ebc019689b22487c0b43cda4146a530f9a7c 100644 (file)
@@ -233,4 +233,10 @@ NTSTATUS samba_gnutls_sp800_108_derive_key(
        uint8_t *KO,
        size_t KO_len);
 
+#ifndef HAVE_GNUTLS_CB_TLS_SERVER_END_POINT
+int legacy_gnutls_server_end_point_cb(gnutls_session_t session,
+                                     bool is_server,
+                                     gnutls_datum_t * cb);
+#endif /* HAVE_GNUTLS_CB_TLS_SERVER_END_POINT */
+
 #endif /* _GNUTLS_HELPERS_H */
diff --git a/lib/crypto/gnutls_server_end_point_cb.c b/lib/crypto/gnutls_server_end_point_cb.c
new file mode 100644 (file)
index 0000000..c909197
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2002-2016 Free Software Foundation, Inc.
+ * Copyright (C) 2014-2016 Nikos Mavrogiannopoulos
+ * Copyright (C) 2015-2018 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+#include "replace.h"
+#include "gnutls_helpers.h"
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+
+int legacy_gnutls_server_end_point_cb(gnutls_session_t session,
+                                     bool is_server,
+                                     gnutls_datum_t * cb)
+{
+       /*
+        * copied from the logic in gnutls_session_channel_binding()
+        * introduced by gnutls commit (as LGPL 2.1+):
+        *
+        * commit 9ebee00c793e40e3e8c797c645577c9e025b9f1e
+        * Author: Ruslan N. Marchenko <me@ruff.mobi>
+        * Date:   Sat May 1 23:05:54 2021 +0200
+        *
+        *  Add tls-server-end-point tls channel binding implementation.
+        *  ...
+        */
+       const gnutls_datum_t *ders = NULL;
+       unsigned int num_certs = 1;
+       int ret;
+       size_t rlen;
+       gnutls_x509_crt_t cert;
+       gnutls_digest_algorithm_t algo;
+
+       /* Only X509 certificates are supported for this binding type */
+       ret = gnutls_certificate_type_get(session);
+       if (ret != GNUTLS_CRT_X509) {
+               return GNUTLS_E_UNIMPLEMENTED_FEATURE;
+       }
+
+       if (is_server) {
+               ders = gnutls_certificate_get_ours(session);
+       } else {
+               ders = gnutls_certificate_get_peers(session, &num_certs);
+       }
+
+       /* Previous check indicated we have x509 but you never know */
+       if (!ders || num_certs == 0) {
+               return GNUTLS_E_UNIMPLEMENTED_FEATURE;
+       }
+
+       ret = gnutls_x509_crt_list_import(&cert,
+                                         &num_certs,
+                                         ders,
+                                         GNUTLS_X509_FMT_DER,
+                                         0);
+       /* Again, this is not supposed to happen (normally) */
+       if (ret < 0 || num_certs == 0) {
+               return GNUTLS_E_CHANNEL_BINDING_NOT_AVAILABLE;
+       }
+
+       /* Obtain signature algorithm used by certificate */
+       ret = gnutls_x509_crt_get_signature_algorithm(cert);
+       if (ret < 0 || ret == GNUTLS_SIGN_UNKNOWN) {
+               gnutls_x509_crt_deinit(cert);
+               return GNUTLS_E_UNIMPLEMENTED_FEATURE;
+       }
+
+       /* obtain hash function from signature and normalize it */
+       algo = gnutls_sign_get_hash_algorithm(ret);
+       switch (algo) {
+       case GNUTLS_DIG_MD5:
+       case GNUTLS_DIG_SHA1:
+               algo = GNUTLS_DIG_SHA256;
+               break;
+       case GNUTLS_DIG_UNKNOWN:
+       case GNUTLS_DIG_NULL:
+       case GNUTLS_DIG_MD5_SHA1:
+               /* double hashing not supported either */
+               gnutls_x509_crt_deinit(cert);
+               return GNUTLS_E_UNIMPLEMENTED_FEATURE;
+       default:
+               break;
+       }
+
+       /* preallocate 512 bits buffer as maximum supported digest */
+       rlen = 64;
+       cb->data = gnutls_malloc(rlen);
+       if (cb->data == NULL) {
+               gnutls_x509_crt_deinit(cert);
+               return GNUTLS_E_MEMORY_ERROR;
+       }
+
+       ret = gnutls_x509_crt_get_fingerprint(cert,
+                                             algo,
+                                             cb->data,
+                                             &rlen);
+       if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) {
+               cb->data = gnutls_realloc(cb->data, cb->size);
+               if (cb->data == NULL) {
+                       gnutls_x509_crt_deinit(cert);
+                       return GNUTLS_E_MEMORY_ERROR;
+               }
+               ret = gnutls_x509_crt_get_fingerprint(cert,
+                                                     algo,
+                                                     cb->data,
+                                                     &rlen);
+       }
+
+       cb->size = rlen;
+       gnutls_x509_crt_deinit(cert);
+       return ret;
+}
index 251eaae363ce51e5ad18044e6c18166433c92e99..7d70b62f4ae2266b747996253c0a0d49c5ca6659 100644 (file)
@@ -2,6 +2,10 @@
 
 
 def build(bld):
+    legacy_gnutls_helpers = ''
+    if not bld.CONFIG_SET('HAVE_GNUTLS_CB_TLS_SERVER_END_POINT'):
+        legacy_gnutls_helpers += ' gnutls_server_end_point_cb.c'
+
     bld.SAMBA_SUBSYSTEM("GNUTLS_HELPERS",
                         source='''
                         gnutls_error.c
@@ -9,7 +13,7 @@ def build(bld):
                         gnutls_arcfour_confounded_md5.c
                         gnutls_weak_crypto.c
                         gnutls_sp800_108.c
-                        ''',
+                        ''' + legacy_gnutls_helpers,
                         deps="gnutls samba-errors")
 
     bld.SAMBA_SUBSYSTEM('LIBCRYPTO',
index 1983a0cdfe94cd5773e10751251a1629a469f992..f62de5560d6393b4864d768432078287a963e314 100644 (file)
@@ -34,6 +34,11 @@ conf.SET_TARGET_TYPE('gnutls', 'SYSLIB')
 if (gnutls_version > parse_version('3.6.14')):
       conf.DEFINE('ALLOW_GNUTLS_AEAD_CIPHER_ENCRYPTV2_AES_CCM', 1)
 
+# GNUTLS_CB_TLS_SERVER_END_POINT is available with
+# 3.7.2
+if (gnutls_version >= parse_version('3.7.2')):
+      conf.DEFINE('HAVE_GNUTLS_CB_TLS_SERVER_END_POINT', 1)
+
 # Check if gnutls has fips mode support
 # gnutls_fips140_mode_enabled() is available since 3.3.0
 fragment = '''