On Wed, 03 Aug 2016, Stuart Henderson wrote: > On 2016/08/03 12:10, Stuart Henderson wrote: > > On 2016/07/30 17:04, Giovanni Bechis wrote: > > > On Sat, Jul 30, 2016 at 11:57:14AM +0100, Stuart Henderson wrote: > > > > Forwarding from an offlist message because incoming mail is broken.. > > > > > > > no problem for me, just renewed my certificate with > > > authenticator=standalone @i386 (OpenBSD 6.0 (GENERIC) #1915: Tue Jul 26 > > > 09:46:01 MDT 2016) > > > Have you added wxallowed in fstab(5) ? > > > > So wxallowed is required for this; it turns out that this is > > due to devel/py-cffi. py-cffi does an mmap which is currently > > RWX unless it detects PaX on Linux in which case it changes > > it to RW. But if I change things to permit that, we run into > > another problem: the library using py-cffi in this case is > > py-cryptography, which calls out to libcrypto, and it segfaults > > after doing so: > > > > (gdb) bt > > #0 0x000003d6f2169fc0 in ?? () > > #1 0x000003d6bef3ba4c in internal_verify (ctx=0x7f7ffffe4eb0) > > at > > /usr/src/lib/libcrypto/crypto/../../libssl/src/crypto/x509/x509_vfy.c:1611 > > #2 0x000003d6bef3d109 in X509_verify_cert (ctx=0x7f7ffffe4eb0) > > at > > /usr/src/lib/libcrypto/crypto/../../libssl/src/crypto/x509/x509_vfy.c:374 > > #3 0x000003d78b79c8f0 in ssl_verify_cert_chain (s=0x3d6c5708000, > > sk=Variable "sk" is not available. > > ) > > at /usr/src/lib/libssl/ssl/../../libssl/src/ssl/ssl_cert.c:448 > > #4 0x000003d78b78c030 in ssl3_get_server_certificate (s=0x3d6c5708000) > > at /usr/src/lib/libssl/ssl/../../libssl/src/ssl/s3_clnt.c:1015 > > #5 0x000003d78b78d421 in ssl3_connect (s=0x3d6c5708000) > > at /usr/src/lib/libssl/ssl/../../libssl/src/ssl/s3_clnt.c:297 > > #6 0x000003d78b794c4e in ssl23_connect (s=0x3d6c5708000) > > at /usr/src/lib/libssl/ssl/../../libssl/src/ssl/s23_clnt.c:477 > > #7 0x000003d78a76f08c in _cffi_f_SSL_do_handshake () > > from > > /usr/local/lib/python2.7/site-packages/cryptography/hazmat/bindings/_openssl.so > > #8 0x000003d70659d528 in PyEval_EvalFrameEx () > > from /usr/local/lib/libpython2.7.so.0.0 > > #9 0x000003d70659d2ea in PyEval_EvalFrameEx () > > from /usr/local/lib/libpython2.7.so.0.0 > > #10 0x000003d70659f33d in PyEval_EvalCodeEx () > > > > 1604 ok = x509_check_cert_time(ctx, xs, 0); > > 1605 if (!ok) > > 1606 goto end; > > 1607 > > 1608 /* The last error (if any) is still in the error value > > */ > > 1609 ctx->current_issuer = xi; > > 1610 ctx->current_cert = xs; > > 1611 ok = (*cb)(1, ctx); > > 1612 if (!ok) > > 1613 goto end; > > > > At the moment I don't have any ideas other than "mark /usr/local > > as wxallowed if you want to run things using py-cffi". Can anyone > > think of something better? > > > > Here are some more prints from another run of this (this segfault > is occurring with py-cffi patched to just do RW mappings; it occurs > whether or not the partition is marked wxallowed). > > I can't do any more digging until next week. > > (gdb) bt > #0 0x00001207405f1fc0 in ?? () > #1 0x00001206b3377a4c in internal_verify (ctx=0x7f7ffffeaae0) > at > /usr/src/lib/libcrypto/crypto/../../libssl/src/crypto/x509/x509_vfy.c:1611 > #2 0x00001206b3379109 in X509_verify_cert (ctx=0x7f7ffffeaae0) > at > /usr/src/lib/libcrypto/crypto/../../libssl/src/crypto/x509/x509_vfy.c:374 > #3 0x00001206d2eff8f0 in ssl_verify_cert_chain (s=0x120700f1c000, > sk=Variable "sk" is not available. > ) > at /usr/src/lib/libssl/ssl/../../libssl/src/ssl/ssl_cert.c:448 > #4 0x00001206d2eef030 in ssl3_get_server_certificate (s=0x120700f1c000) > at /usr/src/lib/libssl/ssl/../../libssl/src/ssl/s3_clnt.c:1015 > #5 0x00001206d2ef0421 in ssl3_connect (s=0x120700f1c000) > at /usr/src/lib/libssl/ssl/../../libssl/src/ssl/s3_clnt.c:297 > #6 0x00001206d2ef7c4e in ssl23_connect (s=0x120700f1c000) > at /usr/src/lib/libssl/ssl/../../libssl/src/ssl/s23_clnt.c:477 > #7 0x00001206dca5a44f in _cffi_f_SSL_do_handshake (self=0x1206c2cf7dc0, > arg0=0x1206b5421650) > at > /usr/obj/ports/py-cryptography-1.4/cryptography-1.4/temp.openbsd-6.0-amd64-2.7/_openssl.c:51236 > #8 0x0000120731c1a528 in PyEval_EvalFrameEx () from > /usr/local/lib/libpython2.7.so.0.0 > #9 0x0000120731c1a2ea in PyEval_EvalFrameEx () from > /usr/local/lib/libpython2.7.so.0.0 > [..] > #38 0x0000120731c1c33d in PyEval_EvalCodeEx () from > /usr/local/lib/libpython2.7.so.0.0 > #39 0x0000120731ba3bf7 in function_call () from > /usr/local/lib/libpython2.7.so.0.0 > #40 0x0000120731b79cc8 in PyObject_Call () from > /usr/local/lib/libpython2.7.so.0.0 > #41 0x0000120731b88f92 in instancemethod_call () from > /usr/local/lib/libpython2.7.so.0.0 > [..] > (gdb) frame 1 > #1 0x00001206b3377a4c in internal_verify (ctx=0x7f7ffffeaae0) > at > /usr/src/lib/libcrypto/crypto/../../libssl/src/crypto/x509/x509_vfy.c:1611 > 1611 ok = (*cb)(1, ctx); > (gdb) p *ctx > $1 = {ctx = 0x1206c17c4d00, current_method = 0, cert = 0x12067c8ab600, > untrusted = 0x1206c17ca8c0, crls = 0x0, > param = 0x120753e79540, other_ctx = 0x0, verify = 0x1206b3377940 > <internal_verify>, verify_cb = 0x1207405f1fc0, > get_issuer = 0x1206b333f890 <X509_STORE_CTX_get1_issuer>, check_issued = > 0x1206b3377240 <check_issued>, > check_revocation = 0x1206b3378490 <check_revocation>, get_crl = 0, > check_crl = 0x1206b3379220 <check_crl>, > cert_crl = 0x1206b3377070 <cert_crl>, check_policy = 0x1206b3376f10 > <check_policy>, > lookup_certs = 0x1206b333fd10 <X509_STORE_get1_certs>, lookup_crls = > 0x1206b333fb10 <X509_STORE_get1_crls>, > cleanup = 0, valid = 0, last_untrusted = 3, chain = 0x1206a6a5c860, tree = > 0x0, explicit_policy = 0, > error_depth = 3, error = 0, current_cert = 0x1206d841ae00, current_issuer = > 0x1206d841ae00, current_crl = 0x0, > current_crl_score = 0, current_reasons = 0, parent = 0x0, ex_data = {sk = > 0x1206a6a5ca00}} > (gdb) list > 1606 goto end; > 1607 > 1608 /* The last error (if any) is still in the error value > */ > 1609 ctx->current_issuer = xi; > 1610 ctx->current_cert = xs; > 1611 ok = (*cb)(1, ctx); > 1612 if (!ok) > 1613 goto end; > 1614 > 1615 n--; > (gdb) p *ctx->cert > $2 = {cert_info = 0x120738043f80, sig_alg = 0x1206fd9518f0, signature = > 0x1206881396c0, valid = 0, references = 2, > name = 0x1206de46cc00 "/CN=*.api.letsencrypt.org/O=INTERNET SECURITY > RESEARCH GROUP/L=Mountain View/ST=California/C=US", ex_data = {sk = 0x0}, > ex_pathlen = -1, ex_pcpathlen = 0, ex_flags = 262, ex_kusage = 160, > ex_xkusage = 3, > ex_nscert = 0, skid = 0x1206a6a5cd20, akid = 0x120699a98880, policy_cache = > 0x0, crldp = 0x12068875b3a0, > altname = 0x12068875b0a0, nc = 0x0, sha1_hash = > "?[i?\205@???\a?O?\v??1B\022?", aux = 0x0} > (gdb) p cb > $3 = (int (*)(int, X509_STORE_CTX *)) 0x1207405f1fc0 > > So it's trying to call 0x1207405f1fc0 which is the function pointer > stored in verify_cb.
The problem is in py-openssl, which is still using dynamic ffi callbacks, causing W^X violations. The diff below (adapted from an unmerged branch of py-cffi) makes letsencrypt work for me. https://bitbucket.org/cffi/cffi/issues/231/writeable-memory-execution-execmem-with https://github.com/pyca/cryptography/issues/2477 https://bugzilla.redhat.com/show_bug.cgi?id=1249685 Ciao! David P.S.: I know this implementation has problems wrt fork() (https://bugs.python.org/issue25653), but given that we already took this road with devel/libffi rev 1.35... The example code from https://bugzilla.redhat.com/show_bug.cgi?id=531233 is already crashing for us. Index: Makefile =================================================================== RCS file: /cvs/ports/devel/py-cffi/Makefile,v retrieving revision 1.9 diff -u -p -r1.9 Makefile --- Makefile 9 Jan 2016 16:23:23 -0000 1.9 +++ Makefile 9 Aug 2016 09:19:46 -0000 @@ -8,6 +8,7 @@ COMMENT= Foreign Function Interface for MODPY_EGG_VERSION= 1.4.2 DISTNAME= cffi-${MODPY_EGG_VERSION} PKGNAME= py-${DISTNAME} +REVISION= 0 CATEGORIES= devel # MIT Index: patches/patch-c__cffi_backend_c =================================================================== RCS file: patches/patch-c__cffi_backend_c diff -N patches/patch-c__cffi_backend_c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ patches/patch-c__cffi_backend_c 9 Aug 2016 09:19:46 -0000 @@ -0,0 +1,138 @@ +$OpenBSD$ + +Merge patches from the "ffi_closure_alloc" branch of +https://bitbucket.org/cffi/cffi: this is a partial rewrite of cffi's +allocation routines to simply wrap newer libffi's allocation routines +ffi_closure_alloc(), ffi_closure_free(), and ffi_prep_closure_loc(), +which can handle the need for separate writable and executable +memory mappings. + +--- c/_cffi_backend.c.orig Fri Aug 5 23:14:34 2016 ++++ c/_cffi_backend.c Fri Aug 5 23:18:04 2016 +@@ -58,8 +58,6 @@ + # endif + #endif + +-#include "malloc_closure.h" +- + #if PY_MAJOR_VERSION >= 3 + # define STR_OR_BYTES "bytes" + # define PyText_Type PyUnicode_Type +@@ -251,6 +249,11 @@ typedef struct { + } CDataObject_gcp; + + typedef struct { ++ CDataObject head; ++ ffi_closure *closure; ++} CDataObject_closure; ++ ++typedef struct { + ffi_cif cif; + /* the following information is used when doing the call: + - a buffer of size 'exchange_size' is malloced +@@ -1604,10 +1607,10 @@ static void cdataowninggc_dealloc(CDataObject *cd) + Py_DECREF(x); + } + else if (cd->c_type->ct_flags & CT_FUNCTIONPTR) { /* a callback */ +- ffi_closure *closure = (ffi_closure *)cd->c_data; +- PyObject *args = (PyObject *)(closure->user_data); ++ ffi_closure *closure = ((CDataObject_closure *)cd)->closure; ++ PyObject *args = (PyObject *)closure->user_data; + Py_XDECREF(args); +- cffi_closure_free(closure); ++ ffi_closure_free(closure); + } + else if (cd->c_type->ct_flags & CT_IS_UNSIZED_CHAR_A) { /* from_buffer */ + Py_buffer *view = ((CDataObject_owngc_frombuf *)cd)->bufferview; +@@ -1624,8 +1627,8 @@ static int cdataowninggc_traverse(CDataObject *cd, vis + Py_VISIT(x); + } + else if (cd->c_type->ct_flags & CT_FUNCTIONPTR) { /* a callback */ +- ffi_closure *closure = (ffi_closure *)cd->c_data; +- PyObject *args = (PyObject *)(closure->user_data); ++ ffi_closure *closure = ((CDataObject_closure *)cd)->closure; ++ PyObject *args = (PyObject *)closure->user_data; + Py_VISIT(args); + } + else if (cd->c_type->ct_flags & CT_IS_UNSIZED_CHAR_A) { /* from_buffer */ +@@ -1645,8 +1648,8 @@ static int cdataowninggc_clear(CDataObject *cd) + Py_DECREF(x); + } + else if (cd->c_type->ct_flags & CT_FUNCTIONPTR) { /* a callback */ +- ffi_closure *closure = (ffi_closure *)cd->c_data; +- PyObject *args = (PyObject *)(closure->user_data); ++ ffi_closure *closure = ((CDataObject_closure *)cd)->closure; ++ PyObject *args = (PyObject *)closure->user_data; + closure->user_data = NULL; + Py_XDECREF(args); + } +@@ -1833,7 +1836,8 @@ static PyObject *cdataowninggc_repr(CDataObject *cd) + return _cdata_repr2(cd, "handle to", x); + } + else if (cd->c_type->ct_flags & CT_FUNCTIONPTR) { /* a callback */ +- PyObject *args = (PyObject *)((ffi_closure *)cd->c_data)->user_data; ++ ffi_closure *closure = ((CDataObject_closure *)cd)->closure; ++ PyObject *args = (PyObject *)closure->user_data; + if (args == NULL) + return cdata_repr(cd); + else +@@ -5156,11 +5160,12 @@ static PyObject *prepare_callback_info_tuple(CTypeDesc + static PyObject *b_callback(PyObject *self, PyObject *args) + { + CTypeDescrObject *ct; +- CDataObject *cd; ++ CDataObject_closure *cd; + PyObject *ob, *error_ob = Py_None, *onerror_ob = Py_None; + PyObject *infotuple; + cif_description_t *cif_descr; + ffi_closure *closure; ++ void *closure_exec; + + if (!PyArg_ParseTuple(args, "O!O|OO:callback", &CTypeDescr_Type, &ct, &ob, + &error_ob, &onerror_ob)) +@@ -5170,15 +5175,20 @@ static PyObject *b_callback(PyObject *self, PyObject * + if (infotuple == NULL) + return NULL; + +- closure = cffi_closure_alloc(); ++ closure = ffi_closure_alloc(sizeof(ffi_closure), &closure_exec); ++ if (closure == NULL) { ++ Py_DECREF(infotuple); ++ return NULL; ++ } + +- cd = PyObject_GC_New(CDataObject, &CDataOwningGC_Type); ++ cd = PyObject_GC_New(CDataObject_closure, &CDataOwningGC_Type); + if (cd == NULL) + goto error; + Py_INCREF(ct); +- cd->c_type = ct; +- cd->c_data = (char *)closure; +- cd->c_weakreflist = NULL; ++ cd->head.c_type = ct; ++ cd->head.c_data = (char *)closure_exec; ++ cd->head.c_weakreflist = NULL; ++ cd->closure = closure; + PyObject_GC_Track(cd); + + cif_descr = (cif_description_t *)ct->ct_extra; +@@ -5188,8 +5198,8 @@ static PyObject *b_callback(PyObject *self, PyObject * + "return type or with '...'", ct->ct_name); + goto error; + } +- if (ffi_prep_closure(closure, &cif_descr->cif, +- invoke_callback, infotuple) != FFI_OK) { ++ if (ffi_prep_closure_loc(closure, &cif_descr->cif, invoke_callback, ++ infotuple, closure_exec) != FFI_OK) { + PyErr_SetString(PyExc_SystemError, + "libffi failed to build this callback"); + goto error; +@@ -5200,7 +5210,7 @@ static PyObject *b_callback(PyObject *self, PyObject * + error: + closure->user_data = NULL; + if (cd == NULL) +- cffi_closure_free(closure); ++ ffi_closure_free(closure); + else + Py_DECREF(cd); + Py_XDECREF(infotuple);