Author: Richard Plangger <[email protected]>
Branch: py3.5-ssl
Changeset: r88141:d873256da8f4
Date: 2016-11-04 20:33 +0100
http://bitbucket.org/pypy/pypy/changeset/d873256da8f4/
Log: checkin changes made on the pypy stdlib ssl repo
diff --git a/lib_pypy/openssl/_cffi_src/openssl/x509_vfy.py
b/lib_pypy/openssl/_cffi_src/openssl/x509_vfy.py
--- a/lib_pypy/openssl/_cffi_src/openssl/x509_vfy.py
+++ b/lib_pypy/openssl/_cffi_src/openssl/x509_vfy.py
@@ -133,7 +133,7 @@
int X509_STORE_set_default_paths(X509_STORE *);
int X509_STORE_set_flags(X509_STORE *, unsigned long);
void X509_STORE_free(X509_STORE *);
-
+X509_VERIFY_PARAM *_X509_STORE_get0_param(X509_STORE *);
/* X509_STORE_CTX */
X509_STORE_CTX *X509_STORE_CTX_new(void);
@@ -250,4 +250,8 @@
static const long Cryptography_HAS_X509_V_FLAG_TRUSTED_FIRST = 0;
static const long X509_V_FLAG_TRUSTED_FIRST = 0;
#endif
+
+X509_VERIFY_PARAM *_X509_STORE_get0_param(X509_STORE *store) {
+ return store->param;
+}
"""
diff --git a/lib_pypy/openssl/_stdssl/__init__.py
b/lib_pypy/openssl/_stdssl/__init__.py
--- a/lib_pypy/openssl/_stdssl/__init__.py
+++ b/lib_pypy/openssl/_stdssl/__init__.py
@@ -1,3 +1,5 @@
+import sys
+import errno
import time
import _thread
import weakref
@@ -5,7 +7,7 @@
from _openssl import lib
from openssl._stdssl.certificate import _test_decode_cert
from openssl._stdssl.utility import _str_with_len
-from openssl._stdssl.error import (ssl_error,
+from openssl._stdssl.error import (ssl_error, ssl_lib_error,
SSLError, SSLZeroReturnError, SSLWantReadError,
SSLWantWriteError, SSLSyscallError,
SSLEOFError)
@@ -33,6 +35,13 @@
CLIENT = 0
SERVER = 1
+VERIFY_DEFAULT = 0
+VERIFY_CRL_CHECK_LEAF = lib.X509_V_FLAG_CRL_CHECK
+VERIFY_CRL_CHECK_CHAIN = lib.X509_V_FLAG_CRL_CHECK |
lib.X509_V_FLAG_CRL_CHECK_ALL
+VERIFY_X509_STRICT = lib.X509_V_FLAG_X509_STRICT
+if lib.Cryptography_HAS_X509_V_FLAG_TRUSTED_FIRST:
+ VERIFY_X509_TRUSTED_FIRST = lib.X509_V_FLAG_TRUSTED_FIRST
+
CERT_NONE = 0
CERT_OPTIONAL = 1
CERT_REQUIRED = 2
@@ -41,10 +50,13 @@
if name.startswith('SSL_OP'):
globals()[name[4:]] = getattr(lib, name)
+OP_ALL = lib.SSL_OP_ALL & ~lib.SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS
+
SSL_CLIENT = 0
SSL_SERVER = 1
-PROTOCOL_SSLv2 = 0
+if lib.Cryptography_HAS_SSL2:
+ PROTOCOL_SSLv2 = 0
PROTOCOL_SSLv3 = 1
PROTOCOL_SSLv23 = 2
PROTOCOL_TLSv1 = 3
@@ -71,14 +83,43 @@
lib.OpenSSL_add_all_algorithms()
class PasswordInfo(object):
- w_callable = None
+ callable = None
password = None
operationerror = None
PWINFO_STORAGE = {}
[email protected]_extern
-def _password_callback(buf, size, rwflag, userdata):
- pass
+def _Cryptography_pem_password_cb(buf, size, rwflag, userdata):
+ pw_info = ffi.from_handle(userdata)
+
+ # TODO PySSL_END_ALLOW_THREADS_S(pw_info->thread_state);
+ password = pw_info.password
+
+ if pw_info.callable:
+ try:
+ password = pw_info.callable()
+ except Exception as e:
+ pw_info.operationerror = e
+ return 0
+
+ if not isinstance(password, (str, bytes, bytearray)):
+ pw_info.operationerror = TypeError("password callback must return
a string")
+ return 0
+
+ password = _str_to_ffi_buffer(password)
+
+ if (len(password) > size):
+ pw_info.operationerror = ValueError("password cannot be longer than %d
bytes" % size)
+ return 0
+
+ #PySSL_BEGIN_ALLOW_THREADS_S(pw_info->thread_state);
+ ffi.memmove(buf, password, len(password))
+ return len(password)
+
+if lib.Cryptography_STATIC_CALLBACKS:
+ ffi.def_extern(_Cryptography_pem_password_cb)
+ Cryptography_pem_password_cb = lib.Cryptography_pem_password_cb
+else:
+ Cryptography_pem_password_cb =
ffi.callback("int(char*,int,int,void*)")(_Cryptography_pem_password_cb)
def _ssl_select(sock, write, timeout):
pass
@@ -216,7 +257,7 @@
class _SSLContext(object):
- __slots__ = ('ctx', 'check_hostname', 'verify_mode')
+ __slots__ = ('ctx', 'check_hostname')
def __new__(cls, protocol):
self = object.__new__(cls)
@@ -229,7 +270,7 @@
method = lib.TLSv1_2_method()
elif protocol == PROTOCOL_SSLv3 and lib.Cryptography_HAS_SSL3_METHOD:
method = lib.SSLv3_method()
- elif protocol == PROTOCOL_SSLv2 and lib.Cryptography_HAS_SSL2_METHOD:
+ elif lib.Cryptography_HAS_SSL2 and protocol == PROTOCOL_SSLv2:
method = lib.SSLv2_method()
elif protocol == PROTOCOL_SSLv23:
method = lib.SSLv23_method()
@@ -246,7 +287,7 @@
# Defaults
lib.SSL_CTX_set_verify(self.ctx, lib.SSL_VERIFY_NONE, ffi.NULL)
options = lib.SSL_OP_ALL & ~lib.SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS
- if protocol != PROTOCOL_SSLv2:
+ if not lib.Cryptography_HAS_SSL2 or protocol != PROTOCOL_SSLv2:
options |= lib.SSL_OP_NO_SSLv2
if protocol != PROTOCOL_SSLv3:
options |= lib.SSL_OP_NO_SSLv3
@@ -269,8 +310,80 @@
lib.SSL_CTX_set_tmp_ecdh(self.ctx, key)
finally:
lib.EC_KEY_free(key)
+ if lib.Cryptography_HAS_X509_V_FLAG_TRUSTED_FIRST:
+ store = lib.SSL_CTX_get_cert_store(self.ctx)
+ lib.X509_STORE_set_flags(store, lib.X509_V_FLAG_TRUSTED_FIRST)
return self
+ @property
+ def options(self):
+ return lib.SSL_CTX_get_options(self.ctx)
+
+ @options.setter
+ def options(self, value):
+ new_opts = int(value)
+ opts = lib.SSL_CTX_get_options(self.ctx)
+ clear = opts & ~new_opts
+ set = ~opts & new_opts
+ if clear:
+ if lib.Cryptography_HAS_SSL_CTX_CLEAR_OPTIONS:
+ lib.SSL_CTX_clear_options(self.ctx, clear)
+ else:
+ raise ValueError("can't clear options before OpenSSL 0.9.8m")
+ if set:
+ lib.SSL_CTX_set_options(self.ctx, set)
+
+ @property
+ def verify_mode(self):
+ mode = lib.SSL_CTX_get_verify_mode(self.ctx)
+ if mode == lib.SSL_VERIFY_NONE:
+ return CERT_NONE
+ elif mode == lib.SSL_VERIFY_PEER:
+ return CERT_OPTIONAL
+ elif mode == lib.SSL_VERIFY_PEER | lib.SSL_VERIFY_FAIL_IF_NO_PEER_CERT:
+ return CERT_REQUIRED
+ raise ssl_error("invalid return value from SSL_CTX_get_verify_mode")
+
+ @verify_mode.setter
+ def verify_mode(self, value):
+ n = int(value)
+ if n == CERT_NONE:
+ mode = lib.SSL_VERIFY_NONE
+ elif n == CERT_OPTIONAL:
+ mode = lib.SSL_VERIFY_PEER
+ elif n == CERT_REQUIRED:
+ mode = lib.SSL_VERIFY_PEER | lib.SSL_VERIFY_FAIL_IF_NO_PEER_CERT
+ else:
+ raise ValueError("invalid value for verify_mode")
+ if mode == lib.SSL_VERIFY_NONE and self.check_hostname:
+ raise ValueError("Cannot set verify_mode to CERT_NONE when " \
+ "check_hostname is enabled.")
+ lib.SSL_CTX_set_verify(self.ctx, mode, ffi.NULL);
+
+ @property
+ def verify_flags(self):
+ store = lib.SSL_CTX_get_cert_store(self.ctx)
+ param = lib._X509_STORE_get0_param(store)
+ flags = lib.X509_VERIFY_PARAM_get_flags(param)
+ return int(flags)
+
+ @verify_flags.setter
+ def verify_flags(self, value):
+ new_flags = int(value)
+ store = lib.SSL_CTX_get_cert_store(self.ctx);
+ param = lib._X509_STORE_get0_param(store)
+ flags = lib.X509_VERIFY_PARAM_get_flags(param);
+ clear = flags & ~new_flags;
+ set = ~flags & new_flags;
+ if clear:
+ param = lib._X509_STORE_get0_param(store)
+ if not lib.X509_VERIFY_PARAM_clear_flags(param, clear):
+ raise ssl_error(None, 0)
+ if set:
+ param = lib._X509_STORE_get0_param(store)
+ if not lib.X509_VERIFY_PARAM_set_flags(param, set):
+ raise ssl_error(None, 0)
+
def set_ciphers(self, cipherlist):
cipherlistbuf = _str_to_ffi_buffer(cipherlist)
ret = lib.SSL_CTX_set_cipher_list(self.ctx, cipherlistbuf)
@@ -294,13 +407,13 @@
if callable(password):
pw_info.callable = password
else:
- if isinstance(password, str):
+ if isinstance(password, (str, bytes, bytearray)):
pw_info.password = password
+ else:
+ raise TypeError("password should be a string or callable")
- raise TypeError("password should be a string or callable")
-
- lib.SSL_CTX_set_default_passwd_cb(self.ctx, _password_callback)
- lib.SSL_CTX_set_default_passwd_cb_userdata(self.ctx,
ffi.cast("void*", index))
+ lib.SSL_CTX_set_default_passwd_cb(self.ctx,
Cryptography_pem_password_cb)
+ lib.SSL_CTX_set_default_passwd_cb_userdata(self.ctx,
ffi.new_handle(pw_info))
try:
certfilebuf = _str_to_ffi_buffer(certfile)
@@ -309,12 +422,12 @@
if pw_info.operationerror:
lib.ERR_clear_error()
raise pw_info.operationerror
- errno = ffi.errno
- if errno:
+ _errno = ffi.errno
+ if _errno:
lib.ERR_clear_error()
- raise OSError(errno, '')
+ raise OSError(_errno, "Error")
else:
- raise _ssl_seterror(None, -1)
+ raise ssl_lib_error()
keyfilebuf = _str_to_ffi_buffer(keyfile)
ret = lib.SSL_CTX_use_PrivateKey_file(self.ctx, keyfilebuf,
@@ -323,12 +436,12 @@
if pw_info.operationerror:
lib.ERR_clear_error()
raise pw_info.operationerror
- errno = ffi.errno
- if errno:
+ _errno = ffi.errno
+ if _errno:
lib.ERR_clear_error()
- raise OSError(errno, '')
+ raise OSError(_errno, None)
else:
- raise _ssl_seterror(None, -1)
+ raise ssl_lib_error()
ret = lib.SSL_CTX_check_private_key(self.ctx)
if ret != 1:
@@ -665,12 +778,15 @@
#
#
+def _str_from_buf(buf):
+ return ffi.string(buf).decode('utf-8')
+
def _asn1obj2py(obj):
nid = lib.OBJ_obj2nid(obj)
if nid == lib.NID_undef:
raise ValueError("Unknown object")
- sn = lib.OBJ_nid2sn(nid)
- ln = lib.OBJ_nid2ln(nid)
+ sn = _str_from_buf(lib.OBJ_nid2sn(nid))
+ ln = _str_from_buf(lib.OBJ_nid2ln(nid))
buf = ffi.new("char[255]")
length = lib.OBJ_obj2txt(buf, len(buf), obj, 1)
if length < 0:
@@ -682,15 +798,23 @@
def txt2obj(txt, name):
_bytes = _str_to_ffi_buffer(txt)
- obj = lib.OBJ_txt2obj(_bytes, int(name))
- if obj is ffi.NULL:
- raise ValueError("unkown object '%s'", txt)
+ is_name = 0 if name else 1
+ obj = lib.OBJ_txt2obj(_bytes, is_name)
+ if obj == ffi.NULL:
+ raise ValueError("unknown object '%s'" % txt)
result = _asn1obj2py(obj)
lib.ASN1_OBJECT_free(obj)
return result
def nid2obj(nid):
- raise NotImplementedError
+ if nid < lib.NID_undef:
+ raise ValueError("NID must be positive.")
+ obj = lib.OBJ_nid2obj(nid);
+ if obj == ffi.NULL:
+ raise ValueError("unknown NID %i" % nid)
+ result = _asn1obj2py(obj);
+ lib.ASN1_OBJECT_free(obj);
+ return result;
class MemoryBIO(object):
@@ -744,7 +868,7 @@
# if (!target) goto error; \
# }
# XXX
- return ffi.string(buf).decode('utf-8')
+ return ffi.string(buf).decode(sys.getfilesystemencoding())
def get_default_verify_paths():
diff --git a/lib_pypy/openssl/_stdssl/error.py
b/lib_pypy/openssl/_stdssl/error.py
--- a/lib_pypy/openssl/_stdssl/error.py
+++ b/lib_pypy/openssl/_stdssl/error.py
@@ -23,6 +23,9 @@
class SSLEOFError(SSLError):
""" SSL/TLS connection terminated abruptly. """
+def ssl_lib_error():
+ errcode = lib.ERR_peek_last_error()
+ return ssl_error(None, 0, None, errcode)
def ssl_error(msg, errno=0, errtype=None, errcode=0):
reason_str = None
@@ -30,8 +33,8 @@
if errcode:
err_lib = lib.ERR_GET_LIB(errcode)
err_reason = lib.ERR_GET_REASON(errcode)
- reason_str = ERROR_CODES_TO_NAMES.get((err_lib, err_reason), None)
- lib_str = LIBRARY_CODES_TO_NAMES.get(err_lib, None)
+ reason_str = ERR_CODES_TO_NAMES.get((err_lib, err_reason), None)
+ lib_str = LIB_CODES_TO_NAMES.get(err_lib, None)
msg = ffi.string(lib.ERR_reason_error_string(errcode)).decode('utf-8')
if not msg:
msg = "unknown error"
@@ -54,22 +57,16 @@
#return OperationError(w_exception_class, w_exception)
ERR_CODES_TO_NAMES = {}
+ERR_NAMES_TO_CODES = {}
LIB_CODES_TO_NAMES = {}
-# TODO errcode = error_codes;
-# TODO while (errcode->mnemonic != NULL) {
-# TODO mnemo = PyUnicode_FromString(errcode->mnemonic);
-# TODO key = Py_BuildValue("ii", errcode->library, errcode->reason);
-# TODO if (mnemo == NULL || key == NULL)
-# TODO return NULL;
-# TODO if (PyDict_SetItem(err_codes_to_names, key, mnemo))
-# TODO return NULL;
-# TODO if (PyDict_SetItem(err_names_to_codes, mnemo, key))
-# TODO return NULL;
-# TODO Py_DECREF(key);
-# TODO Py_DECREF(mnemo);
-# TODO errcode++;
-# TODO }
+from openssl._stdssl.errorcodes import _error_codes
+
+for mnemo, library, reason in _error_codes:
+ key = (library, reason)
+ assert mnemo is not None and key is not None
+ ERR_CODES_TO_NAMES[key] = mnemo
+ ERR_NAMES_TO_CODES[mnemo] = key
def _fill_and_raise_ssl_error(error, errcode):
pass
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit