Re: PyOpenSSL empty socket property

Sean DiZazzo <[email protected]> Thu, 5 Sep 2019 23:07:33 -0700
Newsgroups gmane.comp.python.twisted
Message-ID <CANzH6etmN2uaeSwHHiNqjypwegHtwBD6mGMLgE8T88KT7+8+6w@mail.gmail.com>
--===============1979704781669944454==
Content-Type: multipart/alternative; boundary="000000000000a8458d0591dc412a"

--000000000000a8458d0591dc412a
Content-Type: text/plain; charset="UTF-8"

Perhaps this line from OpenSSL.SSL.Connection is a clue.

"socket may be None; in this case, the Connection is created with a memory
BIO: see the bio_read(), bio_write(), and bio_shutdown() methods."
https://www.pyopenssl.org/en/stable/api/ssl.html#OpenSSL.SSL.Connection

On Wed, Sep 4, 2019 at 1:38 AM Arn Vollebregt <[email protected]>
wrote:

> Hi,
>
> I noticed that PyOpenSSL SNI callbacks (set with
> *ctx.set_tlsext_servername_callback*) receive a *OpenSSL.SSL.Connection*
> object within Twisted that have an empty *_socket* property, while this
> property *is* actually set when using *PyOpenSSL* directly. For my
> use-case this is a problem as I want to call *conn._socket.getpeername()*
> to determine the peer's IP address. So I am wondering: why is this
> behaviour different? And how do I get the peer IP address?
>
> ---console---
> user:~$ sudo python testTwisted.py &
> [3] 32842
> user:~$ curl -s --insecure --key clientPrivateKey.pem --cert
> clientCertificate.pem https://127.0.0.1 > /dev/null
> 'sniCallback' called.
>         conn._socket: None
> 'verifyCallback' called for result 0
>         conn._socket: None
> 'verifyCallback' called for result 1
>         conn._socket: None
> user:~$ sudo python testPyOpenSSL.py &
> [1] 33270
> user:~$ curl -s --insecure --key clientPrivateKey.pem --cert
> clientCertificate.pem https://127.0.0.1 > /dev/null
> 'sniCallback' called.
>         conn._socket: <socket._socketobject object at 0x7f34c5bd3130>
> <class 'OpenSSL.SSL.Connection'>
> 'verifyCallback' called for result 0
>         conn._socket: <socket._socketobject object at 0x7f34c5bd3130>
> 'verifyCallback' called for result 1
>         conn._socket: <socket._socketobject object at 0x7f34c5bd3130>
> 127.0.0.1 - - [29/Aug/2019 11:45:47] "GET / HTTP/1.1" 200 -
> ------
>
> ---testTwisted.py---
> ### Generate server key material ###
> # openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout
> serverPrivateKey.pem -out serverCertificate.pem -subj
> "/C=''/O=''/OU=''/CN=server"
> ### Generate client key material ###
> # openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout
> clientPrivateKey.pem -out clientCertificate.pem -subj
> "/C=''/O=''/OU=''/CN=client"
> from __future__ import print_function
> #https://twistedmatrix.com/documents/12.0.0/core/howto/ssl.html
> from OpenSSL import SSL
> from twisted.internet import ssl, reactor
> from twisted.web import server, resource
> from twisted.internet.protocol import Factory, Protocol
>
> def verifyCallback(conn, cert, errno, depth, result):
>     print('\'verifyCallback\' called for result ' + str(result))
>     print('\tconn._socket: ' + str(conn._socket))
>     return True
>
> def sniCallback(conn):
>     print('\'sniCallback\' called.')
>     print('\tconn._socket: ' + str(conn._socket))
>
> class MainResource(resource.Resource):
>     isLeaf = True
>
>     def render_GET(self, request):
>         request.responseHeaders.addRawHeader("Content-Type", "text/html;
> charset=utf-8")
>         return b"<html><body>Hello World</body></html>"
>
> if __name__ == '__main__':
>     myContextFactory = ssl.DefaultOpenSSLContextFactory(
>         'serverPrivateKey.pem',
>         'serverCertificate.pem'
>     )
>     ctx = myContextFactory.getContext()
>     #
> https://www.pyopenssl.org/en/stable/api/ssl.html#OpenSSL.SSL.Context.set_verify
>     ctx.set_verify(SSL.VERIFY_PEER, verifyCallback)
>     #
> https://pyopenssl.org/en/stable/api/ssl.html#OpenSSL.SSL.Context.set_tlsext_servername_callback
>     ctx.set_tlsext_servername_callback(sniCallback)
>
>     site = server.Site(MainResource())
>     reactor.listenSSL(443, site, myContextFactory)
>     reactor.run()
> ------
>
> ---testPyOpenSSL.py---
> ### Generate server key material ###
> # openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout
> serverPrivateKey.pem -out serverCertificate.pem -subj
> "/C=''/O=''/OU=''/CN=server"
> ### Generate client key material ###
> # openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout
> clientPrivateKey.pem -out clientCertificate.pem -subj
> "/C=''/O=''/OU=''/CN=client"
> from __future__ import print_function
> import socket, sys, os
> from SocketServer import BaseServer
> from BaseHTTPServer import HTTPServer
> from SimpleHTTPServer import SimpleHTTPRequestHandler
> from OpenSSL import SSL
>
> def verifyCallback(conn, cert, errno, depth, result):
>     print('\'verifyCallback\' called for result ' + str(result))
>     print('\tconn._socket: ' + str(conn._socket))
>     return True
>
> def sniCallback(conn):
>     print('\'sniCallback\' called.')
>     print('\tconn._socket: ' + str(conn._socket))
>     print(type(conn))
>
> class SecureHTTPServer(HTTPServer):
>     def __init__(self, server_address, HandlerClass):
>         BaseServer.__init__(self, server_address, HandlerClass)
>         ctx = SSL.Context(SSL.TLSv1_2_METHOD)
>         ctx.use_privatekey_file('serverPrivateKey.pem')
>         ctx.use_certificate_file('serverCertificate.pem')
>         #
> https://www.pyopenssl.org/en/stable/api/ssl.html#OpenSSL.SSL.Context.set_verify
>         ctx.set_verify(SSL.VERIFY_PEER, verifyCallback)
>         #
> https://pyopenssl.org/en/stable/api/ssl.html#OpenSSL.SSL.Context.set_tlsext_servername_callback
>         ctx.set_tlsext_servername_callback(sniCallback)
>         self.socket = SSL.Connection(ctx,
> socket.socket(self.address_family,self.socket_type))
>         self.server_bind()
>         self.server_activate()
>
>     def shutdown_request(self,request):
>         request.shutdown()
>
> class SecureHTTPRequestHandler(SimpleHTTPRequestHandler):
>     def setup(self):
>         self.connection = self.request
>         self.rfile = socket._fileobject(self.request, "rb", self.rbufsize)
>         self.wfile = socket._fileobject(self.request, "wb", self.wbufsize)
>
>     def do_GET(self):
>         self.send_response(200)
>         SimpleHTTPRequestHandler.end_headers(self)
>         self.wfile.write('<html><body>Hello World</body></html>')
>
> if __name__ == '__main__':
>     ip,port = ('0.0.0.0', 443)
>     httpd = SecureHTTPServer((ip, port), SecureHTTPRequestHandler)
>     httpd.serve_forever()
> ------
>
> (Please note that even though these examples are for Python2 (due to other
> quirks) I am aiming to implement this in Python3.)
>
> Regards,
>
> Arn
> _______________________________________________
> Twisted-Python mailing list
> [email protected]
> https://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-python
>

--000000000000a8458d0591dc412a
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

<div dir=3D"ltr"><div dir=3D"ltr">Perhaps this line from OpenSSL.SSL.Connec=
tion is a clue.<div><br></div><div>&quot;socket may be None; in this case, =
the Connection is created with a memory BIO: see the bio_read(), bio_write(=
), and bio_shutdown() methods.&quot;<div><a href=3D"https://www.pyopenssl.o=
rg/en/stable/api/ssl.html#OpenSSL.SSL.Connection">https://www.pyopenssl.org=
/en/stable/api/ssl.html#OpenSSL.SSL.Connection</a><span style=3D"color:rgb(=
64,64,64);font-family:Lato,proxima-nova,&quot;Helvetica Neue&quot;,Arial,sa=
ns-serif;font-size:16px;background-color:rgb(252,252,252)"><br></span></div=
></div><input name=3D"virtru-metadata" type=3D"hidden" value=3D"{&quot;emai=
l-policy&quot;:{&quot;state&quot;:&quot;closed&quot;,&quot;expirationUnit&q=
uot;:&quot;days&quot;,&quot;disableCopyPaste&quot;:false,&quot;disablePrint=
&quot;:false,&quot;disableForwarding&quot;:false,&quot;enableNoauth&quot;:f=
alse,&quot;persistentProtection&quot;:false,&quot;expandedWatermarking&quot=
;:false,&quot;expires&quot;:false,&quot;isManaged&quot;:false},&quot;attach=
ments&quot;:{},&quot;compose-id&quot;:&quot;1&quot;,&quot;compose-window&qu=
ot;:{&quot;secure&quot;:false}}"></div><br><div class=3D"gmail_quote" style=
=3D""><div dir=3D"ltr" class=3D"gmail_attr">On Wed, Sep 4, 2019 at 1:38 AM =
Arn Vollebregt &lt;<a href=3D"mailto:[email protected]">kpn.arn.=
[email protected]</a>&gt; wrote:<br></div><blockquote class=3D"gmail_quo=
te" style=3D"margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204=
);padding-left:1ex"><div dir=3D"ltr"><div>Hi,</div><div><br></div><div>I no=
ticed that PyOpenSSL SNI callbacks (set with <i>ctx.set_tlsext_servername_c=
allback</i>) receive a <i>OpenSSL.SSL.Connection</i> object within Twisted =
that have an empty <i>_socket</i> property, while this property <u>is</u> a=
ctually set when using <i>PyOpenSSL</i> directly. For my use-case this is a=
 problem as I want to call <i>conn._socket.getpeername()</i> to determine t=
