# # Upstream: http://lists.gnu.org/archive/html/help-gnutls/2009-08/msg00011.html # Patch: adapted from upstream commits: # http://git.savannah.gnu.org/cgit/gnutls.git/patch/?id=177e7ddb761999cd8b439e14a2bf43590756e230 # http://git.savannah.gnu.org/cgit/gnutls.git/commit/?h=gnutls_2_8_x&id=a431be86124f900c4082e82d32917f86fcce461a # http://git.savannah.gnu.org/cgit/gnutls.git/commit/?h=gnutls_2_8_x&id=74b6d92f9675ce4e03642c4d6ced4a3a614b07f6 # http://git.savannah.gnu.org/cgit/gnutls.git/commit/?h=gnutls_2_8_x&id=40081594e3de518b998f3e5177ed5a9f7707f2e8 # http://git.savannah.gnu.org/cgit/gnutls.git/patch/?id=5a58e9d33448235377afd5fbfcee1683dc70eae3 # http://git.savannah.gnu.org/cgit/gnutls.git/patch/?id=1ea190d216767dd4ab93b87361cbcb9d4fb3aafc # Description: fix improper handling of '\0' in Common Name (CN) and Subject # Alternative Name (SAN) in X.509 certificates. Also backported # 177e7ddb761999cd8b439e14a2bf43590756e230 for added wide wildcard # hostname matching so _gnutls_hostname_compare() is up to date # with upstream GnuTLS. # diff -Nur -x '*.orig' -x '*~' gnutls13-2.0.4/lib/x509/common.c gnutls13-2.0.4.new/lib/x509/common.c --- gnutls13-2.0.4/lib/x509/common.c 2007-11-15 09:24:56.000000000 -0600 +++ gnutls13-2.0.4.new/lib/x509/common.c 2009-08-14 15:12:06.698514568 -0500 @@ -239,6 +239,10 @@ { str[len] = 0; + /* Refuse to deal with strings containing NULs. */ + if (strlen (str) != len) + return GNUTLS_E_ASN1_DER_ERROR; + if (res) _gnutls_str_cpy (res, *res_size, str); *res_size = len; @@ -289,25 +293,27 @@ non_printable = 0; } - if (res) + if (non_printable == 0) { - if (non_printable == 0) - { - str[len] = 0; - _gnutls_str_cpy (res, *res_size, str); - *res_size = len; - } - else + str[len] = 0; + + /* Refuse to deal with strings containing NULs. */ + if (strlen (str) != len) + return GNUTLS_E_ASN1_DER_ERROR; + + if (res) + _gnutls_str_cpy (res, *res_size, str); + *res_size = len; + } + else + { + result = _gnutls_x509_data2hex (str, len, res, res_size); + if (result < 0) { - result = _gnutls_x509_data2hex (str, len, res, res_size); - if (result < 0) - { - gnutls_assert (); - return result; - } + gnutls_assert (); + return result; } } - } return 0; diff -Nur -x '*.orig' -x '*~' gnutls13-2.0.4/lib/x509/output.c gnutls13-2.0.4.new/lib/x509/output.c --- gnutls13-2.0.4/lib/x509/output.c 2007-11-15 09:21:47.000000000 -0600 +++ gnutls13-2.0.4.new/lib/x509/output.c 2009-08-14 15:12:06.710506308 -0500 @@ -271,6 +271,17 @@ return; } + if ((err == GNUTLS_SAN_DNSNAME + || err == GNUTLS_SAN_RFC822NAME + || err == GNUTLS_SAN_URI) && + strlen (buffer) != size) + { + adds (str, _("warning: distributionPoint contains an embedded NUL, " + "replacing with '!'\n")); + while (strlen (buffer) < size) + buffer[strlen (buffer)] = '!'; + } + switch (err) { case GNUTLS_SAN_DNSNAME: @@ -422,6 +433,17 @@ return; } + if ((err == GNUTLS_SAN_DNSNAME + || err == GNUTLS_SAN_RFC822NAME + || err == GNUTLS_SAN_URI) && + strlen (buffer) != size) + { + adds (str, _("warning: SAN contains an embedded NUL, " + "replacing with '!'\n")); + while (strlen (buffer) < size) + buffer[strlen (buffer)] = '!'; + } + switch (err) { case GNUTLS_SAN_DNSNAME: @@ -480,7 +502,17 @@ } if (err == GNUTLS_SAN_OTHERNAME_XMPP) - addf (str, _("\t\t\tXMPP Address: %.*s\n"), size, buffer); + { + if (strlen (buffer) != size) + { + adds (str, _("warning: SAN contains an embedded NUL, " + "replacing with '!'\n")); + while (strlen (buffer) < size) + buffer[strlen (buffer)] = '!'; + } + + addf (str, _("\t\t\tXMPP Address: %.*s\n"), size, buffer); + } else { addf (str, _("\t\t\totherName OID: %.*s\n"), oidsize, oid); diff -Nur -x '*.orig' -x '*~' gnutls13-2.0.4/lib/x509/rfc2818.h gnutls13-2.0.4.new/lib/x509/rfc2818.h --- gnutls13-2.0.4/lib/x509/rfc2818.h 2007-11-15 09:24:56.000000000 -0600 +++ gnutls13-2.0.4.new/lib/x509/rfc2818.h 2009-08-14 15:13:03.362007683 -0500 @@ -22,5 +22,5 @@ * */ -int _gnutls_hostname_compare (const char *certname, const char *hostname); +int _gnutls_hostname_compare (const char *certname, size_t certnamesize, const char *hostname); #define MAX_CN 256 diff -Nur -x '*.orig' -x '*~' gnutls13-2.0.4/lib/x509/rfc2818_hostname.c gnutls13-2.0.4.new/lib/x509/rfc2818_hostname.c --- gnutls13-2.0.4/lib/x509/rfc2818_hostname.c 2007-11-15 09:21:47.000000000 -0600 +++ gnutls13-2.0.4.new/lib/x509/rfc2818_hostname.c 2009-08-14 15:12:06.710506308 -0500 @@ -30,43 +30,45 @@ /* compare hostname against certificate, taking account of wildcards * return 1 on success or 0 on error + * + * note: certnamesize is required as X509 certs can contain embedded NULs in + * the strings such as CN or subjectAltName */ int -_gnutls_hostname_compare (const char *certname, const char *hostname) +_gnutls_hostname_compare (const char *certname, + size_t certnamesize, + const char *hostname) { - const char *cmpstr1, *cmpstr2; - - if (strlen (certname) == 0 || strlen (hostname) == 0) - return 0; - - if (strlen (certname) > 2 && strncmp (certname, "*.", 2) == 0) + /* find the first different character */ + for (; *certname && *hostname && toupper(*certname) == toupper(*hostname); certname++, hostname++, certnamesize--) + ; + + /* the strings are the same */ + if (certnamesize == 0 && *hostname == '\0') + return 1; + + if (*certname == '*') { /* a wildcard certificate */ - cmpstr1 = certname + 1; - - /* find the first dot in hostname, compare from there on */ - cmpstr2 = strchr (hostname, '.'); - - if (cmpstr2 == NULL) - { - /* error, the hostname we're connecting to is only a local part */ - return 0; - } + certname++; + certnamesize--; - if (strcasecmp (cmpstr1, cmpstr2) == 0) - { - return 1; + while (1) + { + if (_gnutls_hostname_compare (certname, certnamesize, hostname)) + return 1; + + /* wildcards are only allowed to match a single domain + component or component fragment */ + if (*hostname == '\0' || *hostname == '.') + break; + hostname++; } return 0; } - if (strcasecmp (certname, hostname) == 0) - { - return 1; - } - return 0; } @@ -118,7 +120,7 @@ if (ret == GNUTLS_SAN_DNSNAME) { found_dnsname = 1; - if (_gnutls_hostname_compare (dnsname, hostname)) + if (_gnutls_hostname_compare (dnsname, dnsnamesize, hostname)) { return 1; } @@ -128,7 +130,7 @@ found_dnsname = 1; /* RFC 2818 is unclear whether the CN should be compared for IP addresses too, but we won't do it. */ - if (_gnutls_hostname_compare (dnsname, hostname)) + if (_gnutls_hostname_compare (dnsname, dnsnamesize, hostname)) { return 1; } @@ -148,7 +150,7 @@ return 0; } - if (_gnutls_hostname_compare (dnsname, hostname)) + if (_gnutls_hostname_compare (dnsname, dnsnamesize, hostname)) { return 1; } diff -Nur -x '*.orig' -x '*~' gnutls13-2.0.4/libextra/openpgp/pgp.c gnutls13-2.0.4.new/libextra/openpgp/pgp.c --- gnutls13-2.0.4/libextra/openpgp/pgp.c 2007-11-15 09:24:56.000000000 -0600 +++ gnutls13-2.0.4.new/libextra/openpgp/pgp.c 2009-08-14 15:12:06.710506308 -0500 @@ -501,7 +501,7 @@ dnsnamesize = sizeof (dnsname); ret = gnutls_openpgp_key_get_name (key, i, dnsname, &dnsnamesize); /* FIXME: ret is not used */ - if (_gnutls_hostname_compare (dnsname, hostname)) + if (_gnutls_hostname_compare (dnsname, dnsnamesize, hostname)) return 1; } diff -Nur -x '*.orig' -x '*~' gnutls13-2.0.4/tests/hostname-check/hostname-check.c gnutls13-2.0.4.new/tests/hostname-check/hostname-check.c --- gnutls13-2.0.4/tests/hostname-check/hostname-check.c 2007-11-15 09:21:47.000000000 -0600 +++ gnutls13-2.0.4.new/tests/hostname-check/hostname-check.c 2009-08-14 15:12:06.710506308 -0500 @@ -336,6 +336,154 @@ "V4YbqvLUY2LG/bMEZGCcd8NL0eTHdwAXedIrHxSt6TTI2g==\n" "-----END CERTIFICATE-----\n"; +/* Certificate with multiple wildcards SAN but no CN. */ +char pem6[] = + "X.509 Certificate Information:\n" + " Version: 3\n" + " Serial Number (hex): 00\n" + " Validity:\n" + " Not Before: Sat May 3 11:00:51 UTC 2008\n" + " Not After: Sat May 17 11:00:54 UTC 2008\n" + " Subject: O=GnuTLS hostname check test CA\n" + " Subject Public Key Algorithm: RSA\n" + " Modulus (bits 1024):\n" + " d2:05:c1:65:cb:bd:1e:2e:eb:7b:87:07:94:4c:93:33\n" + " f3:81:83:7d:32:1b:71:4e:4e:7f:c7:bc:bf:4b:2f:f2\n" + " 49:b5:cf:bf:c0:b8:e8:29:cc:f3:61:bd:2e:1d:e4:e8\n" + " 19:dd:c5:bd:2e:f0:35:b1:fd:30:d7:f5:a8:7c:83:9a\n" + " 13:9e:bf:25:ed:08:a6:05:9e:7b:4e:23:59:c3:0e:5a\n" + " f3:bf:54:c7:dc:d4:13:57:a1:0f:a2:9e:c8:ab:75:66\n" + " de:07:84:8d:68:ad:71:04:e0:9c:bd:cb:f6:08:7a:97\n" + " 42:f8:10:94:29:01:4a:7e:61:d7:04:21:05:4c:f1:07\n" + " Exponent:\n" + " 01:00:01\n" + " Extensions:\n" + " Basic Constraints (critical):\n" + " Certificate Authority (CA): TRUE\n" + " Subject Alternative Name (not critical):\n" + " DNSname: *.*.example.org\n" + " Key Purpose (not critical):\n" + " TLS WWW Server.\n" + " Subject Key Identifier (not critical):\n" + " 5493e6599b283b4529378818aef9a4abbf4d9918\n" + "Other Information:\n" + " Public Key Id:\n" + " 5493e6599b283b4529378818aef9a4abbf4d9918\n" + "\n" + "-----BEGIN CERTIFICATE-----\n" + "MIICIjCCAY2gAwIBAgIBADALBgkqhkiG9w0BAQUwKDEmMCQGA1UEChMdR251VExT\n" + "IGhvc3RuYW1lIGNoZWNrIHRlc3QgQ0EwHhcNMDgwNTAzMTEwMDUxWhcNMDgwNTE3\n" + "MTEwMDU0WjAoMSYwJAYDVQQKEx1HbnVUTFMgaG9zdG5hbWUgY2hlY2sgdGVzdCBD\n" + "QTCBnDALBgkqhkiG9w0BAQEDgYwAMIGIAoGA0gXBZcu9Hi7re4cHlEyTM/OBg30y\n" + "G3FOTn/HvL9LL/JJtc+/wLjoKczzYb0uHeToGd3FvS7wNbH9MNf1qHyDmhOevyXt\n" + "CKYFnntOI1nDDlrzv1TH3NQTV6EPop7Iq3Vm3geEjWitcQTgnL3L9gh6l0L4EJQp\n" + "AUp+YdcEIQVM8QcCAwEAAaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAaBgNVHREEEzAR\n" + "gg8qLiouZXhhbXBsZS5vcmcwEwYDVR0lBAwwCgYIKwYBBQUHAwEwHQYDVR0OBBYE\n" + "FFST5lmbKDtFKTeIGK75pKu/TZkYMAsGCSqGSIb3DQEBBQOBgQAQ9PStleVvfmlK\n" + "wRs8RE/oOO+ouC3qLdnumNEITMRFh8Q12/X4yMLD3CH0aQ/hvHcP26PxAWzpNutk\n" + "swNx7AzsCu6pN1t1aI3jLgo8e4/zZi57e8QcRuXZPDJxtJxVhJZX/C4pSz802WhS\n" + "64NgtpHEMu9JUHFhtRwPcvVGYqPUUA==\n" + "-----END CERTIFICATE-----\n"; + +/* Certificate with prefixed and suffixed wildcard SAN but no CN. */ +char pem7[] = + "X.509 Certificate Information:\n" + " Version: 3\n" + " Serial Number (hex): 00\n" + " Validity:\n" + " Not Before: Sat May 3 11:02:43 UTC 2008\n" + " Not After: Sat May 17 11:02:45 UTC 2008\n" + " Subject: O=GnuTLS hostname check test CA\n" + " Subject Public Key Algorithm: RSA\n" + " Modulus (bits 1024):\n" + " d2:05:c1:65:cb:bd:1e:2e:eb:7b:87:07:94:4c:93:33\n" + " f3:81:83:7d:32:1b:71:4e:4e:7f:c7:bc:bf:4b:2f:f2\n" + " 49:b5:cf:bf:c0:b8:e8:29:cc:f3:61:bd:2e:1d:e4:e8\n" + " 19:dd:c5:bd:2e:f0:35:b1:fd:30:d7:f5:a8:7c:83:9a\n" + " 13:9e:bf:25:ed:08:a6:05:9e:7b:4e:23:59:c3:0e:5a\n" + " f3:bf:54:c7:dc:d4:13:57:a1:0f:a2:9e:c8:ab:75:66\n" + " de:07:84:8d:68:ad:71:04:e0:9c:bd:cb:f6:08:7a:97\n" + " 42:f8:10:94:29:01:4a:7e:61:d7:04:21:05:4c:f1:07\n" + " Exponent:\n" + " 01:00:01\n" + " Extensions:\n" + " Basic Constraints (critical):\n" + " Certificate Authority (CA): TRUE\n" + " Subject Alternative Name (not critical):\n" + " DNSname: foo*bar.example.org\n" + " Key Purpose (not critical):\n" + " TLS WWW Server.\n" + " Subject Key Identifier (not critical):\n" + " 5493e6599b283b4529378818aef9a4abbf4d9918\n" + "Other Information:\n" + " Public Key Id:\n" + " 5493e6599b283b4529378818aef9a4abbf4d9918\n" + "\n" + "-----BEGIN CERTIFICATE-----\n" + "MIICJjCCAZGgAwIBAgIBADALBgkqhkiG9w0BAQUwKDEmMCQGA1UEChMdR251VExT\n" + "IGhvc3RuYW1lIGNoZWNrIHRlc3QgQ0EwHhcNMDgwNTAzMTEwMjQzWhcNMDgwNTE3\n" + "MTEwMjQ1WjAoMSYwJAYDVQQKEx1HbnVUTFMgaG9zdG5hbWUgY2hlY2sgdGVzdCBD\n" + "QTCBnDALBgkqhkiG9w0BAQEDgYwAMIGIAoGA0gXBZcu9Hi7re4cHlEyTM/OBg30y\n" + "G3FOTn/HvL9LL/JJtc+/wLjoKczzYb0uHeToGd3FvS7wNbH9MNf1qHyDmhOevyXt\n" + "CKYFnntOI1nDDlrzv1TH3NQTV6EPop7Iq3Vm3geEjWitcQTgnL3L9gh6l0L4EJQp\n" + "AUp+YdcEIQVM8QcCAwEAAaNnMGUwDwYDVR0TAQH/BAUwAwEB/zAeBgNVHREEFzAV\n" + "ghNmb28qYmFyLmV4YW1wbGUub3JnMBMGA1UdJQQMMAoGCCsGAQUFBwMBMB0GA1Ud\n" + "DgQWBBRUk+ZZmyg7RSk3iBiu+aSrv02ZGDALBgkqhkiG9w0BAQUDgYEAPPNe38jc\n" + "8NsZQVKKLYc1Y4y8LRPhvnxkSnlcGa1RzYZY1s12BZ6OVIfyxD1Z9BcNdqRSq7bQ\n" + "kEicsGp5ugGQTNq6aSlzYOUD9/fUP3jDsH7HVb36aCF3waGCQWj+pLqK0LYcW2p/\n" + "xnr5+z4YevFBhn7l/fMhg8TzKejxYm7TECg=\n" + "-----END CERTIFICATE-----\n"; + +/* Certificate with ending wildcard SAN but no CN. */ +char pem8[] = + "X.509 Certificate Information:\n" + " Version: 3\n" + " Serial Number (hex): 00\n" + " Validity:\n" + " Not Before: Sat May 3 11:24:38 UTC 2008\n" + " Not After: Sat May 17 11:24:40 UTC 2008\n" + " Subject: O=GnuTLS hostname check test CA\n" + " Subject Public Key Algorithm: RSA\n" + " Modulus (bits 1024):\n" + " d2:05:c1:65:cb:bd:1e:2e:eb:7b:87:07:94:4c:93:33\n" + " f3:81:83:7d:32:1b:71:4e:4e:7f:c7:bc:bf:4b:2f:f2\n" + " 49:b5:cf:bf:c0:b8:e8:29:cc:f3:61:bd:2e:1d:e4:e8\n" + " 19:dd:c5:bd:2e:f0:35:b1:fd:30:d7:f5:a8:7c:83:9a\n" + " 13:9e:bf:25:ed:08:a6:05:9e:7b:4e:23:59:c3:0e:5a\n" + " f3:bf:54:c7:dc:d4:13:57:a1:0f:a2:9e:c8:ab:75:66\n" + " de:07:84:8d:68:ad:71:04:e0:9c:bd:cb:f6:08:7a:97\n" + " 42:f8:10:94:29:01:4a:7e:61:d7:04:21:05:4c:f1:07\n" + " Exponent:\n" + " 01:00:01\n" + " Extensions:\n" + " Basic Constraints (critical):\n" + " Certificate Authority (CA): TRUE\n" + " Subject Alternative Name (not critical):\n" + " DNSname: www.example.*\n" + " Key Purpose (not critical):\n" + " TLS WWW Server.\n" + " Subject Key Identifier (not critical):\n" + " 5493e6599b283b4529378818aef9a4abbf4d9918\n" + "Other Information:\n" + " Public Key Id:\n" + " 5493e6599b283b4529378818aef9a4abbf4d9918\n" + "\n" + "-----BEGIN CERTIFICATE-----\n" + "MIICIDCCAYugAwIBAgIBADALBgkqhkiG9w0BAQUwKDEmMCQGA1UEChMdR251VExT\n" + "IGhvc3RuYW1lIGNoZWNrIHRlc3QgQ0EwHhcNMDgwNTAzMTEyNDM4WhcNMDgwNTE3\n" + "MTEyNDQwWjAoMSYwJAYDVQQKEx1HbnVUTFMgaG9zdG5hbWUgY2hlY2sgdGVzdCBD\n" + "QTCBnDALBgkqhkiG9w0BAQEDgYwAMIGIAoGA0gXBZcu9Hi7re4cHlEyTM/OBg30y\n" + "G3FOTn/HvL9LL/JJtc+/wLjoKczzYb0uHeToGd3FvS7wNbH9MNf1qHyDmhOevyXt\n" + "CKYFnntOI1nDDlrzv1TH3NQTV6EPop7Iq3Vm3geEjWitcQTgnL3L9gh6l0L4EJQp\n" + "AUp+YdcEIQVM8QcCAwEAAaNhMF8wDwYDVR0TAQH/BAUwAwEB/zAYBgNVHREEETAP\n" + "gg13d3cuZXhhbXBsZS4qMBMGA1UdJQQMMAoGCCsGAQUFBwMBMB0GA1UdDgQWBBRU\n" + "k+ZZmyg7RSk3iBiu+aSrv02ZGDALBgkqhkiG9w0BAQUDgYEAZ7gLXtXwFW61dSAM\n" + "0Qt6IN68WBH7LCzetSF8ofG1WVUImCUU3pqXhXYtPGTrswOh2AavWTRbzVTtrFvf\n" + "WJg09Z7H6I70RPvAYGsK9t9qJ/4TPoYTGYQgsTbVpkv13O54O6jzemd8Zws/xMH5\n" + "7/q6C7P5OUmGOtfVe7UVDY0taQM=\n" + "-----END CERTIFICATE-----\n"; + + void doit (void) { @@ -475,6 +623,90 @@ else success ("Hostname correctly does not match (%d)\n", ret); + success ("Testing pem6...\n"); + data.data = pem6; + data.size = strlen (pem6); + + ret = gnutls_x509_crt_import (cert, &data, GNUTLS_X509_FMT_PEM); + if (ret < 0) + fail ("gnutls_x509_crt_import: %d\n", ret); + + ret = gnutls_x509_crt_check_hostname (cert, "foo.example.org"); + if (ret) + fail ("Hostname incorrectly matches (%d)\n", ret); + else + success ("Hostname correctly does not match (%d)\n", ret); + + ret = gnutls_x509_crt_check_hostname (cert, "bar.foo.example.org"); + if (ret) + success ("Hostname correctly matches (%d)\n", ret); + else + fail ("Hostname incorrectly does not match (%d)\n", ret); + + success ("Testing pem7...\n"); + data.data = pem7; + data.size = strlen (pem7); + + ret = gnutls_x509_crt_import (cert, &data, GNUTLS_X509_FMT_PEM); + if (ret < 0) + fail ("gnutls_x509_crt_import: %d\n", ret); + + ret = gnutls_x509_crt_check_hostname (cert, "foo.bar.example.org"); + if (ret) + fail ("Hostname incorrectly matches (%d)\n", ret); + else + success ("Hostname correctly does not match (%d)\n", ret); + + ret = gnutls_x509_crt_check_hostname (cert, "foobar.bar.example.org"); + if (ret) + fail ("Hostname incorrectly matches (%d)\n", ret); + else + success ("Hostname correctly does not match (%d)\n", ret); + + ret = gnutls_x509_crt_check_hostname (cert, "foobar.example.org"); + if (ret) + success ("Hostname correctly matches (%d)\n", ret); + else + fail ("Hostname incorrectly does not match (%d)\n", ret); + + ret = gnutls_x509_crt_check_hostname (cert, "foobazbar.example.org"); + if (ret) + success ("Hostname correctly matches (%d)\n", ret); + else + fail ("Hostname incorrectly does not match (%d)\n", ret); + + success ("Testing pem8...\n"); + data.data = pem8; + data.size = strlen (pem8); + + ret = gnutls_x509_crt_import (cert, &data, GNUTLS_X509_FMT_PEM); + if (ret < 0) + fail ("gnutls_x509_crt_import: %d\n", ret); + + ret = gnutls_x509_crt_check_hostname (cert, "www.example.org"); + if (ret) + success ("Hostname correctly matches (%d)\n", ret); + else + fail ("Hostname incorrectly does not match (%d)\n", ret); + + ret = gnutls_x509_crt_check_hostname (cert, "www.example."); + if (ret) + success ("Hostname correctly matches (%d)\n", ret); + else + fail ("Hostname incorrectly does not match (%d)\n", ret); + + ret = gnutls_x509_crt_check_hostname (cert, "www.example.com"); + if (ret) + success ("Hostname correctly matches (%d)\n", ret); + else + fail ("Hostname incorrectly does not match (%d)\n", ret); + + ret = gnutls_x509_crt_check_hostname (cert, "www.example.foo.com"); + if (ret) + fail ("Hostname incorrectly matches (%d)\n", ret); + else + success ("Hostname correctly does not match (%d)\n", ret); + gnutls_x509_crt_deinit (cert); gnutls_global_deinit ();