[pypy-commit] pypy stdlib-2.7.9: SSL module: add "cadata" parameter to load_verify_locations().
amauryfa
noreply at buildbot.pypy.org
Fri Jan 23 23:41:12 CET 2015
Author: Amaury Forgeot d'Arc <amauryfa at gmail.com>
Branch: stdlib-2.7.9
Changeset: r75506:8e49368b697a
Date: 2015-01-21 19:04 +0100
http://bitbucket.org/pypy/pypy/changeset/8e49368b697a/
Log: SSL module: add "cadata" parameter to load_verify_locations().
diff --git a/pypy/module/_ssl/interp_ssl.py b/pypy/module/_ssl/interp_ssl.py
--- a/pypy/module/_ssl/interp_ssl.py
+++ b/pypy/module/_ssl/interp_ssl.py
@@ -962,7 +962,8 @@
if ret != 1:
raise _ssl_seterror(space, None, -1)
- def load_verify_locations_w(self, space, w_cafile=None, w_capath=None):
+ def load_verify_locations_w(self, space, w_cafile=None, w_capath=None,
+ w_cadata=None):
if space.is_none(w_cafile):
cafile = None
else:
@@ -971,21 +972,112 @@
capath = None
else:
capath = space.str_w(w_capath)
- if cafile is None and capath is None:
+ if space.is_none(w_cadata):
+ cadata = None
+ ca_file_type = -1
+ else:
+ if not space.isinstance_w(w_cadata, space.w_unicode):
+ ca_file_type = SSL_FILETYPE_ASN1
+ cadata = space.bufferstr_w(w_cadata)
+ else:
+ ca_file_type = SSL_FILETYPE_PEM
+ try:
+ cadata = space.unicode_w(w_cadata).encode('ascii')
+ except UnicodeEncodeError:
+ raise oefmt(space.w_TypeError,
+ "cadata should be a ASCII string or a "
+ "bytes-like object")
+ if cafile is None and capath is None and cadata is None:
raise OperationError(space.w_TypeError, space.wrap(
"cafile and capath cannot be both omitted"))
- set_errno(0)
- ret = libssl_SSL_CTX_load_verify_locations(
- self.ctx, cafile, capath)
- if ret != 1:
- errno = get_errno()
- if errno:
- libssl_ERR_clear_error()
- raise wrap_oserror(space, OSError(errno, ''),
- exception_name = 'w_IOError')
+ # load from cadata
+ if cadata:
+ biobuf = libssl_BIO_new_mem_buf(cadata, len(cadata))
+ if not biobuf:
+ raise ssl_error(space, "Can't allocate buffer")
+ try:
+ store = libssl_SSL_CTX_get_cert_store(self.ctx)
+ loaded = 0
+ while True:
+ if ca_file_type == SSL_FILETYPE_ASN1:
+ cert = libssl_d2i_X509_bio(
+ biobuf, None)
+ else:
+ cert = libssl_PEM_read_bio_X509(
+ biobuf, None, None, None)
+ if not cert:
+ break
+ try:
+ r = libssl_X509_STORE_add_cert(store, cert)
+ finally:
+ libssl_X509_free(cert)
+ if not r:
+ err = libssl_ERR_peek_last_error()
+ if (libssl_ERR_GET_LIB(err) == ERR_LIB_X509 and
+ libssl_ERR_GET_REASON(err) ==
+ X509_R_CERT_ALREADY_IN_HASH_TABLE):
+ # cert already in hash table, not an error
+ libssl_ERR_clear_error()
+ else:
+ break
+ loaded += 1
+
+ err = libssl_ERR_peek_last_error()
+ if (ca_file_type == SSL_FILETYPE_ASN1 and
+ loaded > 0 and
+ libssl_ERR_GET_LIB(err) == ERR_LIB_ASN1 and
+ libssl_ERR_GET_REASON(err) == ASN1_R_HEADER_TOO_LONG):
+ # EOF ASN1 file, not an error
+ libssl_ERR_clear_error()
+ elif (ca_file_type == SSL_FILETYPE_PEM and
+ loaded > 0 and
+ libssl_ERR_GET_LIB(err) == ERR_LIB_PEM and
+ libssl_ERR_GET_REASON(err) == PEM_R_NO_START_LINE):
+ # EOF PEM file, not an error
+ libssl_ERR_clear_error
+ else:
+ _ssl_seterror(space, None, 0)
+ finally:
+ libssl_BIO_free(biobuf)
+
+ # load cafile or capath
+ if cafile or capath:
+ set_errno(0)
+ ret = libssl_SSL_CTX_load_verify_locations(
+ self.ctx, cafile, capath)
+ if ret != 1:
+ errno = get_errno()
+ if errno:
+ libssl_ERR_clear_error()
+ raise wrap_oserror(space, OSError(errno, ''),
+ exception_name = 'w_IOError')
+ else:
+ raise _ssl_seterror(space, None, -1)
+
+ def cert_store_stats_w(self, space):
+ store = libssl_SSL_CTX_get_cert_store(self.ctx)
+ counters = {'x509': 0, 'x509_ca': 0, 'crl': 0}
+ for i in range(libssl_sk_X509_OBJECT_num(store[0].c_objs)):
+ obj = libssl_sk_X509_OBJECT_value(store[0].c_objs, i)
+ if intmask(obj.c_type) == X509_LU_X509:
+ counters['x509'] += 1
+ if libssl_pypy_X509_OBJECT_data_x509(obj):
+ counters['x509_ca'] += 1
+ elif intmask(obj.c_type) == X509_LU_CRL:
+ counters['crl'] += 1
else:
- raise _ssl_seterror(space, None, -1)
-
+ # Ignore X509_LU_FAIL, X509_LU_RETRY, X509_LU_PKEY.
+ # As far as I can tell they are internal states and never
+ # stored in a cert store
+ pass
+ w_result = space.newdict()
+ space.setitem(w_result,
+ space.wrap('x509'), space.wrap(counters['x509']))
+ space.setitem(w_result,
+ space.wrap('x509_ca'), space.wrap(counters['x509_ca']))
+ space.setitem(w_result,
+ space.wrap('crl'), space.wrap(counters['crl']))
+ return w_result
_SSLContext.typedef = TypeDef(
"_ssl._SSLContext",
@@ -993,6 +1085,7 @@
_wrap_socket=interp2app(_SSLContext.descr_wrap_socket),
set_ciphers=interp2app(_SSLContext.descr_set_ciphers),
load_verify_locations=interp2app(_SSLContext.load_verify_locations_w),
+ cert_store_stats=interp2app(_SSLContext.cert_store_stats_w),
load_cert_chain=interp2app(_SSLContext.load_cert_chain_w),
set_default_verify_paths=interp2app(_SSLContext.descr_set_default_verify_paths),
diff --git a/pypy/module/_ssl/test/test_ssl.py b/pypy/module/_ssl/test/test_ssl.py
--- a/pypy/module/_ssl/test/test_ssl.py
+++ b/pypy/module/_ssl/test/test_ssl.py
@@ -276,6 +276,11 @@
ctx.load_verify_locations(self.keycert)
ctx.load_verify_locations(cafile=self.keycert, capath=None)
+ ctx = _ssl._SSLContext(_ssl.PROTOCOL_TLSv1)
+ with open(self.keycert) as f:
+ cacert_pem = f.read().decode('ascii')
+ ctx.load_verify_locations(cadata=cacert_pem)
+ assert ctx.cert_store_stats()["x509_ca"]
SSL_CERTIFICATE = """
-----BEGIN CERTIFICATE-----
diff --git a/rpython/rlib/ropenssl.py b/rpython/rlib/ropenssl.py
--- a/rpython/rlib/ropenssl.py
+++ b/rpython/rlib/ropenssl.py
@@ -38,6 +38,7 @@
# Unnamed structures are not supported by rffi_platform.
# So we replace an attribute access with a macro call.
'#define pypy_GENERAL_NAME_dirn(name) (name->d.dirn)',
+ '#define pypy_X509_OBJECT_data_x509(obj) (obj->data.x509)',
],
)
@@ -47,9 +48,11 @@
include_dir='inc32', library_dir='out32'),
])
+X509 = rffi.COpaquePtr('X509')
ASN1_STRING = lltype.Ptr(lltype.ForwardReference())
ASN1_ITEM = rffi.COpaquePtr('ASN1_ITEM')
X509_NAME = rffi.COpaquePtr('X509_NAME')
+stack_st_X509_OBJECT = rffi.COpaquePtr('struct stack_st_X509_OBJECT')
class CConfigBootstrap:
_compilation_info_ = eci
@@ -72,6 +75,7 @@
OPENSSL_NO_ECDH = rffi_platform.Defined("OPENSSL_NO_ECDH")
OPENSSL_NPN_NEGOTIATED = rffi_platform.Defined("OPENSSL_NPN_NEGOTIATED")
SSL_FILETYPE_PEM = rffi_platform.ConstantInteger("SSL_FILETYPE_PEM")
+ SSL_FILETYPE_ASN1 = rffi_platform.ConstantInteger("SSL_FILETYPE_ASN1")
SSL_OP_ALL = rffi_platform.ConstantInteger("SSL_OP_ALL")
SSL_OP_NO_SSLv2 = rffi_platform.ConstantInteger("SSL_OP_NO_SSLv2")
SSL_OP_NO_SSLv3 = rffi_platform.ConstantInteger("SSL_OP_NO_SSLv3")
@@ -102,6 +106,15 @@
SSL_MODE_AUTO_RETRY = rffi_platform.ConstantInteger("SSL_MODE_AUTO_RETRY")
SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER = rffi_platform.ConstantInteger("SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER")
+ ERR_LIB_X509 = rffi_platform.ConstantInteger("ERR_LIB_X509")
+ ERR_LIB_PEM = rffi_platform.ConstantInteger("ERR_LIB_PEM")
+ ERR_LIB_ASN1 = rffi_platform.ConstantInteger("ERR_LIB_ASN1")
+ PEM_R_NO_START_LINE = rffi_platform.ConstantInteger("PEM_R_NO_START_LINE")
+ ASN1_R_HEADER_TOO_LONG = rffi_platform.ConstantInteger(
+ "ASN1_R_HEADER_TOO_LONG")
+ X509_R_CERT_ALREADY_IN_HASH_TABLE = rffi_platform.ConstantInteger(
+ "X509_R_CERT_ALREADY_IN_HASH_TABLE")
+
NID_undef = rffi_platform.ConstantInteger("NID_undef")
NID_subject_alt_name = rffi_platform.ConstantInteger("NID_subject_alt_name")
GEN_DIRNAME = rffi_platform.ConstantInteger("GEN_DIRNAME")
@@ -128,6 +141,17 @@
X509_extension_st = rffi_platform.Struct(
'struct X509_extension_st',
[('value', ASN1_STRING)])
+ x509_store_st = rffi_platform.Struct(
+ 'struct x509_store_st',
+ [('objs', stack_st_X509_OBJECT)])
+
+ x509_object_st = rffi_platform.Struct(
+ 'struct x509_object_st',
+ [('type', rffi.INT)])
+
+ X509_LU_X509 = rffi_platform.ConstantInteger("X509_LU_X509")
+ X509_LU_CRL = rffi_platform.ConstantInteger("X509_LU_CRL")
+
X509V3_EXT_D2I = lltype.FuncType([rffi.VOIDP, rffi.CCHARPP, rffi.LONG],
rffi.VOIDP)
v3_ext_method = rffi_platform.Struct(
@@ -150,12 +174,6 @@
('name', rffi.CCHARP),
])
- OBJ_NAME_st = rffi_platform.Struct(
- 'OBJ_NAME',
- [('alias', rffi.INT),
- ('name', rffi.CCHARP),
- ])
-
for k, v in rffi_platform.configure(CConfig).items():
globals()[k] = v
@@ -166,9 +184,10 @@
SSL_CIPHER = rffi.COpaquePtr('SSL_CIPHER')
SSL = rffi.COpaquePtr('SSL')
BIO = rffi.COpaquePtr('BIO')
-X509 = rffi.COpaquePtr('X509')
X509_NAME_ENTRY = rffi.CArrayPtr(X509_name_entry_st)
X509_EXTENSION = rffi.CArrayPtr(X509_extension_st)
+X509_STORE = rffi.CArrayPtr(x509_store_st)
+X509_OBJECT = lltype.Ptr(x509_object_st)
X509V3_EXT_METHOD = rffi.CArrayPtr(v3_ext_method)
ASN1_OBJECT = rffi.COpaquePtr('ASN1_OBJECT')
ASN1_STRING.TO.become(asn1_string_st)
@@ -217,6 +236,7 @@
ssl_external('SSLv23_method', [], SSL_METHOD)
ssl_external('SSL_CTX_use_PrivateKey_file', [SSL_CTX, rffi.CCHARP, rffi.INT], rffi.INT)
ssl_external('SSL_CTX_use_certificate_chain_file', [SSL_CTX, rffi.CCHARP], rffi.INT)
+ssl_external('SSL_CTX_get_cert_store', [SSL_CTX], X509_STORE)
ssl_external('SSL_CTX_get_options', [SSL_CTX], rffi.LONG, macro=True)
ssl_external('SSL_CTX_set_options', [SSL_CTX, rffi.LONG], rffi.LONG, macro=True)
if HAVE_SSL_CTX_CLEAR_OPTIONS:
@@ -264,6 +284,7 @@
ssl_external('X509_NAME_ENTRY_get_data', [X509_NAME_ENTRY], ASN1_STRING)
ssl_external('i2d_X509', [X509, rffi.CCHARPP], rffi.INT)
ssl_external('X509_free', [X509], lltype.Void, releasegil=False)
+ssl_external('X509_check_ca', [X509], rffi.INT)
ssl_external('X509_get_notBefore', [X509], ASN1_TIME, macro=True)
ssl_external('X509_get_notAfter', [X509], ASN1_TIME, macro=True)
ssl_external('X509_get_serialNumber', [X509], ASN1_INTEGER)
@@ -272,6 +293,7 @@
ssl_external('X509_get_ext', [X509, rffi.INT], X509_EXTENSION)
ssl_external('X509V3_EXT_get', [X509_EXTENSION], X509V3_EXT_METHOD)
+ssl_external('X509_STORE_add_cert', [X509_STORE, X509], rffi.INT)
ssl_external('OBJ_obj2txt',
[rffi.CCHARP, rffi.INT, ASN1_OBJECT, rffi.INT], rffi.INT)
@@ -293,6 +315,13 @@
macro=True)
ssl_external('sk_GENERAL_NAME_value', [GENERAL_NAMES, rffi.INT], GENERAL_NAME,
macro=True)
+ssl_external('sk_X509_OBJECT_num', [stack_st_X509_OBJECT], rffi.INT,
+ macro=True)
+ssl_external('sk_X509_OBJECT_value', [stack_st_X509_OBJECT, rffi.INT],
+ X509_OBJECT, macro=True)
+ssl_external('pypy_X509_OBJECT_data_x509', [X509_OBJECT], X509,
+ macro=True)
+
ssl_external('GENERAL_NAME_print', [BIO, GENERAL_NAME], rffi.INT)
ssl_external('pypy_GENERAL_NAME_dirn', [GENERAL_NAME], X509_NAME,
macro=True)
@@ -306,6 +335,8 @@
ssl_external('ERR_peek_last_error', [], rffi.INT)
ssl_external('ERR_error_string', [rffi.ULONG, rffi.CCHARP], rffi.CCHARP)
ssl_external('ERR_clear_error', [], lltype.Void)
+ssl_external('ERR_GET_LIB', [rffi.ULONG], rffi.INT, macro=True)
+ssl_external('ERR_GET_REASON', [rffi.ULONG], rffi.INT, macro=True)
# 'releasegil=False' here indicates that this function will be called
# with the GIL held, and so is allowed to run in a RPython __del__ method.
@@ -323,10 +354,14 @@
ssl_external('BIO_s_file', [], BIO_METHOD)
ssl_external('BIO_new', [BIO_METHOD], BIO)
ssl_external('BIO_set_nbio', [BIO, rffi.INT], rffi.INT, macro=True)
+ssl_external('BIO_new_mem_buf', [rffi.VOIDP, rffi.INT], BIO)
ssl_external('BIO_free', [BIO], rffi.INT)
ssl_external('BIO_reset', [BIO], rffi.INT, macro=True)
ssl_external('BIO_read_filename', [BIO, rffi.CCHARP], rffi.INT, macro=True)
ssl_external('BIO_gets', [BIO, rffi.CCHARP, rffi.INT], rffi.INT)
+ssl_external('d2i_X509_bio', [BIO, rffi.VOIDP], X509)
+ssl_external('PEM_read_bio_X509',
+ [BIO, rffi.VOIDP, rffi.VOIDP, rffi.VOIDP], X509)
ssl_external('PEM_read_bio_X509_AUX',
[BIO, rffi.VOIDP, rffi.VOIDP, rffi.VOIDP], X509)
diff --git a/rpython/rtyper/tool/rffi_platform.py b/rpython/rtyper/tool/rffi_platform.py
--- a/rpython/rtyper/tool/rffi_platform.py
+++ b/rpython/rtyper/tool/rffi_platform.py
@@ -493,7 +493,7 @@
def prepare_code(self):
yield '#ifdef %s' % self.macro
yield 'int i;'
- yield 'char *p = %s;' % self.name
+ yield 'const char *p = %s;' % self.name
yield 'dump("defined", 1);'
yield 'for (i = 0; p[i] != 0; i++ ) {'
yield ' printf("value_%d: %d\\n", i, (int)(unsigned char)p[i]);'
More information about the pypy-commit
mailing list