he peer&#39;s IP address. So I am wondering: why is this behaviour differen=
t? And how do I get the peer IP address?</div><div><br></div><div>---consol=
e---</div><div>user:~$ sudo python testTwisted.py &amp;</div><div>[3] 32842=
</div><div>user:~$ curl -s --insecure --key clientPrivateKey.pem --cert cli=
entCertificate.pem <a href=3D"https://127.0.0.1" target=3D"_blank">https://=
127.0.0.1</a> &gt; /dev/null<br>&#39;sniCallback&#39; called.<br>=C2=A0 =C2=
=A0 =C2=A0 =C2=A0 conn._socket: None<br>&#39;verifyCallback&#39; called for=
 result 0<br>=C2=A0 =C2=A0 =C2=A0 =C2=A0 conn._socket: None<br>&#39;verifyC=
allback&#39; called for result 1<br>=C2=A0 =C2=A0 =C2=A0 =C2=A0 conn._socke=
t: None</div><div>user:~$ sudo python testPyOpenSSL.py &amp;<br>[1] 33270</=
div><div>user:~$ curl -s --insecure --key clientPrivateKey.pem --cert clien=
tCertificate.pem <a href=3D"https://127.0.0.1" target=3D"_blank">https://12=
7.0.0.1</a> &gt; /dev/null<br>&#39;sniCallback&#39; called.<br>=C2=A0 =C2=
=A0 =C2=A0 =C2=A0 conn._socket: &lt;socket._socketobject object at 0x7f34c5=
bd3130&gt;<br>&lt;class &#39;OpenSSL.SSL.Connection&#39;&gt;<br>&#39;verify=
Callback&#39; called for result 0<br>=C2=A0 =C2=A0 =C2=A0 =C2=A0 conn._sock=
et: &lt;socket._socketobject object at 0x7f34c5bd3130&gt;<br>&#39;verifyCal=
lback&#39; called for result 1<br>=C2=A0 =C2=A0 =C2=A0 =C2=A0 conn._socket:=
 &lt;socket._socketobject object at 0x7f34c5bd3130&gt;<br>127.0.0.1 - - [29=
/Aug/2019 11:45:47] &quot;GET / HTTP/1.1&quot; 200 -</div><div>------<br></=
div><div><br></div><div>---testTwisted.py---</div><div>### Generate server =
key material ###<br>#
 openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout=20
