Dear maintainer, I've prepared an NMU for pyopenssl (versioned as 0.13-3.1) and uploaded it to DELAYED/5. Please feel free to tell me if I should delay it longer.
p.s.: this is the package targetting experimental. Regards, Salvatore
diff -Nru pyopenssl-0.13/debian/changelog pyopenssl-0.13/debian/changelog --- pyopenssl-0.13/debian/changelog 2012-08-29 22:45:16.000000000 +0200 +++ pyopenssl-0.13/debian/changelog 2013-09-15 22:11:44.000000000 +0200 @@ -1,3 +1,12 @@ +pyopenssl (0.13-3.1) experimental; urgency=low + + * Non-maintainer upload. + * Add CVE-2013-4314.patch patch. + CVE-2013-4314: Fix hostname check bypassing vulnerability with server + certificates that have a null byte in the subjectAltName. (Closes: #722055) + + -- Salvatore Bonaccorso <car...@debian.org> Sun, 15 Sep 2013 16:59:07 +0200 + pyopenssl (0.13-3) experimental; urgency=low * debian/{control, rules} diff -Nru pyopenssl-0.13/debian/patches/CVE-2013-4314.patch pyopenssl-0.13/debian/patches/CVE-2013-4314.patch --- pyopenssl-0.13/debian/patches/CVE-2013-4314.patch 1970-01-01 01:00:00.000000000 +0100 +++ pyopenssl-0.13/debian/patches/CVE-2013-4314.patch 2013-09-15 22:11:44.000000000 +0200 @@ -0,0 +1,222 @@ +Description: Fix hostname check bypassing vulnerability + Fix handling of NULL bytes inside subjectAltName general names when + formatting an X509 extension as a string. + . + When a CA than an SSL client trusts issues a server certificate that + has a null byte in the subjectAltName, remote attackers can obtain a + certifcate for 'www.foo.org\0.example.com' from the CA to spoof + 'www.foo.org' and conduct man-in-the-middle attacks between the + pyOpenSSL-using client and SSL servers. + . + Additionally fix memory leak in get_extension(). +Origin: upstream, http://bazaar.launchpad.net/~exarkun/pyopenssl/trunk/revision/169 +Bug-Debian: http://bugs.debian.org/722055 +Bug-RedHat: https://bugzilla.redhat.com/show_bug.cgi?id=1005325 +Forwarded: not-needed +Author: Salvatore Bonaccorso <car...@debian.org> +Last-Update: 2013-09-14 +Applied-Upstream: 0.13.1 + +--- a/OpenSSL/crypto/x509.c ++++ b/OpenSSL/crypto/x509.c +@@ -756,6 +756,7 @@ + + extobj = PyObject_New(crypto_X509ExtensionObj, &crypto_X509Extension_Type); + extobj->x509_extension = X509_EXTENSION_dup(ext); ++ extobj->dealloc = 1; + + return (PyObject*)extobj; + } +--- a/OpenSSL/crypto/x509ext.c ++++ b/OpenSSL/crypto/x509ext.c +@@ -236,19 +236,92 @@ + PyObject_Del(self); + } + ++ ++/* Special handling of subjectAltName. OpenSSL's builtin formatter, ++ * X509V3_EXT_print, mishandles NUL bytes allowing a truncated display that ++ * does not accurately reflect what's in the extension. ++ */ ++int ++crypto_X509Extension_str_subjectAltName(crypto_X509ExtensionObj *self, BIO *bio) { ++ GENERAL_NAMES *names; ++ const X509V3_EXT_METHOD *method = NULL; ++ long i, length, num; ++ const unsigned char *p; ++ ++ method = X509V3_EXT_get(self->x509_extension); ++ if (method == NULL) { ++ return -1; ++ } ++ ++ p = self->x509_extension->value->data; ++ length = self->x509_extension->value->length; ++ if (method->it) { ++ names = (GENERAL_NAMES*)(ASN1_item_d2i(NULL, &p, length, ++ ASN1_ITEM_ptr(method->it))); ++ } else { ++ names = (GENERAL_NAMES*)(method->d2i(NULL, &p, length)); ++ } ++ if (names == NULL) { ++ return -1; ++ } ++ ++ num = sk_GENERAL_NAME_num(names); ++ for (i = 0; i < num; i++) { ++ GENERAL_NAME *name; ++ ASN1_STRING *as; ++ name = sk_GENERAL_NAME_value(names, i); ++ switch (name->type) { ++ case GEN_EMAIL: ++ BIO_puts(bio, "email:"); ++ as = name->d.rfc822Name; ++ BIO_write(bio, ASN1_STRING_data(as), ++ ASN1_STRING_length(as)); ++ break; ++ case GEN_DNS: ++ BIO_puts(bio, "DNS:"); ++ as = name->d.dNSName; ++ BIO_write(bio, ASN1_STRING_data(as), ++ ASN1_STRING_length(as)); ++ break; ++ case GEN_URI: ++ BIO_puts(bio, "URI:"); ++ as = name->d.uniformResourceIdentifier; ++ BIO_write(bio, ASN1_STRING_data(as), ++ ASN1_STRING_length(as)); ++ break; ++ default: ++ /* use builtin print for GEN_OTHERNAME, GEN_X400, ++ * GEN_EDIPARTY, GEN_DIRNAME, GEN_IPADD and GEN_RID ++ */ ++ GENERAL_NAME_print(bio, name); ++ } ++ /* trailing ', ' except for last element */ ++ if (i < (num - 1)) { ++ BIO_puts(bio, ", "); ++ } ++ } ++ sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free); ++ ++ return 0; ++} ++ + /* + * Print a nice text representation of the certificate request. + */ + static PyObject * +-crypto_X509Extension_str(crypto_X509ExtensionObj *self) +-{ ++crypto_X509Extension_str(crypto_X509ExtensionObj *self) { + int str_len; + char *tmp_str; + PyObject *str; + BIO *bio = BIO_new(BIO_s_mem()); + +- if (!X509V3_EXT_print(bio, self->x509_extension, 0, 0)) +- { ++ if (OBJ_obj2nid(self->x509_extension->object) == NID_subject_alt_name) { ++ if (crypto_X509Extension_str_subjectAltName(self, bio) == -1) { ++ BIO_free(bio); ++ exception_from_error_queue(crypto_Error); ++ return NULL; ++ } ++ } else if (!X509V3_EXT_print(bio, self->x509_extension, 0, 0)) { + BIO_free(bio); + exception_from_error_queue(crypto_Error); + return NULL; +@@ -267,7 +340,7 @@ + "X509Extension", + sizeof(crypto_X509ExtensionObj), + 0, +- (destructor)crypto_X509Extension_dealloc, ++ (destructor)crypto_X509Extension_dealloc, + NULL, /* print */ + NULL, /* getattr */ + NULL, /* setattr (setattrfunc)crypto_X509Name_setattr, */ +--- a/OpenSSL/test/test_crypto.py ++++ b/OpenSSL/test/test_crypto.py +@@ -265,6 +265,37 @@ + -----END RSA PRIVATE KEY----- + """) + ++# certificate with NULL bytes in subjectAltName and common name ++ ++nulbyteSubjectAltNamePEM = b("""-----BEGIN CERTIFICATE----- ++MIIE2DCCA8CgAwIBAgIBADANBgkqhkiG9w0BAQUFADCBxTELMAkGA1UEBhMCVVMx ++DzANBgNVBAgMBk9yZWdvbjESMBAGA1UEBwwJQmVhdmVydG9uMSMwIQYDVQQKDBpQ ++eXRob24gU29mdHdhcmUgRm91bmRhdGlvbjEgMB4GA1UECwwXUHl0aG9uIENvcmUg ++RGV2ZWxvcG1lbnQxJDAiBgNVBAMMG251bGwucHl0aG9uLm9yZwBleGFtcGxlLm9y ++ZzEkMCIGCSqGSIb3DQEJARYVcHl0aG9uLWRldkBweXRob24ub3JnMB4XDTEzMDgw ++NzEzMTE1MloXDTEzMDgwNzEzMTI1MlowgcUxCzAJBgNVBAYTAlVTMQ8wDQYDVQQI ++DAZPcmVnb24xEjAQBgNVBAcMCUJlYXZlcnRvbjEjMCEGA1UECgwaUHl0aG9uIFNv ++ZnR3YXJlIEZvdW5kYXRpb24xIDAeBgNVBAsMF1B5dGhvbiBDb3JlIERldmVsb3Bt ++ZW50MSQwIgYDVQQDDBtudWxsLnB5dGhvbi5vcmcAZXhhbXBsZS5vcmcxJDAiBgkq ++hkiG9w0BCQEWFXB5dGhvbi1kZXZAcHl0aG9uLm9yZzCCASIwDQYJKoZIhvcNAQEB ++BQADggEPADCCAQoCggEBALXq7cn7Rn1vO3aA3TrzA5QLp6bb7B3f/yN0CJ2XFj+j ++pHs+Gw6WWSUDpybiiKnPec33BFawq3kyblnBMjBU61ioy5HwQqVkJ8vUVjGIUq3P ++vX/wBmQfzCe4o4uM89gpHyUL9UYGG8oCRa17dgqcv7u5rg0Wq2B1rgY+nHwx3JIv ++KRrgSwyRkGzpN8WQ1yrXlxWjgI9de0mPVDDUlywcWze1q2kwaEPTM3hLAmD1PESA ++oY/n8A/RXoeeRs9i/Pm/DGUS8ZPINXk/yOzsR/XvvkTVroIeLZqfmFpnZeF0cHzL ++08LODkVJJ9zjLdT7SA4vnne4FEbAxDbKAq5qkYzaL4UCAwEAAaOB0DCBzTAMBgNV ++HRMBAf8EAjAAMB0GA1UdDgQWBBSIWlXAUv9hzVKjNQ/qWpwkOCL3XDALBgNVHQ8E ++BAMCBeAwgZAGA1UdEQSBiDCBhYIeYWx0bnVsbC5weXRob24ub3JnAGV4YW1wbGUu ++Y29tgSBudWxsQHB5dGhvbi5vcmcAdXNlckBleGFtcGxlLm9yZ4YpaHR0cDovL251 ++bGwucHl0aG9uLm9yZwBodHRwOi8vZXhhbXBsZS5vcmeHBMAAAgGHECABDbgAAAAA ++AAAAAAAAAAEwDQYJKoZIhvcNAQEFBQADggEBAKxPRe99SaghcI6IWT7UNkJw9aO9 ++i9eo0Fj2MUqxpKbdb9noRDy2CnHWf7EIYZ1gznXPdwzSN4YCjV5d+Q9xtBaowT0j ++HPERs1ZuytCNNJTmhyqZ8q6uzMLoht4IqH/FBfpvgaeC5tBTnTT0rD5A/olXeimk ++kX4LxlEx5RAvpGB2zZVRGr6LobD9rVK91xuHYNIxxxfEGE8tCCWjp0+3ksri9SXx ++VHWBnbM9YaL32u3hxm8sYB/Yb8WSBavJCWJJqRStVRHM1koZlJmXNx2BX4vPo6iW ++RFEIPQsFZRLrtnCAiEhyT8bC2s/Njlu6ly9gtJZWSV46Q3ZjBL4q9sHKqZQ= ++-----END CERTIFICATE-----""") ++ + + class X509ExtTests(TestCase): + """ +@@ -856,6 +887,19 @@ + [(b("CN"), b("foo")), (b("OU"), b("bar"))]) + + ++ def test_load_nul_byte_attribute(self): ++ """ ++ An :py:class:`OpenSSL.crypto.X509Name` from an ++ :py:class:`OpenSSL.crypto.X509` instance loaded from a file can have a ++ NUL byte in the value of one of its attributes. ++ """ ++ cert = load_certificate(FILETYPE_PEM, nulbyteSubjectAltNamePEM) ++ subject = cert.get_subject() ++ self.assertEqual( ++ "null.python.org\x00example.org", subject.commonName) ++ ++ ++ + class _PKeyInteractionTestsMixin: + """ + Tests which involve another thing and a PKey. +@@ -1382,6 +1426,24 @@ + self.assertRaises(TypeError, cert.get_extension, "hello") + + ++ def test_nullbyte_subjectAltName(self): ++ """ ++ The fields of a `subjectAltName` extension on an X509 may contain NUL ++ bytes and this value is reflected in the string representation of the ++ extension object. ++ """ ++ cert = load_certificate(FILETYPE_PEM, nulbyteSubjectAltNamePEM) ++ ++ ext = cert.get_extension(3) ++ self.assertEqual(ext.get_short_name(), b('subjectAltName')) ++ self.assertEqual( ++ b("DNS:altnull.python.org\x00example.com, " ++ "email:n...@python.org\x00u...@example.org, " ++ "URI:http://null.python.org\x00http://example.org, " ++ "IP Address:192.0.2.1, IP Address:2001:DB8:0:0:0:0:0:1\n"), ++ b(str(ext))) ++ ++ + def test_invalid_digest_algorithm(self): + """ + L{X509.digest} raises L{ValueError} if called with an unrecognized hash diff -Nru pyopenssl-0.13/debian/patches/series pyopenssl-0.13/debian/patches/series --- pyopenssl-0.13/debian/patches/series 2011-08-15 18:28:49.000000000 +0200 +++ pyopenssl-0.13/debian/patches/series 2013-09-15 22:11:44.000000000 +0200 @@ -1,2 +1,3 @@ 10_fix_doc_buildsystem.patch disable_test_set_default_verify_paths.patch +CVE-2013-4314.patch