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);

Reply via email to