From f91f7250937c203d531f5daa3785b9e372c0d4ee Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Sat, 17 Sep 2016 19:48:23 +0200 Subject: [PATCH] Add RFC4985 SRVName to SAN --- Doc/library/ssl.rst | 6 +++ Doc/whatsnew/3.6.rst | 4 ++ Lib/test/allsans.pem | 59 ++++++++++++++-------------- Lib/test/make_ssl_certs.py | 1 + Lib/test/test_ssl.py | 7 +++- Modules/_ssl.c | 96 +++++++++++++++++++++++++++++++++++++++++++++- 6 files changed, 140 insertions(+), 33 deletions(-) diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst index 76003ea3938c2935991efc55d5c326f1726396cb..4bc544a5b9ac74bb794df45e2ca221fc0394b65a 100644 --- a/Doc/library/ssl.rst +++ b/Doc/library/ssl.rst @@ -1095,6 +1095,12 @@ SSL sockets also have the following additional methods and attributes: The returned dictionary includes additional X509v3 extension items such as ``crlDistributionPoints``, ``caIssuers`` and ``OCSP`` URIs. + .. versionchanged:: 3.6 + ``othername`` entries in *subjectAltName* are handled differently. Instead of + ``('othername', '')`` Python returns + ``('SRVName', 'name')`` for :rfc:`4985` service names and oid string / + DER bytes pairs for all remaining ``othername`` entries. + .. method:: SSLSocket.cipher() Returns a three-value tuple containing the name of the cipher being used, the diff --git a/Doc/whatsnew/3.6.rst b/Doc/whatsnew/3.6.rst index 941a5ebad52fa6205f75d034c6e960ef8571f902..70e00693533e3a68f8faa3e36ca4f2f92199dcbf 100644 --- a/Doc/whatsnew/3.6.rst +++ b/Doc/whatsnew/3.6.rst @@ -924,6 +924,10 @@ General resource ids (``GEN_RID``) in subject alternative name extensions no longer case a SystemError. (Contributed by Christian Heimes in :issue:`27691`.) +:func:`~ssl.SSLSocket.getpeercert` now returns :rfc:`4985` SRVName and +OID / DER bytes for remaining ``othername`` entries. +(Contributed by Christian Heimes in :issue:`28191`.) + subprocess ---------- diff --git a/Lib/test/allsans.pem b/Lib/test/allsans.pem index 3ee4f59513abfedd6d61b5115315924e528d8d30..d058312247019534301b4329666f17e12f0a7d80 100644 --- a/Lib/test/allsans.pem +++ b/Lib/test/allsans.pem @@ -1,37 +1,38 @@ -----BEGIN PRIVATE KEY----- -MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAOoy7/QOtTjQ0niE -6uDcTwtkC0R2Tvy1AjVnXohCntZfdzbTGDoYTgXSOLsP8A697jUiJ8VCePGH50xG -Z4DKnAF3a9O3a9nr2pLXb0iY3XOMv+YEBii7CfI+3oxFYgCl0sMgHzDD2ZTVYAsm -DWgLUVsE2gHEccRwrM2tPf2EgR+FAgMBAAECgYEA3qyfyYVSeTrTYxO93x6ZaVMu -A2IZp9zSxMQL9bKiI2GRj+cV2ebSCGbg2btFnD6qBor7FWsmYz+8g6FNN/9sY4az -61rMqMtQvLBe+7L8w70FeTze4qQ4Y1oQri0qD6tBWhDVlpnbI5Py9bkZKD67yVUk -elcEA/5x4PrYXkuqsAECQQD80NjT0mDvaY0JOOaQFSEpMv6QiUA8GGX8Xli7IoKb -tAolPG8rQBa+qSpcWfDMTrWw/aWHuMEEQoP/bVDH9W4FAkEA7SYQbBAKnojZ5A3G -kOHdV7aeivRQxQk/JN8Fb8oKB9Csvpv/BsuGxPKXHdhFa6CBTTsNRtHQw/szPo4l -xMIjgQJAPoMxqibR+0EBM6+TKzteSL6oPXsCnBl4Vk/J5vPgkbmR7KUl4+7j8N8J -b2554TrxKEN/w7CGYZRE6UrRd7ATNQJAWD7Yz41sli+wfPdPU2xo1BHljyl4wMk/ -EPZYbI/PCbdyAH/F935WyQTIjNeEhZc1Zkq6FwdOWw8ns3hrv3rKgQJAHXv1BqUa -czGPIFxX2TNoqtcl6/En4vrxVB1wzsfzkkDAg98kBl7qsF+S3qujSzKikjeaVbI2 -/CyWR2P3yLtOmA== +MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBANChX45+8n5CZpSP +iqg4d5NyaklUSK0kI5JCTsR9Ysum1IqNrH8xBHQu0qw7NmXbuTt0ywKPh6Jyf6b9 +l4UKQHyQR3/tUPlQwX76nLJgQifPKKpB/QsgFGNHLsJPOc9Ixj2AvsbIilQePhrb +mDGiYYkTvuLhUW6uDadsEyo07Tm/AgMBAAECgYA9x7uzR7ZpWyJjVcpanDYUrKn6 +wwGue+gotIf8uaCa6/E+gkuVcp9+BCuQowwtx44npCNFw9kHat6QRcIrvGhxsaeb +7U3wZJqQcLVLGoKT6SJK5FG86zmQrdh8lBsPGu2VZ2x9/rxTxQpddrUhHTDNvkOT +NnVnJKLhR/lPuud9sQJBAOr9BDlpHSNqF4Y+RarV5AZ0eIjjA0WbrEWAbUOJeQXu +VmewfLY2jb487PhI4hAMMIsairqpZ1/X7BK/3D+IwfcCQQDjSQLmT4vWB60qgIoT +H6UthVosETCCSFTEU7DBdDjl2aBak9SninjHr1x9vlsi0Rey0q2uh6msqHFgBMge +X9R5AkEA1s8F/ok1JruD16azPtCZysDLsq56vjXpS/tzZmqT5DUUbWLrDhJejknZ +gbn2VR8ffPl9Wdcj9XxHhHg4mSelzwJBAJSrCU6E8b8KPmbytV5U8MKZ26W/iK9v +iKLy60dfMmiGtbEJyavdk6tqNWG6xfo8MvgxfpIPAxmBBMf92LiljmECQAnOUpRx +RutoVwK57QdsTz1Lkpa0BYS5qmPOMSKDWAZoAFUNVz1BPPvlDd0XZCodAlHofP94 +bbynru2dYlHUJoc= -----END PRIVATE KEY----- -----BEGIN CERTIFICATE----- -MIIDcjCCAtugAwIBAgIJAN5dc9TOWjB7MA0GCSqGSIb3DQEBCwUAMF0xCzAJBgNV +MIIDmDCCAwGgAwIBAgIJAM4OS5Av5xG1MA0GCSqGSIb3DQEBCwUAMF0xCzAJBgNV BAYTAlhZMRcwFQYDVQQHDA5DYXN0bGUgQW50aHJheDEjMCEGA1UECgwaUHl0aG9u -IFNvZnR3YXJlIEZvdW5kYXRpb24xEDAOBgNVBAMMB2FsbHNhbnMwHhcNMTYwODA1 -MTAyMTExWhcNMjYwODAzMTAyMTExWjBdMQswCQYDVQQGEwJYWTEXMBUGA1UEBwwO +IFNvZnR3YXJlIEZvdW5kYXRpb24xEDAOBgNVBAMMB2FsbHNhbnMwHhcNMTYwOTE3 +MTczODUzWhcNMjYwOTE1MTczODUzWjBdMQswCQYDVQQGEwJYWTEXMBUGA1UEBwwO Q2FzdGxlIEFudGhyYXgxIzAhBgNVBAoMGlB5dGhvbiBTb2Z0d2FyZSBGb3VuZGF0 aW9uMRAwDgYDVQQDDAdhbGxzYW5zMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB -gQDqMu/0DrU40NJ4hOrg3E8LZAtEdk78tQI1Z16IQp7WX3c20xg6GE4F0ji7D/AO -ve41IifFQnjxh+dMRmeAypwBd2vTt2vZ69qS129ImN1zjL/mBAYouwnyPt6MRWIA -pdLDIB8ww9mU1WALJg1oC1FbBNoBxHHEcKzNrT39hIEfhQIDAQABo4IBODCCATQw -ggEwBgNVHREEggEnMIIBI4IHYWxsc2Fuc6AeBgMqAwSgFwwVc29tZSBvdGhlciBp +gQDQoV+OfvJ+QmaUj4qoOHeTcmpJVEitJCOSQk7EfWLLptSKjax/MQR0LtKsOzZl +27k7dMsCj4eicn+m/ZeFCkB8kEd/7VD5UMF++pyyYEInzyiqQf0LIBRjRy7CTznP +SMY9gL7GyIpUHj4a25gxomGJE77i4VFurg2nbBMqNO05vwIDAQABo4IBXjCCAVow +ggFWBgNVHREEggFNMIIBSYIHYWxsc2Fuc6AeBgMqAwSgFwwVc29tZSBvdGhlciBp ZGVudGlmaWVyoDUGBisGAQUCAqArMCmgEBsOS0VSQkVST1MuUkVBTE2hFTAToAMC -AQGhDDAKGwh1c2VybmFtZYEQdXNlckBleGFtcGxlLm9yZ4IPd3d3LmV4YW1wbGUu -b3JnpGcwZTELMAkGA1UEBhMCWFkxFzAVBgNVBAcMDkNhc3RsZSBBbnRocmF4MSMw -IQYDVQQKDBpQeXRob24gU29mdHdhcmUgRm91bmRhdGlvbjEYMBYGA1UEAwwPZGly -bmFtZSBleGFtcGxlhhdodHRwczovL3d3dy5weXRob24ub3JnL4cEfwAAAYcQAAAA -AAAAAAAAAAAAAAAAAYgEKgMEBTANBgkqhkiG9w0BAQsFAAOBgQAy16h+F+nOmeiT -VWR0fc8F/j6FcadbLseAUaogcC15OGxCl4UYpLV88HBkABOoGCpP155qwWTwOrdG -iYPGJSusf1OnJEbvzFejZf6u078bPd9/ZL4VWLjv+FPGkjd+N+/OaqMvgj8Lu99f -3Y/C4S7YbHxxwff6C6l2Xli+q6gnuQ== +AQGhDDAKGwh1c2VybmFtZaAkBggrBgEFBQcIB6AYFhZfaHR0cHMud3d3LmV4YW1w +bGUub3JngRB1c2VyQGV4YW1wbGUub3Jngg93d3cuZXhhbXBsZS5vcmekZzBlMQsw +CQYDVQQGEwJYWTEXMBUGA1UEBwwOQ2FzdGxlIEFudGhyYXgxIzAhBgNVBAoMGlB5 +dGhvbiBTb2Z0d2FyZSBGb3VuZGF0aW9uMRgwFgYDVQQDDA9kaXJuYW1lIGV4YW1w +bGWGF2h0dHBzOi8vd3d3LnB5dGhvbi5vcmcvhwR/AAABhxAAAAAAAAAAAAAAAAAA +AAABiAQqAwQFMA0GCSqGSIb3DQEBCwUAA4GBALrN0kgd9TMIOhO5DHJoHQ0coQYK +jkRNLrSkIcdhojX4EedON7Nm15y/7HL9sIoBaj+rynSF6mYuuXvr0eqpMnt1goCZ +xA/d9ZxbSrOKiMkf6eeLTwHS1DU1BHnNZgBPGHW9Nk97/ihBkEEzh++f7LlGTCku +RZetook9Zz4etJNE -----END CERTIFICATE----- diff --git a/Lib/test/make_ssl_certs.py b/Lib/test/make_ssl_certs.py index 4d9f01ba6a8e91388f79a68d8a8c125d06e9d525..6c2f18d7db4c9de6a0f9d5fefd0d1199d201e448 100644 --- a/Lib/test/make_ssl_certs.py +++ b/Lib/test/make_ssl_certs.py @@ -195,6 +195,7 @@ if __name__ == '__main__': extra_san = [ 'otherName.1 = 1.2.3.4;UTF8:some other identifier', 'otherName.2 = 1.3.6.1.5.2.2;SEQUENCE:princ_name', + 'otherName.3 = 1.3.6.1.5.5.7.8.7;IA5STRING:_https.www.example.org', 'email.1 = user@example.org', 'DNS.2 = www.example.org', # GEN_X400 diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index 46ec8223e8cf09380d9260e1e7d6c3b96f11ccc4..bd967fec9b329b32eca5be0524b37171f79201d8 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -314,8 +314,11 @@ class BasicSocketTests(unittest.TestCase): self.assertEqual(p['subjectAltName'], ( ('DNS', 'allsans'), - ('othername', ''), - ('othername', ''), + ('1.2.3.4', b'\x0c\x15some other identifier'), + ('1.3.6.1.5.2.2', + b'0)\xa0\x10\x1b\x0eKERBEROS.REALM\xa1\x150\x13\xa0\x03' + b'\x02\x01\x01\xa1\x0c0\n\x1b\x08username'), + ('SRVName', '_https.www.example.org'), ('email', 'user@example.org'), ('DNS', 'www.example.org'), ('DirName', diff --git a/Modules/_ssl.c b/Modules/_ssl.c index fc7a989a8d5dea48f4ce12a78123e244096d16d1..4417a3fdd3c16a5c53b31b7a7d0891837abe418a 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -320,6 +320,8 @@ static PyTypeObject PySSLContext_Type; static PyTypeObject PySSLSocket_Type; static PyTypeObject PySSLMemoryBIO_Type; static PyTypeObject PySSLSession_Type; +static int PySSL_NID_srvname = -1; +static const char* PySSL_OID_srvname = "1.3.6.1.5.5.7.8.7"; /*[clinic input] module _ssl @@ -970,6 +972,9 @@ _get_peer_alt_names (X509 *certificate) { /* get a rendering of each name in the set of names */ int gntype; ASN1_STRING *as = NULL; + OTHERNAME *other; + ASN1_TYPE *value; + int nid; name = sk_GENERAL_NAME_value(names, j); gntype = name->type; @@ -1063,15 +1068,72 @@ _get_peer_alt_names (X509 *certificate) { PyTuple_SET_ITEM(t, 1, v); break; + case GEN_OTHERNAME: + other = name->d.otherName; + value = other->value; + nid = OBJ_obj2nid(other->type_id); + + t = PyTuple_New(2); + if (t == NULL) + goto fail; + + if ((nid == PySSL_NID_srvname) && (value->type == V_ASN1_IA5STRING)) { + /* RFC 4985 dnsSRV aka SRVName */ + v = PyUnicode_FromString("SRVName"); + if (v == NULL) { + Py_DECREF(t); + goto fail; + } + PyTuple_SET_ITEM(t, 0, v); + v = PyUnicode_FromStringAndSize( + (char *)ASN1_STRING_data(value->value.ia5string), + ASN1_STRING_length(value->value.ia5string)); + if (v == NULL) { + Py_DECREF(t); + goto fail; + } + PyTuple_SET_ITEM(t, 1, v); + } else { + /* GENERAL_NAME_print() does not know how to handle other + * names. We convert data to ASN.1 DER and use the OID as key + * to give applications a better way to handle other names. + */ + int len; + unsigned char *der; + char oid[100]; + + len = OBJ_obj2txt(oid, sizeof(oid), other->type_id, 1); + if (len < 0) { + _setSSLError(NULL, 0, __FILE__, __LINE__); + Py_DECREF(t); + goto fail; + } + v = PyUnicode_FromStringAndSize(oid, len); + if (v == NULL) { + Py_DECREF(t); + goto fail; + } + PyTuple_SET_ITEM(t, 0, v); + + len = i2d_ASN1_TYPE(value, NULL); + v = PyBytes_FromStringAndSize(NULL, len); + if (v == NULL) { + Py_DECREF(t); + goto fail; + } + der = (unsigned char*)PyBytes_AS_STRING(v); + i2d_ASN1_TYPE(other->value, &der); + PyTuple_SET_ITEM(t, 1, v); + } + break; + default: /* for everything else, we use the OpenSSL print form */ switch (gntype) { /* check for new general name type */ - case GEN_OTHERNAME: case GEN_X400: case GEN_EDIPARTY: case GEN_IPADD: - case GEN_RID: break; default: if (PyErr_WarnFormat(PyExc_RuntimeWarning, 1, @@ -5102,6 +5164,32 @@ static int _setup_ssl_threads(void) { #endif /* HAVE_OPENSSL_CRYPTO_LOCK for WITH_THREAD && OpenSSL < 1.1.0 */ +/* OpenSSL lacks RFC4985 SRVName OID + */ +int +pyssl_nid_srvname(void) +{ + int nid; + + if (PySSL_NID_srvname != -1) { + return PySSL_NID_srvname; + } + + nid = OBJ_txt2nid(PySSL_OID_srvname); + if (nid != NID_undef) { + PySSL_NID_srvname = nid; + return nid; + } + nid = OBJ_create(PySSL_OID_srvname, "id-on-dnsSRV", "OtherName SRVName"); + if (nid == 0) { + _setSSLError(NULL, 0, __FILE__, __LINE__); + return -1; + } + PySSL_NID_srvname = nid; + return nid; +} + + PyDoc_STRVAR(module_doc, "Implementation module for SSL socket operations. See the socket module\n\ for documentation."); @@ -5231,6 +5319,10 @@ PyInit__ssl(void) (PyObject *)&PySSLSession_Type) != 0) return NULL; + if (pyssl_nid_srvname() == -1) { + return NULL; + } + PyModule_AddIntConstant(m, "SSL_ERROR_ZERO_RETURN", PY_SSL_ERROR_ZERO_RETURN); PyModule_AddIntConstant(m, "SSL_ERROR_WANT_READ", -- 2.7.4