serverPrivateKey.pem -out serverCertificate.pem -subj=20
&quot;/C=3D&#39;&#39;/O=3D&#39;&#39;/OU=3D&#39;&#39;/CN=3Dserver&quot;<br>#=
## Generate client key material ###<br>#
 openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout=20
clientPrivateKey.pem -out clientCertificate.pem -subj=20
&quot;/C=3D&#39;&#39;/O=3D&#39;&#39;/OU=3D&#39;&#39;/CN=3Dclient&quot;<br>f=
rom __future__ import print_function<br>#<a href=3D"https://twistedmatrix.c=
om/documents/12.0.0/core/howto/ssl.html" target=3D"_blank">https://twistedm=
atrix.com/documents/12.0.0/core/howto/ssl.html</a><br>from OpenSSL import S=
SL<br>from twisted.internet import ssl, reactor<br>from twisted.web import =
server, resource<br>from twisted.internet.protocol import Factory, Protocol=
<br><br>def verifyCallback(conn, cert, errno, depth, result):<br>=C2=A0 =C2=
=A0 print(&#39;\&#39;verifyCallback\&#39; called for result &#39; + str(res=
ult))<br>=C2=A0 =C2=A0 print(&#39;\tconn._socket: &#39; + str(conn._socket)=
)<br>=C2=A0 =C2=A0 return True<br><br>def sniCallback(conn):<br>=C2=A0 =C2=
=A0 print(&#39;\&#39;sniCallback\&#39; called.&#39;)<br>=C2=A0 =C2=A0 print=
(&#39;\tconn._socket: &#39; + str(conn._socket))<br><br>class MainResource(=
resource.Resource):<br>=C2=A0 =C2=A0 isLeaf =3D True<br><br>=C2=A0 =C2=A0 d=
ef render_GET(self, request):<br>=C2=A0 =C2=A0 =C2=A0 =C2=A0 request.respon=
seHeaders.addRawHeader(&quot;Content-Type&quot;, &quot;text/html; charset=
=3Dutf-8&quot;)<br>=C2=A0 =C2=A0 =C2=A0 =C2=A0 return b&quot;&lt;html&gt;&l=
t;body&gt;Hello World&lt;/body&gt;&lt;/html&gt;&quot;<br><br>if __name__ =
=3D=3D &#39;__main__&#39;:<br>=C2=A0 =C2=A0 myContextFactory =3D ssl.Defaul=
tOpenSSLContextFactory(<br>=C2=A0 =C2=A0 =C2=A0 =C2=A0 &#39;serverPrivateKe=
y.pem&#39;,<br>=C2=A0 =C2=A0 =C2=A0 =C2=A0 &#39;serverCertificate.pem&#39;<=
br>=C2=A0 =C2=A0 )<br>=C2=A0 =C2=A0 ctx =3D myContextFactory.getContext()<b=
r>=C2=A0 =C2=A0 # <a href=3D"https://www.pyopenssl.org/en/stable/api/ssl.ht=
ml#OpenSSL.SSL.Context.set_verify" target=3D"_blank">https://www.pyopenssl.=
org/en/stable/api/ssl.html#OpenSSL.SSL.Context.set_verify</a><br>=C2=A0 =C2=
=A0 ctx.set_verify(SSL.VERIFY_PEER, verifyCallback)<br>=C2=A0 =C2=A0 # <a h=
ref=3D"https://pyopenssl.org/en/stable/api/ssl.html#OpenSSL.SSL.Context.set=
_tlsext_servername_callback" target=3D"_blank">https://pyopenssl.org/en/sta=
ble/api/ssl.html#OpenSSL.SSL.Context.set_tlsext_servername_callback</a><br>=
=C2=A0 =C2=A0 ctx.set_tlsext_servername_callback(sniCallback)<br><br>=C2=A0=
 =C2=A0 site =3D server.Site(MainResource())<br>=C2=A0 =C2=A0 reactor.liste=
nSSL(443, site, myContextFactory)<br>=C2=A0 =C2=A0 reactor.run()</div><div>=
------</div><div><br></div><div>---testPyOpenSSL.py---</div><div>### Genera=
te server key material ###<br>#
 openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout=20
serverPrivateKey.pem -out serverCertificate.pem -subj=20
&quot;/C=3D&#39;&#39;/O=3D&#39;&#39;/OU=3D&#39;&#39;/CN=3Dserver&quot;<br>#=
## Generate client key material ###<br>#
 openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout=20
clientPrivateKey.pem -out clientCertificate.pem -subj=20
&quot;/C=3D&#39;&#39;/O=3D&#39;&#39;/OU=3D&#39;&#39;/CN=3Dclient&quot;<br>f=
rom __future__ import print_function<br>import socket, sys, os<br>from Sock=
etServer import BaseServer<br>from BaseHTTPServer import HTTPServer<br>from=
 SimpleHTTPServer import SimpleHTTPRequestHandler<br>from OpenSSL import SS=
L<br><br>def verifyCallback(conn, cert, errno, depth, result):<br>=C2=A0 =
=C2=A0 print(&#39;\&#39;verifyCallback\&#39; called for result &#39; + str(=
result))<br>=C2=A0 =C2=A0 print(&#39;\tconn._socket: &#39; + str(conn._sock=
et))<br>=C2=A0 =C2=A0 return True<br><br>def sniCallback(conn):<br>=C2=A0 =
=C2=A0 print(&#39;\&#39;sniCallback\&#39; called.&#39;)<br>=C2=A0 =C2=A0 pr=
int(&#39;\tconn._socket: &#39; + str(conn._socket))<br>=C2=A0 =C2=A0 print(=
type(conn))<br><br>class SecureHTTPServer(HTTPServer):<br>=C2=A0 =C2=A0 def=
 __init__(self, server_address, HandlerClass):<br>=C2=A0 =C2=A0 =C2=A0 =C2=
=A0 BaseServer.__init__(self, server_address, HandlerClass)<br>=C2=A0 =C2=
=A0 =C2=A0 =C2=A0 ctx =3D SSL.Context(SSL.TLSv1_2_METHOD)<br>=C2=A0 =C2=A0 =
=C2=A0 =C2=A0 ctx.use_privatekey_file(&#39;serverPrivateKey.pem&#39;)<br>=
=C2=A0 =C2=A0 =C2=A0 =C2=A0 ctx.use_certificate_file(&#39;serverCertificate=
.pem&#39;)<br>=C2=A0 =C2=A0 =C2=A0 =C2=A0 # <a href=3D"https://www.pyopenss=
l.org/en/stable/api/ssl.html#OpenSSL.SSL.Context.set_verify" target=3D"_bla=
nk">https://www.pyopenssl.org/en/stable/api/ssl.html#OpenSSL.SSL.Context.se=
t_verify</a><br>=C2=A0 =C2=A0 =C2=A0 =C2=A0 ctx.set_verify(SSL.VERIFY_PEER,=
 verifyCallback)<br>=C2=A0 =C2=A0 =C2=A0 =C2=A0 # <a href=3D"https://pyopen=
ssl.org/en/stable/api/ssl.html#OpenSSL.SSL.Context.set_tlsext_servername_ca=
llback" target=3D"_blank">https://pyopenssl.org/en/stable/api/ssl.html#Open=
SSL.SSL.Context.set_tlsext_servername_callback</a><br>=C2=A0 =C2=A0 =C2=A0 =
=C2=A0 ctx.set_tlsext_servername_callback(sniCallback)<br>=C2=A0 =C2=A0 =C2=
=A0 =C2=A0 self.socket =3D SSL.Connection(ctx, socket.socket(self.address_f=
amily,self.socket_type))<br>=C2=A0 =C2=A0 =C2=A0 =C2=A0 self.server_bind()<=
br>=C2=A0 =C2=A0 =C2=A0 =C2=A0 self.server_activate()<br>=C2=A0 =C2=A0 <br>=
=C2=A0 =C2=A0 def shutdown_request(self,request):<br>=C2=A0 =C2=A0 =C2=A0 =
=C2=A0 request.shutdown()<br><br>class SecureHTTPRequestHandler(SimpleHTTPR=
equestHandler):<br>=C2=A0 =C2=A0 def setup(self):<br>=C2=A0 =C2=A0 =C2=A0 =
=C2=A0 self.connection =3D self.request<br>=C2=A0 =C2=A0 =C2=A0 =C2=A0 self=
.rfile =3D socket._fileobject(self.request, &quot;rb&quot;, self.rbufsize)<=
br>=C2=A0 =C2=A0 =C2=A0 =C2=A0 self.wfile =3D socket._fileobject(self.reque=
st, &quot;wb&quot;, self.wbufsize)<br>=C2=A0 =C2=A0 <br>=C2=A0 =C2=A0 def d=
o_GET(self):<br>=C2=A0 =C2=A0 =C2=A0 =C2=A0 self.send_response(200)<br>=C2=
=A0 =C2=A0 =C2=A0 =C2=A0 SimpleHTTPRequestHandler.end_headers(self)<br>=C2=
=A0 =C2=A0 =C2=A0 =C2=A0 self.wfile.write(&#39;&lt;html&gt;&lt;body&gt;Hell=
o World&lt;/body&gt;&lt;/html&gt;&#39;)<br><br>if __name__ =3D=3D &#39;__ma=
in__&#39;:<br>=C2=A0 =C2=A0 ip,port =3D (&#39;0.0.0.0&#39;, 443)<br>=C2=A0 =
=C2=A0 httpd =3D SecureHTTPServer((ip, port), SecureHTTPRequestHandler)<br>=
=C2=A0 =C2=A0 httpd.serve_forever()</div><div>------</div><div><br></div><d=
iv>(Please note that even though these examples are for Python2 (due to oth=
er quirks) I am aiming to implement this in Python3.)<br></div><div><br></d=
iv><div>Regards,</div><div><br></div><div>Arn</div></div>
_______________________________________________<br>
Twisted-Python mailing list<br>
<a href=3D"mailto:[email protected]" target=3D"_blank">Twist=
[email protected]</a><br>
<a href=3D"https://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-pytho=
n" rel=3D"noreferrer" target=3D"_blank">https://twistedmatrix.com/cgi-bin/m=
ailman/listinfo/twisted-python</a><br>
</blockquote></div></div>

--000000000000a8458d0591dc412a--

--===============1979704781669944454==
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: base64
Content-Disposition: inline

X19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX18KVHdpc3RlZC1Q
eXRob24gbWFpbGluZyBsaXN0ClR3aXN0ZWQtUHl0aG9uQHR3aXN0ZWRtYXRyaXguY29tCmh0dHBz
Oi8vdHdpc3RlZG1hdHJpeC5jb20vY2dpLWJpbi9tYWlsbWFuL2xpc3RpbmZvL3R3aXN0ZWQtcHl0
aG9uCg==

--===============1979704781669944454==--