mirror of
https://github.com/python/cpython.git
synced 2025-12-04 00:30:19 +00:00
bpo-18233: Add internal methods to access peer chain (GH-25467)
The internal `_ssl._SSLSocket` object now provides methods to retrieve the peer cert chain and verified cert chain as a list of Certificate objects. Certificate objects have methods to convert the cert to a dict, PEM, or DER (ASN.1). These are private APIs for now. There is a slim chance to stabilize the approach and provide a public API for 3.10. Otherwise I'll provide a stable API in 3.11. Signed-off-by: Christian Heimes <christian@python.org>
This commit is contained in:
parent
3c586ca500
commit
666991fc59
9 changed files with 563 additions and 6 deletions
|
|
@ -32,6 +32,7 @@ except ImportError:
|
||||||
ctypes = None
|
ctypes = None
|
||||||
|
|
||||||
ssl = import_helper.import_module("ssl")
|
ssl = import_helper.import_module("ssl")
|
||||||
|
import _ssl
|
||||||
|
|
||||||
from ssl import TLSVersion, _TLSContentType, _TLSMessageType, _TLSAlertType
|
from ssl import TLSVersion, _TLSContentType, _TLSMessageType, _TLSAlertType
|
||||||
|
|
||||||
|
|
@ -297,7 +298,7 @@ def test_wrap_socket(sock, *,
|
||||||
return context.wrap_socket(sock, **kwargs)
|
return context.wrap_socket(sock, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def testing_context(server_cert=SIGNED_CERTFILE):
|
def testing_context(server_cert=SIGNED_CERTFILE, *, server_chain=True):
|
||||||
"""Create context
|
"""Create context
|
||||||
|
|
||||||
client_context, server_context, hostname = testing_context()
|
client_context, server_context, hostname = testing_context()
|
||||||
|
|
@ -316,6 +317,7 @@ def testing_context(server_cert=SIGNED_CERTFILE):
|
||||||
|
|
||||||
server_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
|
server_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
|
||||||
server_context.load_cert_chain(server_cert)
|
server_context.load_cert_chain(server_cert)
|
||||||
|
if server_chain:
|
||||||
server_context.load_verify_locations(SIGNING_CA)
|
server_context.load_verify_locations(SIGNING_CA)
|
||||||
|
|
||||||
return client_context, server_context, hostname
|
return client_context, server_context, hostname
|
||||||
|
|
@ -2482,6 +2484,12 @@ class ThreadedEchoServer(threading.Thread):
|
||||||
elif stripped == b'GETCERT':
|
elif stripped == b'GETCERT':
|
||||||
cert = self.sslconn.getpeercert()
|
cert = self.sslconn.getpeercert()
|
||||||
self.write(repr(cert).encode("us-ascii") + b"\n")
|
self.write(repr(cert).encode("us-ascii") + b"\n")
|
||||||
|
elif stripped == b'VERIFIEDCHAIN':
|
||||||
|
certs = self.sslconn._sslobj.get_verified_chain()
|
||||||
|
self.write(len(certs).to_bytes(1, "big") + b"\n")
|
||||||
|
elif stripped == b'UNVERIFIEDCHAIN':
|
||||||
|
certs = self.sslconn._sslobj.get_unverified_chain()
|
||||||
|
self.write(len(certs).to_bytes(1, "big") + b"\n")
|
||||||
else:
|
else:
|
||||||
if (support.verbose and
|
if (support.verbose and
|
||||||
self.server.connectionchatty):
|
self.server.connectionchatty):
|
||||||
|
|
@ -4567,6 +4575,63 @@ class TestPostHandshakeAuth(unittest.TestCase):
|
||||||
# server cert has not been validated
|
# server cert has not been validated
|
||||||
self.assertEqual(s.getpeercert(), {})
|
self.assertEqual(s.getpeercert(), {})
|
||||||
|
|
||||||
|
def test_internal_chain_client(self):
|
||||||
|
client_context, server_context, hostname = testing_context(
|
||||||
|
server_chain=False
|
||||||
|
)
|
||||||
|
server = ThreadedEchoServer(context=server_context, chatty=False)
|
||||||
|
with server:
|
||||||
|
with client_context.wrap_socket(
|
||||||
|
socket.socket(),
|
||||||
|
server_hostname=hostname
|
||||||
|
) as s:
|
||||||
|
s.connect((HOST, server.port))
|
||||||
|
vc = s._sslobj.get_verified_chain()
|
||||||
|
self.assertEqual(len(vc), 2)
|
||||||
|
ee, ca = vc
|
||||||
|
uvc = s._sslobj.get_unverified_chain()
|
||||||
|
self.assertEqual(len(uvc), 1)
|
||||||
|
|
||||||
|
self.assertEqual(ee, uvc[0])
|
||||||
|
self.assertEqual(hash(ee), hash(uvc[0]))
|
||||||
|
self.assertEqual(repr(ee), repr(uvc[0]))
|
||||||
|
|
||||||
|
self.assertNotEqual(ee, ca)
|
||||||
|
self.assertNotEqual(hash(ee), hash(ca))
|
||||||
|
self.assertNotEqual(repr(ee), repr(ca))
|
||||||
|
self.assertNotEqual(ee.get_info(), ca.get_info())
|
||||||
|
self.assertIn("CN=localhost", repr(ee))
|
||||||
|
self.assertIn("CN=our-ca-server", repr(ca))
|
||||||
|
|
||||||
|
pem = ee.public_bytes(_ssl.ENCODING_PEM)
|
||||||
|
der = ee.public_bytes(_ssl.ENCODING_DER)
|
||||||
|
self.assertIsInstance(pem, str)
|
||||||
|
self.assertIn("-----BEGIN CERTIFICATE-----", pem)
|
||||||
|
self.assertIsInstance(der, bytes)
|
||||||
|
self.assertEqual(
|
||||||
|
ssl.PEM_cert_to_DER_cert(pem), der
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_internal_chain_server(self):
|
||||||
|
client_context, server_context, hostname = testing_context()
|
||||||
|
client_context.load_cert_chain(SIGNED_CERTFILE)
|
||||||
|
server_context.verify_mode = ssl.CERT_REQUIRED
|
||||||
|
server_context.maximum_version = ssl.TLSVersion.TLSv1_2
|
||||||
|
|
||||||
|
server = ThreadedEchoServer(context=server_context, chatty=False)
|
||||||
|
with server:
|
||||||
|
with client_context.wrap_socket(
|
||||||
|
socket.socket(),
|
||||||
|
server_hostname=hostname
|
||||||
|
) as s:
|
||||||
|
s.connect((HOST, server.port))
|
||||||
|
s.write(b'VERIFIEDCHAIN\n')
|
||||||
|
res = s.recv(1024)
|
||||||
|
self.assertEqual(res, b'\x02\n')
|
||||||
|
s.write(b'UNVERIFIEDCHAIN\n')
|
||||||
|
res = s.recv(1024)
|
||||||
|
self.assertEqual(res, b'\x02\n')
|
||||||
|
|
||||||
|
|
||||||
HAS_KEYLOG = hasattr(ssl.SSLContext, 'keylog_filename')
|
HAS_KEYLOG = hasattr(ssl.SSLContext, 'keylog_filename')
|
||||||
requires_keylog = unittest.skipUnless(
|
requires_keylog = unittest.skipUnless(
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
Certificate and PrivateKey classes were added to the ssl module.
|
||||||
|
Certificates and keys can now be loaded from memory buffer, too.
|
||||||
|
|
@ -1706,6 +1706,9 @@ _certificate_to_der(_sslmodulestate *state, X509 *certificate)
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#include "_ssl/misc.c"
|
||||||
|
#include "_ssl/cert.c"
|
||||||
|
|
||||||
/*[clinic input]
|
/*[clinic input]
|
||||||
_ssl._test_decode_cert
|
_ssl._test_decode_cert
|
||||||
path: object(converter="PyUnicode_FSConverter")
|
path: object(converter="PyUnicode_FSConverter")
|
||||||
|
|
@ -1798,6 +1801,70 @@ _ssl__SSLSocket_getpeercert_impl(PySSLSocket *self, int binary_mode)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*[clinic input]
|
||||||
|
_ssl._SSLSocket.get_verified_chain
|
||||||
|
|
||||||
|
[clinic start generated code]*/
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
_ssl__SSLSocket_get_verified_chain_impl(PySSLSocket *self)
|
||||||
|
/*[clinic end generated code: output=802421163cdc3110 input=5fb0714f77e2bd51]*/
|
||||||
|
{
|
||||||
|
/* borrowed reference */
|
||||||
|
STACK_OF(X509) *chain = SSL_get0_verified_chain(self->ssl);
|
||||||
|
if (chain == NULL) {
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
return _PySSL_CertificateFromX509Stack(self->ctx->state, chain, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*[clinic input]
|
||||||
|
_ssl._SSLSocket.get_unverified_chain
|
||||||
|
|
||||||
|
[clinic start generated code]*/
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
_ssl__SSLSocket_get_unverified_chain_impl(PySSLSocket *self)
|
||||||
|
/*[clinic end generated code: output=5acdae414e13f913 input=78c33c360c635cb5]*/
|
||||||
|
{
|
||||||
|
PyObject *retval;
|
||||||
|
/* borrowed reference */
|
||||||
|
/* TODO: include SSL_get_peer_certificate() for server-side sockets */
|
||||||
|
STACK_OF(X509) *chain = SSL_get_peer_cert_chain(self->ssl);
|
||||||
|
if (chain == NULL) {
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
retval = _PySSL_CertificateFromX509Stack(self->ctx->state, chain, 1);
|
||||||
|
if (retval == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
/* OpenSSL does not include peer cert for server side connections */
|
||||||
|
if (self->socket_type == PY_SSL_SERVER) {
|
||||||
|
PyObject *peerobj = NULL;
|
||||||
|
X509 *peer = SSL_get_peer_certificate(self->ssl);
|
||||||
|
|
||||||
|
if (peer == NULL) {
|
||||||
|
peerobj = Py_None;
|
||||||
|
Py_INCREF(peerobj);
|
||||||
|
} else {
|
||||||
|
/* consume X509 reference on success */
|
||||||
|
peerobj = _PySSL_CertificateFromX509(self->ctx->state, peer, 0);
|
||||||
|
if (peerobj == NULL) {
|
||||||
|
X509_free(peer);
|
||||||
|
Py_DECREF(retval);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int res = PyList_Insert(retval, 0, peerobj);
|
||||||
|
Py_DECREF(peerobj);
|
||||||
|
if (res < 0) {
|
||||||
|
Py_DECREF(retval);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
cipher_to_tuple(const SSL_CIPHER *cipher)
|
cipher_to_tuple(const SSL_CIPHER *cipher)
|
||||||
{
|
{
|
||||||
|
|
@ -2809,6 +2876,8 @@ static PyMethodDef PySSLMethods[] = {
|
||||||
_SSL__SSLSOCKET_COMPRESSION_METHODDEF
|
_SSL__SSLSOCKET_COMPRESSION_METHODDEF
|
||||||
_SSL__SSLSOCKET_SHUTDOWN_METHODDEF
|
_SSL__SSLSOCKET_SHUTDOWN_METHODDEF
|
||||||
_SSL__SSLSOCKET_VERIFY_CLIENT_POST_HANDSHAKE_METHODDEF
|
_SSL__SSLSOCKET_VERIFY_CLIENT_POST_HANDSHAKE_METHODDEF
|
||||||
|
_SSL__SSLSOCKET_GET_UNVERIFIED_CHAIN_METHODDEF
|
||||||
|
_SSL__SSLSOCKET_GET_VERIFIED_CHAIN_METHODDEF
|
||||||
{NULL, NULL}
|
{NULL, NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -5784,6 +5853,10 @@ sslmodule_init_constants(PyObject *m)
|
||||||
X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS);
|
X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* file types */
|
||||||
|
PyModule_AddIntConstant(m, "ENCODING_PEM", PY_SSL_ENCODING_PEM);
|
||||||
|
PyModule_AddIntConstant(m, "ENCODING_DER", PY_SSL_ENCODING_DER);
|
||||||
|
|
||||||
/* protocol versions */
|
/* protocol versions */
|
||||||
PyModule_AddIntConstant(m, "PROTO_MINIMUM_SUPPORTED",
|
PyModule_AddIntConstant(m, "PROTO_MINIMUM_SUPPORTED",
|
||||||
PY_PROTO_MINIMUM_SUPPORTED);
|
PY_PROTO_MINIMUM_SUPPORTED);
|
||||||
|
|
@ -5986,6 +6059,12 @@ sslmodule_init_types(PyObject *module)
|
||||||
if (state->PySSLSession_Type == NULL)
|
if (state->PySSLSession_Type == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
state->PySSLCertificate_Type = (PyTypeObject *)PyType_FromModuleAndSpec(
|
||||||
|
module, &PySSLCertificate_spec, NULL
|
||||||
|
);
|
||||||
|
if (state->PySSLCertificate_Type == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
if (PyModule_AddType(module, state->PySSLContext_Type))
|
if (PyModule_AddType(module, state->PySSLContext_Type))
|
||||||
return -1;
|
return -1;
|
||||||
if (PyModule_AddType(module, state->PySSLSocket_Type))
|
if (PyModule_AddType(module, state->PySSLSocket_Type))
|
||||||
|
|
@ -5994,7 +6073,8 @@ sslmodule_init_types(PyObject *module)
|
||||||
return -1;
|
return -1;
|
||||||
if (PyModule_AddType(module, state->PySSLSession_Type))
|
if (PyModule_AddType(module, state->PySSLSession_Type))
|
||||||
return -1;
|
return -1;
|
||||||
|
if (PyModule_AddType(module, state->PySSLCertificate_Type))
|
||||||
|
return -1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -6017,6 +6097,7 @@ sslmodule_traverse(PyObject *m, visitproc visit, void *arg)
|
||||||
Py_VISIT(state->PySSLSocket_Type);
|
Py_VISIT(state->PySSLSocket_Type);
|
||||||
Py_VISIT(state->PySSLMemoryBIO_Type);
|
Py_VISIT(state->PySSLMemoryBIO_Type);
|
||||||
Py_VISIT(state->PySSLSession_Type);
|
Py_VISIT(state->PySSLSession_Type);
|
||||||
|
Py_VISIT(state->PySSLCertificate_Type);
|
||||||
Py_VISIT(state->PySSLErrorObject);
|
Py_VISIT(state->PySSLErrorObject);
|
||||||
Py_VISIT(state->PySSLCertVerificationErrorObject);
|
Py_VISIT(state->PySSLCertVerificationErrorObject);
|
||||||
Py_VISIT(state->PySSLZeroReturnErrorObject);
|
Py_VISIT(state->PySSLZeroReturnErrorObject);
|
||||||
|
|
@ -6041,6 +6122,7 @@ sslmodule_clear(PyObject *m)
|
||||||
Py_CLEAR(state->PySSLSocket_Type);
|
Py_CLEAR(state->PySSLSocket_Type);
|
||||||
Py_CLEAR(state->PySSLMemoryBIO_Type);
|
Py_CLEAR(state->PySSLMemoryBIO_Type);
|
||||||
Py_CLEAR(state->PySSLSession_Type);
|
Py_CLEAR(state->PySSLSession_Type);
|
||||||
|
Py_CLEAR(state->PySSLCertificate_Type);
|
||||||
Py_CLEAR(state->PySSLErrorObject);
|
Py_CLEAR(state->PySSLErrorObject);
|
||||||
Py_CLEAR(state->PySSLCertVerificationErrorObject);
|
Py_CLEAR(state->PySSLCertVerificationErrorObject);
|
||||||
Py_CLEAR(state->PySSLZeroReturnErrorObject);
|
Py_CLEAR(state->PySSLZeroReturnErrorObject);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
#ifndef Py_SSL_H
|
#ifndef Py_SSL_H
|
||||||
#define Py_SSL_H
|
#define Py_SSL_H
|
||||||
|
|
||||||
|
/* OpenSSL header files */
|
||||||
|
#include "openssl/evp.h"
|
||||||
|
#include "openssl/x509.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ssl module state
|
* ssl module state
|
||||||
*/
|
*/
|
||||||
|
|
@ -10,6 +14,7 @@ typedef struct {
|
||||||
PyTypeObject *PySSLSocket_Type;
|
PyTypeObject *PySSLSocket_Type;
|
||||||
PyTypeObject *PySSLMemoryBIO_Type;
|
PyTypeObject *PySSLMemoryBIO_Type;
|
||||||
PyTypeObject *PySSLSession_Type;
|
PyTypeObject *PySSLSession_Type;
|
||||||
|
PyTypeObject *PySSLCertificate_Type;
|
||||||
/* SSL error object */
|
/* SSL error object */
|
||||||
PyObject *PySSLErrorObject;
|
PyObject *PySSLErrorObject;
|
||||||
PyObject *PySSLCertVerificationErrorObject;
|
PyObject *PySSLCertVerificationErrorObject;
|
||||||
|
|
@ -40,6 +45,30 @@ get_ssl_state(PyObject *module)
|
||||||
(get_ssl_state(_PyType_GetModuleByDef(type, &_sslmodule_def)))
|
(get_ssl_state(_PyType_GetModuleByDef(type, &_sslmodule_def)))
|
||||||
#define get_state_ctx(c) (((PySSLContext *)(c))->state)
|
#define get_state_ctx(c) (((PySSLContext *)(c))->state)
|
||||||
#define get_state_sock(s) (((PySSLSocket *)(s))->ctx->state)
|
#define get_state_sock(s) (((PySSLSocket *)(s))->ctx->state)
|
||||||
#define get_state_mbio(b) ((_sslmodulestate *)PyType_GetModuleState(Py_TYPE(b)))
|
#define get_state_obj(o) ((_sslmodulestate *)PyType_GetModuleState(Py_TYPE(o)))
|
||||||
|
#define get_state_mbio(b) get_state_obj(b)
|
||||||
|
#define get_state_cert(c) get_state_obj(c)
|
||||||
|
|
||||||
|
/* ************************************************************************
|
||||||
|
* certificate
|
||||||
|
*/
|
||||||
|
|
||||||
|
enum py_ssl_encoding {
|
||||||
|
PY_SSL_ENCODING_PEM=X509_FILETYPE_PEM,
|
||||||
|
PY_SSL_ENCODING_DER=X509_FILETYPE_ASN1,
|
||||||
|
PY_SSL_ENCODING_PEM_AUX=X509_FILETYPE_PEM + 0x100,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
PyObject_HEAD
|
||||||
|
X509 *cert;
|
||||||
|
Py_hash_t hash;
|
||||||
|
} PySSLCertificate;
|
||||||
|
|
||||||
|
/* ************************************************************************
|
||||||
|
* helpers and utils
|
||||||
|
*/
|
||||||
|
static PyObject *_PySSL_BytesFromBIO(_sslmodulestate *state, BIO *bio);
|
||||||
|
static PyObject *_PySSL_UnicodeFromBIO(_sslmodulestate *state, BIO *bio, const char *error);
|
||||||
|
|
||||||
#endif /* Py_SSL_H */
|
#endif /* Py_SSL_H */
|
||||||
|
|
|
||||||
245
Modules/_ssl/cert.c
Normal file
245
Modules/_ssl/cert.c
Normal file
|
|
@ -0,0 +1,245 @@
|
||||||
|
#include "Python.h"
|
||||||
|
#include "../_ssl.h"
|
||||||
|
|
||||||
|
#include "openssl/err.h"
|
||||||
|
#include "openssl/bio.h"
|
||||||
|
#include "openssl/pem.h"
|
||||||
|
#include "openssl/x509.h"
|
||||||
|
|
||||||
|
/*[clinic input]
|
||||||
|
module _ssl
|
||||||
|
class _ssl.Certificate "PySSLCertificate *" "PySSLCertificate_Type"
|
||||||
|
[clinic start generated code]*/
|
||||||
|
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=780fc647948cfffc]*/
|
||||||
|
|
||||||
|
#include "clinic/cert.c.h"
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
newCertificate(PyTypeObject *type, X509 *cert, int upref)
|
||||||
|
{
|
||||||
|
PySSLCertificate *self;
|
||||||
|
|
||||||
|
assert(type != NULL && type->tp_alloc != NULL);
|
||||||
|
assert(cert != NULL);
|
||||||
|
|
||||||
|
self = (PySSLCertificate *) type->tp_alloc(type, 0);
|
||||||
|
if (self == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (upref == 1) {
|
||||||
|
X509_up_ref(cert);
|
||||||
|
}
|
||||||
|
self->cert = cert;
|
||||||
|
self->hash = -1;
|
||||||
|
|
||||||
|
return (PyObject *) self;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
_PySSL_CertificateFromX509(_sslmodulestate *state, X509 *cert, int upref)
|
||||||
|
{
|
||||||
|
return newCertificate(state->PySSLCertificate_Type, cert, upref);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
_PySSL_CertificateFromX509Stack(_sslmodulestate *state, STACK_OF(X509) *stack, int upref)
|
||||||
|
{
|
||||||
|
int len, i;
|
||||||
|
PyObject *result = NULL;
|
||||||
|
|
||||||
|
len = sk_X509_num(stack);
|
||||||
|
result = PyList_New(len);
|
||||||
|
if (result == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
X509 *cert = sk_X509_value(stack, i);
|
||||||
|
PyObject *ocert = _PySSL_CertificateFromX509(state, cert, upref);
|
||||||
|
if (ocert == NULL) {
|
||||||
|
Py_DECREF(result);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
PyList_SetItem(result, i, ocert);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*[clinic input]
|
||||||
|
_ssl.Certificate.public_bytes
|
||||||
|
format: int(c_default="PY_SSL_ENCODING_PEM") = Encoding.PEM
|
||||||
|
|
||||||
|
[clinic start generated code]*/
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
_ssl_Certificate_public_bytes_impl(PySSLCertificate *self, int format)
|
||||||
|
/*[clinic end generated code: output=c01ddbb697429e12 input=4d38c45e874b0e64]*/
|
||||||
|
{
|
||||||
|
BIO *bio;
|
||||||
|
int retcode;
|
||||||
|
PyObject *result;
|
||||||
|
_sslmodulestate *state = get_state_cert(self);
|
||||||
|
|
||||||
|
bio = BIO_new(BIO_s_mem());
|
||||||
|
if (bio == NULL) {
|
||||||
|
PyErr_SetString(state->PySSLErrorObject,
|
||||||
|
"failed to allocate BIO");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
switch(format) {
|
||||||
|
case PY_SSL_ENCODING_PEM:
|
||||||
|
retcode = PEM_write_bio_X509(bio, self->cert);
|
||||||
|
break;
|
||||||
|
case PY_SSL_ENCODING_PEM_AUX:
|
||||||
|
retcode = PEM_write_bio_X509_AUX(bio, self->cert);
|
||||||
|
break;
|
||||||
|
case PY_SSL_ENCODING_DER:
|
||||||
|
retcode = i2d_X509_bio(bio, self->cert);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
PyErr_SetString(PyExc_ValueError, "Unsupported format");
|
||||||
|
BIO_free(bio);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (retcode != 1) {
|
||||||
|
BIO_free(bio);
|
||||||
|
_setSSLError(state, NULL, 0, __FILE__, __LINE__);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (format == PY_SSL_ENCODING_DER) {
|
||||||
|
result = _PySSL_BytesFromBIO(state, bio);
|
||||||
|
} else {
|
||||||
|
result = _PySSL_UnicodeFromBIO(state, bio, "error");
|
||||||
|
}
|
||||||
|
BIO_free(bio);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*[clinic input]
|
||||||
|
_ssl.Certificate.get_info
|
||||||
|
|
||||||
|
[clinic start generated code]*/
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
_ssl_Certificate_get_info_impl(PySSLCertificate *self)
|
||||||
|
/*[clinic end generated code: output=0f0deaac54f4408b input=ba2c1694b39d0778]*/
|
||||||
|
{
|
||||||
|
return _decode_certificate(get_state_cert(self), self->cert);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
_x509name_print(_sslmodulestate *state, X509_NAME *name, int indent, unsigned long flags)
|
||||||
|
{
|
||||||
|
PyObject *res;
|
||||||
|
BIO *biobuf;
|
||||||
|
|
||||||
|
biobuf = BIO_new(BIO_s_mem());
|
||||||
|
if (biobuf == NULL) {
|
||||||
|
PyErr_SetString(PyExc_MemoryError, "failed to allocate BIO");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (X509_NAME_print_ex(biobuf, name, indent, flags) <= 0) {
|
||||||
|
_setSSLError(state, NULL, 0, __FILE__, __LINE__);
|
||||||
|
BIO_free(biobuf);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
res = _PySSL_UnicodeFromBIO(state, biobuf, "strict");
|
||||||
|
BIO_free(biobuf);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ************************************************************************
|
||||||
|
* PySSLCertificate_Type
|
||||||
|
*/
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
certificate_repr(PySSLCertificate *self)
|
||||||
|
{
|
||||||
|
PyObject *osubject, *result;
|
||||||
|
|
||||||
|
/* subject string is ASCII encoded, UTF-8 chars are quoted */
|
||||||
|
osubject = _x509name_print(
|
||||||
|
get_state_cert(self),
|
||||||
|
X509_get_subject_name(self->cert),
|
||||||
|
0,
|
||||||
|
XN_FLAG_RFC2253
|
||||||
|
);
|
||||||
|
if (osubject == NULL)
|
||||||
|
return NULL;
|
||||||
|
result = PyUnicode_FromFormat(
|
||||||
|
"<%s '%U'>",
|
||||||
|
Py_TYPE(self)->tp_name, osubject
|
||||||
|
);
|
||||||
|
Py_DECREF(osubject);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Py_hash_t
|
||||||
|
certificate_hash(PySSLCertificate *self)
|
||||||
|
{
|
||||||
|
if (self->hash == (Py_hash_t)-1) {
|
||||||
|
unsigned long hash;
|
||||||
|
hash = X509_subject_name_hash(self->cert);
|
||||||
|
if ((Py_hash_t)hash == (Py_hash_t)-1) {
|
||||||
|
self->hash = -2;
|
||||||
|
} else {
|
||||||
|
self->hash = (Py_hash_t)hash;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return self->hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
certificate_richcompare(PySSLCertificate *self, PyObject *other, int op)
|
||||||
|
{
|
||||||
|
int cmp;
|
||||||
|
_sslmodulestate *state = get_state_cert(self);
|
||||||
|
|
||||||
|
if (Py_TYPE(other) != state->PySSLCertificate_Type) {
|
||||||
|
Py_RETURN_NOTIMPLEMENTED;
|
||||||
|
}
|
||||||
|
/* only support == and != */
|
||||||
|
if ((op != Py_EQ) && (op != Py_NE)) {
|
||||||
|
Py_RETURN_NOTIMPLEMENTED;
|
||||||
|
}
|
||||||
|
cmp = X509_cmp(self->cert, ((PySSLCertificate*)other)->cert);
|
||||||
|
if (((op == Py_EQ) && (cmp == 0)) || ((op == Py_NE) && (cmp != 0))) {
|
||||||
|
Py_RETURN_TRUE;
|
||||||
|
} else {
|
||||||
|
Py_RETURN_FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
certificate_dealloc(PySSLCertificate *self)
|
||||||
|
{
|
||||||
|
PyTypeObject *tp = Py_TYPE(self);
|
||||||
|
X509_free(self->cert);
|
||||||
|
Py_TYPE(self)->tp_free(self);
|
||||||
|
Py_DECREF(tp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyMethodDef certificate_methods[] = {
|
||||||
|
/* methods */
|
||||||
|
_SSL_CERTIFICATE_PUBLIC_BYTES_METHODDEF
|
||||||
|
_SSL_CERTIFICATE_GET_INFO_METHODDEF
|
||||||
|
{NULL, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
static PyType_Slot PySSLCertificate_slots[] = {
|
||||||
|
{Py_tp_dealloc, certificate_dealloc},
|
||||||
|
{Py_tp_repr, certificate_repr},
|
||||||
|
{Py_tp_hash, certificate_hash},
|
||||||
|
{Py_tp_richcompare, certificate_richcompare},
|
||||||
|
{Py_tp_methods, certificate_methods},
|
||||||
|
{0, 0},
|
||||||
|
};
|
||||||
|
|
||||||
|
static PyType_Spec PySSLCertificate_spec = {
|
||||||
|
"_ssl.Certificate",
|
||||||
|
sizeof(PySSLCertificate),
|
||||||
|
0,
|
||||||
|
Py_TPFLAGS_DEFAULT,
|
||||||
|
PySSLCertificate_slots,
|
||||||
|
};
|
||||||
60
Modules/_ssl/clinic/cert.c.h
Normal file
60
Modules/_ssl/clinic/cert.c.h
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
/*[clinic input]
|
||||||
|
preserve
|
||||||
|
[clinic start generated code]*/
|
||||||
|
|
||||||
|
PyDoc_STRVAR(_ssl_Certificate_public_bytes__doc__,
|
||||||
|
"public_bytes($self, /, format=Encoding.PEM)\n"
|
||||||
|
"--\n"
|
||||||
|
"\n");
|
||||||
|
|
||||||
|
#define _SSL_CERTIFICATE_PUBLIC_BYTES_METHODDEF \
|
||||||
|
{"public_bytes", (PyCFunction)(void(*)(void))_ssl_Certificate_public_bytes, METH_FASTCALL|METH_KEYWORDS, _ssl_Certificate_public_bytes__doc__},
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
_ssl_Certificate_public_bytes_impl(PySSLCertificate *self, int format);
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
_ssl_Certificate_public_bytes(PySSLCertificate *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
|
||||||
|
{
|
||||||
|
PyObject *return_value = NULL;
|
||||||
|
static const char * const _keywords[] = {"format", NULL};
|
||||||
|
static _PyArg_Parser _parser = {NULL, _keywords, "public_bytes", 0};
|
||||||
|
PyObject *argsbuf[1];
|
||||||
|
Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0;
|
||||||
|
int format = PY_SSL_ENCODING_PEM;
|
||||||
|
|
||||||
|
args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf);
|
||||||
|
if (!args) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
if (!noptargs) {
|
||||||
|
goto skip_optional_pos;
|
||||||
|
}
|
||||||
|
format = _PyLong_AsInt(args[0]);
|
||||||
|
if (format == -1 && PyErr_Occurred()) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
skip_optional_pos:
|
||||||
|
return_value = _ssl_Certificate_public_bytes_impl(self, format);
|
||||||
|
|
||||||
|
exit:
|
||||||
|
return return_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyDoc_STRVAR(_ssl_Certificate_get_info__doc__,
|
||||||
|
"get_info($self, /)\n"
|
||||||
|
"--\n"
|
||||||
|
"\n");
|
||||||
|
|
||||||
|
#define _SSL_CERTIFICATE_GET_INFO_METHODDEF \
|
||||||
|
{"get_info", (PyCFunction)_ssl_Certificate_get_info, METH_NOARGS, _ssl_Certificate_get_info__doc__},
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
_ssl_Certificate_get_info_impl(PySSLCertificate *self);
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
_ssl_Certificate_get_info(PySSLCertificate *self, PyObject *Py_UNUSED(ignored))
|
||||||
|
{
|
||||||
|
return _ssl_Certificate_get_info_impl(self);
|
||||||
|
}
|
||||||
|
/*[clinic end generated code: output=569d161749ead2da input=a9049054013a1b77]*/
|
||||||
34
Modules/_ssl/misc.c
Normal file
34
Modules/_ssl/misc.c
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
#include "Python.h"
|
||||||
|
#include "../_ssl.h"
|
||||||
|
|
||||||
|
#include "openssl/bio.h"
|
||||||
|
|
||||||
|
/* BIO_s_mem() to PyBytes
|
||||||
|
*/
|
||||||
|
static PyObject *
|
||||||
|
_PySSL_BytesFromBIO(_sslmodulestate *state, BIO *bio)
|
||||||
|
{
|
||||||
|
long size;
|
||||||
|
char *data = NULL;
|
||||||
|
size = BIO_get_mem_data(bio, &data);
|
||||||
|
if (data == NULL || size < 0) {
|
||||||
|
PyErr_SetString(PyExc_ValueError, "Not a memory BIO");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return PyBytes_FromStringAndSize(data, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* BIO_s_mem() to PyUnicode
|
||||||
|
*/
|
||||||
|
static PyObject *
|
||||||
|
_PySSL_UnicodeFromBIO(_sslmodulestate *state, BIO *bio, const char *error)
|
||||||
|
{
|
||||||
|
long size;
|
||||||
|
char *data = NULL;
|
||||||
|
size = BIO_get_mem_data(bio, &data);
|
||||||
|
if (data == NULL || size < 0) {
|
||||||
|
PyErr_SetString(PyExc_ValueError, "Not a memory BIO");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return PyUnicode_DecodeUTF8(data, size, error);
|
||||||
|
}
|
||||||
36
Modules/clinic/_ssl.c.h
generated
36
Modules/clinic/_ssl.c.h
generated
|
|
@ -88,6 +88,40 @@ exit:
|
||||||
return return_value;
|
return return_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PyDoc_STRVAR(_ssl__SSLSocket_get_verified_chain__doc__,
|
||||||
|
"get_verified_chain($self, /)\n"
|
||||||
|
"--\n"
|
||||||
|
"\n");
|
||||||
|
|
||||||
|
#define _SSL__SSLSOCKET_GET_VERIFIED_CHAIN_METHODDEF \
|
||||||
|
{"get_verified_chain", (PyCFunction)_ssl__SSLSocket_get_verified_chain, METH_NOARGS, _ssl__SSLSocket_get_verified_chain__doc__},
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
_ssl__SSLSocket_get_verified_chain_impl(PySSLSocket *self);
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
_ssl__SSLSocket_get_verified_chain(PySSLSocket *self, PyObject *Py_UNUSED(ignored))
|
||||||
|
{
|
||||||
|
return _ssl__SSLSocket_get_verified_chain_impl(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
PyDoc_STRVAR(_ssl__SSLSocket_get_unverified_chain__doc__,
|
||||||
|
"get_unverified_chain($self, /)\n"
|
||||||
|
"--\n"
|
||||||
|
"\n");
|
||||||
|
|
||||||
|
#define _SSL__SSLSOCKET_GET_UNVERIFIED_CHAIN_METHODDEF \
|
||||||
|
{"get_unverified_chain", (PyCFunction)_ssl__SSLSocket_get_unverified_chain, METH_NOARGS, _ssl__SSLSocket_get_unverified_chain__doc__},
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
_ssl__SSLSocket_get_unverified_chain_impl(PySSLSocket *self);
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
_ssl__SSLSocket_get_unverified_chain(PySSLSocket *self, PyObject *Py_UNUSED(ignored))
|
||||||
|
{
|
||||||
|
return _ssl__SSLSocket_get_unverified_chain_impl(self);
|
||||||
|
}
|
||||||
|
|
||||||
PyDoc_STRVAR(_ssl__SSLSocket_shared_ciphers__doc__,
|
PyDoc_STRVAR(_ssl__SSLSocket_shared_ciphers__doc__,
|
||||||
"shared_ciphers($self, /)\n"
|
"shared_ciphers($self, /)\n"
|
||||||
"--\n"
|
"--\n"
|
||||||
|
|
@ -1324,4 +1358,4 @@ exit:
|
||||||
#ifndef _SSL_ENUM_CRLS_METHODDEF
|
#ifndef _SSL_ENUM_CRLS_METHODDEF
|
||||||
#define _SSL_ENUM_CRLS_METHODDEF
|
#define _SSL_ENUM_CRLS_METHODDEF
|
||||||
#endif /* !defined(_SSL_ENUM_CRLS_METHODDEF) */
|
#endif /* !defined(_SSL_ENUM_CRLS_METHODDEF) */
|
||||||
/*[clinic end generated code: output=8736d838c9059151 input=a9049054013a1b77]*/
|
/*[clinic end generated code: output=3b6f4471fb187d85 input=a9049054013a1b77]*/
|
||||||
|
|
|
||||||
8
setup.py
8
setup.py
|
|
@ -2472,7 +2472,13 @@ class PyBuildExt(build_ext):
|
||||||
Extension(
|
Extension(
|
||||||
'_ssl',
|
'_ssl',
|
||||||
['_ssl.c'],
|
['_ssl.c'],
|
||||||
depends=['socketmodule.h', '_ssl/debughelpers.c', '_ssl.h'],
|
depends=[
|
||||||
|
'socketmodule.h',
|
||||||
|
'_ssl.h',
|
||||||
|
'_ssl/debughelpers.c',
|
||||||
|
'_ssl/misc.c',
|
||||||
|
'_ssl/cert.c',
|
||||||
|
],
|
||||||
**openssl_extension_kwargs
|
**openssl_extension_kwargs
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue