Merge lp:~exarkun/pyopenssl/mem-bio into lp:~exarkun/pyopenssl/trunk

Proposed by Jean-Paul Calderone
Status: Merged
Merged at revision: not available
Proposed branch: lp:~exarkun/pyopenssl/mem-bio
Merge into: lp:~exarkun/pyopenssl/trunk
Diff against target: None lines
To merge this branch: bzr merge lp:~exarkun/pyopenssl/mem-bio
Reviewer Review Type Date Requested Status
Jean-Paul Calderone Pending
Review via email: mp+6225@code.launchpad.net
To post a comment you must log in.
lp:~exarkun/pyopenssl/mem-bio updated
110. By Jean-Paul Calderone

Make bio_shutdown raise an exception when called on a socket-based Connection

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'doc/pyOpenSSL.tex'
2--- doc/pyOpenSSL.tex 2009-04-25 12:30:11 +0000
3+++ doc/pyOpenSSL.tex 2009-05-03 23:45:07 +0000
4@@ -681,10 +681,12 @@
5 \end{datadesc}
6
7 \begin{funcdesc}{Connection}{context, socket}
8-Factory fucnction that creates a new Connection object given an SSL context and
9+Factory function that creates a new Connection object given an SSL context and
10 a socket \footnote{Actually, all that is required is an object that
11-\emph{behaves} like a socket, you could even use files, even though it'd be
12-tricky to get the handshakes right!} object.
13+ \emph{behaves} like a socket, you could even use files, even though it'd be
14+ tricky to get the handshakes right!} object. \var{socket} may be \var{None};
15+in this case, the Connection is created with a memory BIO: see the
16+\method{bio_read}, \method{bio_write}, and \method{bio_shutdown} methods.
17 \end{funcdesc}
18
19 \begin{excdesc}{Error}
20@@ -978,6 +980,12 @@
21 by \var{bufsize}.
22 \end{methoddesc}
23
24+\begin{methoddesc}[Connection]{bio_write}{bytes}
25+If the Connection was created with a memory BIO, this method can be used to add
26+bytes to the read end of that memory BIO. The Connection can then read the
27+bytes (for example, in response to a call to \method{recv}).
28+\end{methoddesc}
29+
30 \begin{methoddesc}[Connection]{renegotiate}{}
31 Renegotiate the SSL session. Call this if you wish to change cipher suites or
32 anything like that.
33@@ -987,6 +995,13 @@
34 Send the \var{string} data to the Connection.
35 \end{methoddesc}
36
37+\begin{methoddesc}[Connection]{bio_read}{bufsize}
38+If the Connection was created with a memory BIO, this method can be used to
39+read bytes from the write end of that memory BIO. Many Connection methods will
40+add bytes which must be read in this manner or the buffer will eventually fill
41+up and the Connection will be able to take no further actions.
42+\end{methoddesc}
43+
44 \begin{methoddesc}[Connection]{sendall}{string}
45 Send all of the \var{string} data to the Connection. This calls \method{send}
46 repeatedly until all data is sent. If an error occurs, it's impossible to tell
47@@ -1037,10 +1052,28 @@
48 Call the \method{shutdown} method of the underlying socket.
49 \end{methoddesc}
50
51+\begin{methoddesc}[Connection]{bio_shutdown}{}
52+If the Connection was created with a memory BIO, this method can be used to
53+indicate that ``end of file'' has been reached on the read end of that memory
54+BIO.
55+\end{methoddesc}
56+
57 \begin{methoddesc}[Connection]{state_string}{}
58 Retrieve a verbose string detailing the state of the Connection.
59 \end{methoddesc}
60
61+\begin{methoddesc}[Connection]{client_random}{}
62+Retrieve the random value used with the client hello message.
63+\end{methoddesc}
64+
65+\begin{methoddesc}[Connection]{server_random}{}
66+Retrieve the random value used with the server hello message.
67+\end{methoddesc}
68+
69+\begin{methoddesc}[Connection]{master_key}{}
70+Retrieve the value of the master key for this session.
71+\end{methoddesc}
72+
73 \begin{methoddesc}[Connection]{want_read}{}
74 Checks if more data has to be read from the transport layer to complete an
75 operation.
76
77=== modified file 'src/ssl/connection.c'
78--- src/ssl/connection.c 2008-03-21 22:31:12 +0000
79+++ src/ssl/connection.c 2009-05-01 00:24:35 +0000
80@@ -23,8 +23,8 @@
81 #endif
82
83 #define SSL_MODULE
84+#include <openssl/bio.h>
85 #include <openssl/err.h>
86-
87 #include "ssl.h"
88
89 /**
90@@ -125,6 +125,50 @@
91 }
92
93 /*
94+ * Handle errors raised by BIO functions.
95+ *
96+ * Arguments: bio - The BIO object
97+ * ret - The return value of the BIO_ function.
98+ * Returns: None, the calling function should return NULL;
99+ */
100+static void
101+handle_bio_errors(BIO* bio, int ret)
102+{
103+ if (BIO_should_retry(bio)) {
104+ if (BIO_should_read(bio)) {
105+ PyErr_SetNone(ssl_WantReadError);
106+ } else if (BIO_should_write(bio)) {
107+ PyErr_SetNone(ssl_WantWriteError);
108+ } else if (BIO_should_io_special(bio)) {
109+ /*
110+ * It's somewhat unclear what this means. From the OpenSSL source,
111+ * it seems like it should not be triggered by the memory BIO, so
112+ * for the time being, this case shouldn't come up. The SSL BIO
113+ * (which I think should be named the socket BIO) may trigger this
114+ * case if its socket is not yet connected or it is busy doing
115+ * something related to x509.
116+ */
117+ PyErr_SetString(PyExc_ValueError, "BIO_should_io_special");
118+ } else {
119+ /*
120+ * I hope this is dead code. The BIO documentation suggests that
121+ * one of the above three checks should always be true.
122+ */
123+ PyErr_SetString(PyExc_ValueError, "unknown bio failure");
124+ }
125+ } else {
126+ /*
127+ * If we aren't to retry, it's really an error, so fall back to the
128+ * normal error reporting code. However, the BIO interface does not
129+ * specify a uniform error reporting mechanism. We can only hope that
130+ * the code which triggered the error also kindly pushed something onto
131+ * the error stack.
132+ */
133+ exception_from_error_queue();
134+ }
135+}
136+
137+/*
138 * Handle errors raised by SSL I/O functions. NOTE: Not SSL_shutdown ;)
139 *
140 * Arguments: ssl - The SSL object
141@@ -239,6 +283,49 @@
142 return PyInt_FromLong((long)ret);
143 }
144
145+static char ssl_Connection_bio_write_doc[] = "\n\
146+When using non-socket connections this function sends\n\
147+\"dirty\" data that would have traveled in on the network.\n\
148+\n\
149+Arguments: self - The Connection object\n\
150+ args - The Python argument tuple, should be:\n\
151+ buf - The string to bio_write\n\
152+Returns: The number of bytes written\n\
153+";
154+static PyObject *
155+ssl_Connection_bio_write(ssl_ConnectionObj *self, PyObject *args)
156+{
157+ char *buf;
158+ int len, ret;
159+
160+ if(self->into_ssl == NULL)
161+ {
162+ PyErr_SetString(PyExc_TypeError, "Connection sock was not None");
163+ return NULL;
164+ }
165+
166+ if (!PyArg_ParseTuple(args, "s#|i:bio_write", &buf, &len))
167+ return NULL;
168+
169+ ret = BIO_write(self->into_ssl, buf, len);
170+
171+ if (PyErr_Occurred())
172+ {
173+ flush_error_queue();
174+ return NULL;
175+ }
176+
177+ if (ret <= 0) {
178+ /*
179+ * There was a problem with the BIO_write of some sort.
180+ */
181+ handle_bio_errors(self->into_ssl, ret);
182+ return NULL;
183+ }
184+
185+ return PyInt_FromLong((long)ret);
186+}
187+
188 static char ssl_Connection_send_doc[] = "\n\
189 Send data on the connection. NOTE: If you get one of the WantRead,\n\
190 WantWrite or WantX509Lookup exceptions on this, you have to call the\n\
191@@ -384,6 +471,63 @@
192 }
193 }
194
195+static char ssl_Connection_bio_read_doc[] = "\n\
196+When using non-socket connections this function reads\n\
197+the \"dirty\" data that would have traveled away on the network.\n\
198+\n\
199+Arguments: self - The Connection object\n\
200+ args - The Python argument tuple, should be:\n\
201+ bufsiz - The maximum number of bytes to read\n\
202+Returns: The string read.\n\
203+";
204+static PyObject *
205+ssl_Connection_bio_read(ssl_ConnectionObj *self, PyObject *args)
206+{
207+ int bufsiz, ret;
208+ PyObject *buf;
209+
210+ if(self->from_ssl == NULL)
211+ {
212+ PyErr_SetString(PyExc_TypeError, "Connection sock was not None");
213+ return NULL;
214+ }
215+
216+ if (!PyArg_ParseTuple(args, "i:bio_read", &bufsiz))
217+ return NULL;
218+
219+ buf = PyString_FromStringAndSize(NULL, bufsiz);
220+ if (buf == NULL)
221+ return NULL;
222+
223+ ret = BIO_read(self->from_ssl, PyString_AsString(buf), bufsiz);
224+
225+ if (PyErr_Occurred())
226+ {
227+ Py_DECREF(buf);
228+ flush_error_queue();
229+ return NULL;
230+ }
231+
232+ if (ret <= 0) {
233+ /*
234+ * There was a problem with the BIO_read of some sort.
235+ */
236+ handle_bio_errors(self->from_ssl, ret);
237+ Py_DECREF(buf);
238+ return NULL;
239+ }
240+
241+ /*
242+ * Shrink the string to match the number of bytes we actually read.
243+ */
244+ if (ret != bufsiz && _PyString_Resize(&buf, ret) < 0)
245+ {
246+ Py_DECREF(buf);
247+ return NULL;
248+ }
249+ return buf;
250+}
251+
252 static char ssl_Connection_renegotiate_doc[] = "\n\
253 Renegotiate the session\n\
254 \n\
255@@ -626,6 +770,25 @@
256 return tuple;
257 }
258
259+static char ssl_Connection_bio_shutdown_doc[] = "\n\
260+When using non-socket connections this function signals end of\n\
261+data on the input for this connection.\n\
262+\n\
263+Arguments: self - The Connection object\n\
264+ args - The Python argument tuple, should be empty.\n\
265+Returns: Nothing\n\
266+";
267+
268+static PyObject *
269+ssl_Connection_bio_shutdown(ssl_ConnectionObj *self, PyObject *args)
270+{
271+ BIO_set_mem_eof_return(self->into_ssl, 0);
272+ Py_INCREF(Py_None);
273+ return Py_None;
274+}
275+
276+
277+
278 static char ssl_Connection_shutdown_doc[] = "\n\
279 Send closure alert\n\
280 \n\
281@@ -810,6 +973,66 @@
282 return PyString_FromString(SSL_state_string_long(self->ssl));
283 }
284
285+static char ssl_Connection_client_random_doc[] = "\n\
286+Get a copy of the client hello nonce.\n\
287+\n\
288+Arguments: self - The Connection object\n\
289+ args - The Python argument tuple, should be empty\n\
290+Returns: A string representing the state\n\
291+";
292+static PyObject *
293+ssl_Connection_client_random(ssl_ConnectionObj *self, PyObject *args)
294+{
295+ if (!PyArg_ParseTuple(args, ":client_random"))
296+ return NULL;
297+
298+ if( self->ssl->session == NULL) {
299+ Py_INCREF(Py_None);
300+ return Py_None;
301+ }
302+ return PyString_FromStringAndSize( (const char *) self->ssl->s3->client_random, SSL3_RANDOM_SIZE);
303+}
304+
305+static char ssl_Connection_server_random_doc[] = "\n\
306+Get a copy of the server hello nonce.\n\
307+\n\
308+Arguments: self - The Connection object\n\
309+ args - The Python argument tuple, should be empty\n\
310+Returns: A string representing the state\n\
311+";
312+static PyObject *
313+ssl_Connection_server_random(ssl_ConnectionObj *self, PyObject *args)
314+{
315+ if (!PyArg_ParseTuple(args, ":server_random"))
316+ return NULL;
317+
318+ if( self->ssl->session == NULL) {
319+ Py_INCREF(Py_None);
320+ return Py_None;
321+ }
322+ return PyString_FromStringAndSize( (const char *) self->ssl->s3->server_random, SSL3_RANDOM_SIZE);
323+}
324+
325+static char ssl_Connection_master_key_doc[] = "\n\
326+Get a copy of the master key.\n\
327+\n\
328+Arguments: self - The Connection object\n\
329+ args - The Python argument tuple, should be empty\n\
330+Returns: A string representing the state\n\
331+";
332+static PyObject *
333+ssl_Connection_master_key(ssl_ConnectionObj *self, PyObject *args)
334+{
335+ if (!PyArg_ParseTuple(args, ":master_key"))
336+ return NULL;
337+
338+ if( self->ssl->session == NULL) {
339+ Py_INCREF(Py_None);
340+ return Py_None;
341+ }
342+ return PyString_FromStringAndSize( (const char *) self->ssl->session->master_key, self->ssl->session->master_key_length);
343+}
344+
345 static char ssl_Connection_sock_shutdown_doc[] = "\n\
346 See shutdown(2)\n\
347 \n\
348@@ -912,6 +1135,8 @@
349 ADD_METHOD(sendall),
350 ADD_METHOD(recv),
351 ADD_ALIAS (read, recv),
352+ ADD_METHOD(bio_read),
353+ ADD_METHOD(bio_write),
354 ADD_METHOD(renegotiate),
355 ADD_METHOD(do_handshake),
356 #if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x00907000L
357@@ -921,6 +1146,7 @@
358 ADD_METHOD(connect),
359 ADD_METHOD(connect_ex),
360 ADD_METHOD(accept),
361+ ADD_METHOD(bio_shutdown),
362 ADD_METHOD(shutdown),
363 ADD_METHOD(get_cipher_list),
364 ADD_METHOD(makefile),
365@@ -929,6 +1155,9 @@
366 ADD_METHOD(get_shutdown),
367 ADD_METHOD(set_shutdown),
368 ADD_METHOD(state_string),
369+ ADD_METHOD(server_random),
370+ ADD_METHOD(client_random),
371+ ADD_METHOD(master_key),
372 ADD_METHOD(sock_shutdown),
373 ADD_METHOD(get_peer_certificate),
374 ADD_METHOD(want_read),
375@@ -965,26 +1194,50 @@
376 self->socket = sock;
377
378 self->ssl = NULL;
379+ self->from_ssl = NULL;
380+ self->into_ssl = NULL;
381
382 Py_INCREF(Py_None);
383 self->app_data = Py_None;
384
385 self->tstate = NULL;
386
387- fd = PyObject_AsFileDescriptor(self->socket);
388- if (fd < 0)
389- {
390- Py_DECREF(self);
391- return NULL;
392- }
393-
394 self->ssl = SSL_new(self->context->ctx);
395 SSL_set_app_data(self->ssl, self);
396- SSL_set_fd(self->ssl, (SOCKET_T)fd);
397+
398+ if(self->socket == Py_None)
399+ {
400+ /* If it's not a socket or file, treat it like a memory buffer,
401+ * so crazy people can do things like EAP-TLS. */
402+ self->into_ssl = BIO_new(BIO_s_mem());
403+ self->from_ssl = BIO_new(BIO_s_mem());
404+ if(self->into_ssl == NULL || self->from_ssl == NULL)
405+ goto error;
406+ SSL_set_bio(self->ssl, self->into_ssl, self->from_ssl);
407+ }
408+ else
409+ {
410+ fd = PyObject_AsFileDescriptor(self->socket);
411+ if (fd < 0)
412+ {
413+ Py_DECREF(self);
414+ return NULL;
415+ }
416+ else
417+ {
418+ SSL_set_fd(self->ssl, (SOCKET_T)fd);
419+ }
420+ }
421
422 PyObject_GC_Track(self);
423
424 return self;
425+
426+error:
427+ BIO_free(self->into_ssl); /* NULL safe */
428+ BIO_free(self->from_ssl); /* NULL safe */
429+ Py_DECREF(self);
430+ return NULL;
431 }
432
433 /*
434@@ -1050,6 +1303,8 @@
435 self->socket = NULL;
436 Py_XDECREF(self->app_data);
437 self->app_data = NULL;
438+ self->into_ssl = NULL; /* was cleaned up by SSL_free() */
439+ self->from_ssl = NULL; /* was cleaned up by SSL_free() */
440 return 0;
441 }
442
443
444=== modified file 'src/ssl/connection.h'
445--- src/ssl/connection.h 2008-09-21 21:42:34 +0000
446+++ src/ssl/connection.h 2009-04-01 19:09:23 +0000
447@@ -44,6 +44,7 @@
448 PyObject *socket;
449 PyThreadState *tstate; /* This field is no longer used. */
450 PyObject *app_data;
451+ BIO *into_ssl, *from_ssl; /* for connections without file descriptors */
452 } ssl_ConnectionObj;
453
454
455
456=== modified file 'test/test_crypto.py'
457--- test/test_crypto.py 2009-04-01 18:42:32 +0000
458+++ test/test_crypto.py 2009-05-04 22:27:52 +0000
459@@ -732,7 +732,7 @@
460 Run the command line openssl tool with the given arguments and write
461 the given PEM to its stdin.
462 """
463- write, read = popen2(" ".join(("openssl",) + args))
464+ write, read = popen2(" ".join(("openssl",) + args), "b")
465 write.write(pem)
466 write.close()
467 return read.read()
468
469=== modified file 'test/test_ssl.py'
470--- test/test_ssl.py 2009-04-01 16:34:06 +0000
471+++ test/test_ssl.py 2009-05-01 00:24:35 +0000
472@@ -15,12 +15,13 @@
473 from twisted.trial.unittest import TestCase
474 except ImportError:
475 # Fall back to the stdlib TestCase though, since it kind of works.
476- from unittest import TestCase, main
477+ from unittest import TestCase
478
479 from OpenSSL.crypto import TYPE_RSA, FILETYPE_PEM, PKey, dump_privatekey, load_certificate, load_privatekey
480 from OpenSSL.SSL import WantReadError, Context, Connection, Error
481 from OpenSSL.SSL import SSLv2_METHOD, SSLv3_METHOD, SSLv23_METHOD, TLSv1_METHOD
482-from OpenSSL.SSL import VERIFY_PEER
483+from OpenSSL.SSL import OP_NO_SSLv2, OP_NO_SSLv3, OP_SINGLE_DH_USE
484+from OpenSSL.SSL import VERIFY_PEER, VERIFY_FAIL_IF_NO_PEER_CERT, VERIFY_CLIENT_ONCE
485 from OpenSSL.test.test_crypto import _Python23TestCaseHelper, cleartextCertificatePEM, cleartextPrivateKeyPEM
486 try:
487 from OpenSSL.SSL import OP_NO_QUERY_MTU
488@@ -313,6 +314,286 @@
489 "OP_NO_TICKET unavailable - OpenSSL version may be too old"
490
491
492-if __name__ == '__main__':
493- main()
494-
495+
496+root_cert_pem = """-----BEGIN CERTIFICATE-----
497+MIIC7TCCAlagAwIBAgIIPQzE4MbeufQwDQYJKoZIhvcNAQEFBQAwWDELMAkGA1UE
498+BhMCVVMxCzAJBgNVBAgTAklMMRAwDgYDVQQHEwdDaGljYWdvMRAwDgYDVQQKEwdU
499+ZXN0aW5nMRgwFgYDVQQDEw9UZXN0aW5nIFJvb3QgQ0EwIhgPMjAwOTAzMjUxMjM2
500+NThaGA8yMDE3MDYxMTEyMzY1OFowWDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAklM
501+MRAwDgYDVQQHEwdDaGljYWdvMRAwDgYDVQQKEwdUZXN0aW5nMRgwFgYDVQQDEw9U
502+ZXN0aW5nIFJvb3QgQ0EwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAPmaQumL
503+urpE527uSEHdL1pqcDRmWzu+98Y6YHzT/J7KWEamyMCNZ6fRW1JCR782UQ8a07fy
504+2xXsKy4WdKaxyG8CcatwmXvpvRQ44dSANMihHELpANTdyVp6DCysED6wkQFurHlF
505+1dshEaJw8b/ypDhmbVIo6Ci1xvCJqivbLFnbAgMBAAGjgbswgbgwHQYDVR0OBBYE
506+FINVdy1eIfFJDAkk51QJEo3IfgSuMIGIBgNVHSMEgYAwfoAUg1V3LV4h8UkMCSTn
507+VAkSjch+BK6hXKRaMFgxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJJTDEQMA4GA1UE
508+BxMHQ2hpY2FnbzEQMA4GA1UEChMHVGVzdGluZzEYMBYGA1UEAxMPVGVzdGluZyBS
509+b290IENBggg9DMTgxt659DAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GB
510+AGGCDazMJGoWNBpc03u6+smc95dEead2KlZXBATOdFT1VesY3+nUOqZhEhTGlDMi
511+hkgaZnzoIq/Uamidegk4hirsCT/R+6vsKAAxNTcBjUeZjlykCJWy5ojShGftXIKY
512+w/njVbKMXrvc83qmTdGl3TAM0fxQIpqgcglFLveEBgzn
513+-----END CERTIFICATE-----
514+"""
515+
516+root_key_pem = """-----BEGIN RSA PRIVATE KEY-----
517+MIICXQIBAAKBgQD5mkLpi7q6ROdu7khB3S9aanA0Zls7vvfGOmB80/yeylhGpsjA
518+jWen0VtSQke/NlEPGtO38tsV7CsuFnSmschvAnGrcJl76b0UOOHUgDTIoRxC6QDU
519+3claegwsrBA+sJEBbqx5RdXbIRGicPG/8qQ4Zm1SKOgotcbwiaor2yxZ2wIDAQAB
520+AoGBAPCgMpmLxzwDaUmcFbTJUvlLW1hoxNNYSu2jIZm1k/hRAcE60JYwvBkgz3UB
521+yMEh0AtLxYe0bFk6EHah11tMUPgscbCq73snJ++8koUw+csk22G65hOs51bVb7Aa
522+6JBe67oLzdtvgCUFAA2qfrKzWRZzAdhUirQUZgySZk+Xq1pBAkEA/kZG0A6roTSM
523+BVnx7LnPfsycKUsTumorpXiylZJjTi9XtmzxhrYN6wgZlDOOwOLgSQhszGpxVoMD
524+u3gByT1b2QJBAPtL3mSKdvwRu/+40zaZLwvSJRxaj0mcE4BJOS6Oqs/hS1xRlrNk
525+PpQ7WJ4yM6ZOLnXzm2mKyxm50Mv64109FtMCQQDOqS2KkjHaLowTGVxwC0DijMfr
526+I9Lf8sSQk32J5VWCySWf5gGTfEnpmUa41gKTMJIbqZZLucNuDcOtzUaeWZlZAkA8
527+ttXigLnCqR486JDPTi9ZscoZkZ+w7y6e/hH8t6d5Vjt48JVyfjPIaJY+km58LcN3
528+6AWSeGAdtRFHVzR7oHjVAkB4hutvxiOeiIVQNBhM6RSI9aBPMI21DoX2JRoxvNW2
529+cbvAhow217X9V0dVerEOKxnNYspXRrh36h7k4mQA+sDq
530+-----END RSA PRIVATE KEY-----
531+"""
532+
533+server_cert_pem = """-----BEGIN CERTIFICATE-----
534+MIICKDCCAZGgAwIBAgIJAJn/HpR21r/8MA0GCSqGSIb3DQEBBQUAMFgxCzAJBgNV
535+BAYTAlVTMQswCQYDVQQIEwJJTDEQMA4GA1UEBxMHQ2hpY2FnbzEQMA4GA1UEChMH
536+VGVzdGluZzEYMBYGA1UEAxMPVGVzdGluZyBSb290IENBMCIYDzIwMDkwMzI1MTIz
537+NzUzWhgPMjAxNzA2MTExMjM3NTNaMBgxFjAUBgNVBAMTDWxvdmVseSBzZXJ2ZXIw
538+gZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAL6m+G653V0tpBC/OKl22VxOi2Cv
539+lK4TYu9LHSDP9uDVTe7V5D5Tl6qzFoRRx5pfmnkqT5B+W9byp2NU3FC5hLm5zSAr
540+b45meUhjEJ/ifkZgbNUjHdBIGP9MAQUHZa5WKdkGIJvGAvs8UzUqlr4TBWQIB24+
541+lJ+Ukk/CRgasrYwdAgMBAAGjNjA0MB0GA1UdDgQWBBS4kC7Ij0W1TZXZqXQFAM2e
542+gKEG2DATBgNVHSUEDDAKBggrBgEFBQcDATANBgkqhkiG9w0BAQUFAAOBgQBh30Li
543+dJ+NlxIOx5343WqIBka3UbsOb2kxWrbkVCrvRapCMLCASO4FqiKWM+L0VDBprqIp
544+2mgpFQ6FHpoIENGvJhdEKpptQ5i7KaGhnDNTfdy3x1+h852G99f1iyj0RmbuFcM8
545+uzujnS8YXWvM7DM1Ilozk4MzPug8jzFp5uhKCQ==
546+-----END CERTIFICATE-----
547+"""
548+
549+server_key_pem = """-----BEGIN RSA PRIVATE KEY-----
550+MIICWwIBAAKBgQC+pvhuud1dLaQQvzipdtlcTotgr5SuE2LvSx0gz/bg1U3u1eQ+
551+U5eqsxaEUceaX5p5Kk+QflvW8qdjVNxQuYS5uc0gK2+OZnlIYxCf4n5GYGzVIx3Q
552+SBj/TAEFB2WuVinZBiCbxgL7PFM1Kpa+EwVkCAduPpSflJJPwkYGrK2MHQIDAQAB
553+AoGAbwuZ0AR6JveahBaczjfnSpiFHf+mve2UxoQdpyr6ROJ4zg/PLW5K/KXrC48G
554+j6f3tXMrfKHcpEoZrQWUfYBRCUsGD5DCazEhD8zlxEHahIsqpwA0WWssJA2VOLEN
555+j6DuV2pCFbw67rfTBkTSo32ahfXxEKev5KswZk0JIzH3ooECQQDgzS9AI89h0gs8
556+Dt+1m11Rzqo3vZML7ZIyGApUzVan+a7hbc33nbGRkAXjHaUBJO31it/H6dTO+uwX
557+msWwNG5ZAkEA2RyFKs5xR5USTFaKLWCgpH/ydV96KPOpBND7TKQx62snDenFNNbn
558+FwwOhpahld+vqhYk+pfuWWUpQciE+Bu7ZQJASjfT4sQv4qbbKK/scePicnDdx9th
559+4e1EeB9xwb+tXXXUo/6Bor/AcUNwfiQ6Zt9PZOK9sR3lMZSsP7rMi7kzuQJABie6
560+1sXXjFH7nNJvRG4S39cIxq8YRYTy68II/dlB2QzGpKxV/POCxbJ/zu0CU79tuYK7
561+NaeNCFfH3aeTrX0LyQJAMBWjWmeKM2G2sCExheeQK0ROnaBC8itCECD4Jsve4nqf
562+r50+LF74iLXFwqysVCebPKMOpDWp/qQ1BbJQIPs7/A==
563+-----END RSA PRIVATE KEY-----
564+"""
565+
566+client_cert_pem = """-----BEGIN CERTIFICATE-----
567+MIICJjCCAY+gAwIBAgIJAKxpFI5lODkjMA0GCSqGSIb3DQEBBQUAMFgxCzAJBgNV
568+BAYTAlVTMQswCQYDVQQIEwJJTDEQMA4GA1UEBxMHQ2hpY2FnbzEQMA4GA1UEChMH
569+VGVzdGluZzEYMBYGA1UEAxMPVGVzdGluZyBSb290IENBMCIYDzIwMDkwMzI1MTIz
570+ODA1WhgPMjAxNzA2MTExMjM4MDVaMBYxFDASBgNVBAMTC3VnbHkgY2xpZW50MIGf
571+MA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDAZh/SRtNm5ntMT4qb6YzEpTroMlq2
572+rn+GrRHRiZ+xkCw/CGNhbtPir7/QxaUj26BSmQrHw1bGKEbPsWiW7bdXSespl+xK
573+iku4G/KvnnmWdeJHqsiXeUZtqurMELcPQAw9xPHEuhqqUJvvEoMTsnCEqGM+7Dtb
574+oCRajYyHfluARQIDAQABozYwNDAdBgNVHQ4EFgQUNQB+qkaOaEVecf1J3TTUtAff
575+0fAwEwYDVR0lBAwwCgYIKwYBBQUHAwIwDQYJKoZIhvcNAQEFBQADgYEAyv/Jh7gM
576+Q3OHvmsFEEvRI+hsW8y66zK4K5de239Y44iZrFYkt7Q5nBPMEWDj4F2hLYWL/qtI
577+9Zdr0U4UDCU9SmmGYh4o7R4TZ5pGFvBYvjhHbkSFYFQXZxKUi+WUxplP6I0wr2KJ
578+PSTJCjJOn3xo2NTKRgV1gaoTf2EhL+RG8TQ=
579+-----END CERTIFICATE-----
580+"""
581+
582+client_key_pem = """-----BEGIN RSA PRIVATE KEY-----
583+MIICXgIBAAKBgQDAZh/SRtNm5ntMT4qb6YzEpTroMlq2rn+GrRHRiZ+xkCw/CGNh
584+btPir7/QxaUj26BSmQrHw1bGKEbPsWiW7bdXSespl+xKiku4G/KvnnmWdeJHqsiX
585+eUZtqurMELcPQAw9xPHEuhqqUJvvEoMTsnCEqGM+7DtboCRajYyHfluARQIDAQAB
586+AoGATkZ+NceY5Glqyl4mD06SdcKfV65814vg2EL7V9t8+/mi9rYL8KztSXGlQWPX
587+zuHgtRoMl78yQ4ZJYOBVo+nsx8KZNRCEBlE19bamSbQLCeQMenWnpeYyQUZ908gF
588+h6L9qsFVJepgA9RDgAjyDoS5CaWCdCCPCH2lDkdcqC54SVUCQQDseuduc4wi8h4t
589+V8AahUn9fn9gYfhoNuM0gdguTA0nPLVWz4hy1yJiWYQe0H7NLNNTmCKiLQaJpAbb
590+TC6vE8C7AkEA0Ee8CMJUc20BnGEmxwgWcVuqFWaKCo8jTH1X38FlATUsyR3krjW2
591+dL3yDD9NwHxsYP7nTKp/U8MV7U9IBn4y/wJBAJl7H0/BcLeRmuJk7IqJ7b635iYB
592+D/9beFUw3MUXmQXZUfyYz39xf6CDZsu1GEdEC5haykeln3Of4M9d/4Kj+FcCQQCY
593+si6xwT7GzMDkk/ko684AV3KPc/h6G0yGtFIrMg7J3uExpR/VdH2KgwMkZXisSMvw
594+JJEQjOMCVsEJlRk54WWjAkEAzoZNH6UhDdBK5F38rVt/y4SEHgbSfJHIAmPS32Kq
595+f6GGcfNpip0Uk7q7udTKuX7Q/buZi/C4YW7u3VKAquv9NA==
596+-----END RSA PRIVATE KEY-----
597+"""
598+
599+def verify_cb(conn, cert, errnum, depth, ok):
600+ return ok
601+
602+class MemoryBIOTests(TestCase):
603+ """
604+ Tests for L{OpenSSL.SSL.Connection} using a memory BIO.
605+ """
606+ def _server(self):
607+ # Create the server side Connection. This is mostly setup boilerplate
608+ # - use TLSv1, use a particular certificate, etc.
609+ server_ctx = Context(TLSv1_METHOD)
610+ server_ctx.set_options(OP_NO_SSLv2 | OP_NO_SSLv3 | OP_SINGLE_DH_USE )
611+ server_ctx.set_verify(VERIFY_PEER|VERIFY_FAIL_IF_NO_PEER_CERT|VERIFY_CLIENT_ONCE, verify_cb)
612+ server_store = server_ctx.get_cert_store()
613+ server_ctx.use_privatekey(load_privatekey(FILETYPE_PEM, server_key_pem))
614+ server_ctx.use_certificate(load_certificate(FILETYPE_PEM, server_cert_pem))
615+ server_ctx.check_privatekey()
616+ server_store.add_cert(load_certificate(FILETYPE_PEM, root_cert_pem))
617+ # Here the Connection is actually created. None is passed as the 2nd
618+ # parameter, indicating a memory BIO should be created.
619+ server_conn = Connection(server_ctx, None)
620+ server_conn.set_accept_state()
621+ return server_conn
622+
623+
624+ def _client(self):
625+ # Now create the client side Connection. Similar boilerplate to the above.
626+ client_ctx = Context(TLSv1_METHOD)
627+ client_ctx.set_options(OP_NO_SSLv2 | OP_NO_SSLv3 | OP_SINGLE_DH_USE )
628+ client_ctx.set_verify(VERIFY_PEER|VERIFY_FAIL_IF_NO_PEER_CERT|VERIFY_CLIENT_ONCE, verify_cb)
629+ client_store = client_ctx.get_cert_store()
630+ client_ctx.use_privatekey(load_privatekey(FILETYPE_PEM, client_key_pem))
631+ client_ctx.use_certificate(load_certificate(FILETYPE_PEM, client_cert_pem))
632+ client_ctx.check_privatekey()
633+ client_store.add_cert(load_certificate(FILETYPE_PEM, root_cert_pem))
634+ # Again, None to create a new memory BIO.
635+ client_conn = Connection(client_ctx, None)
636+ client_conn.set_connect_state()
637+ return client_conn
638+
639+
640+ def _loopback(self, client_conn, server_conn):
641+ """
642+ Try to read application bytes from each of the two L{Connection}
643+ objects. Copy bytes back and forth between their send/receive buffers
644+ for as long as there is anything to copy. When there is nothing more
645+ to copy, return C{None}. If one of them actually manages to deliver
646+ some application bytes, return a two-tuple of the connection from which
647+ the bytes were read and the bytes themselves.
648+ """
649+ wrote = True
650+ while wrote:
651+ # Loop until neither side has anything to say
652+ wrote = False
653+
654+ # Copy stuff from each side's send buffer to the other side's
655+ # receive buffer.
656+ for (read, write) in [(client_conn, server_conn),
657+ (server_conn, client_conn)]:
658+
659+ # Give the side a chance to generate some more bytes, or
660+ # succeed.
661+ try:
662+ bytes = read.recv(2 ** 16)
663+ except WantReadError:
664+ # It didn't succeed, so we'll hope it generated some
665+ # output.
666+ pass
667+ else:
668+ # It did succeed, so we'll stop now and let the caller deal
669+ # with it.
670+ return (read, bytes)
671+
672+ while True:
673+ # Keep copying as long as there's more stuff there.
674+ try:
675+ dirty = read.bio_read(4096)
676+ except WantReadError:
677+ # Okay, nothing more waiting to be sent. Stop
678+ # processing this send buffer.
679+ break
680+ else:
681+ # Keep track of the fact that someone generated some
682+ # output.
683+ wrote = True
684+ write.bio_write(dirty)
685+
686+
687+ def test_connect(self):
688+ """
689+ Two L{Connection}s which use memory BIOs can be manually connected by
690+ reading from the output of each and writing those bytes to the input of
691+ the other and in this way establish a connection and exchange
692+ application-level bytes with each other.
693+ """
694+ server_conn = self._server()
695+ client_conn = self._client()
696+
697+ # There should be no key or nonces yet.
698+ self.assertIdentical(server_conn.master_key(), None)
699+ self.assertIdentical(server_conn.client_random(), None)
700+ self.assertIdentical(server_conn.server_random(), None)
701+
702+ # First, the handshake needs to happen. We'll deliver bytes back and
703+ # forth between the client and server until neither of them feels like
704+ # speaking any more.
705+ self.assertIdentical(self._loopback(client_conn, server_conn), None)
706+
707+ # Now that the handshake is done, there should be a key and nonces.
708+ self.assertNotIdentical(server_conn.master_key(), None)
709+ self.assertNotIdentical(server_conn.client_random(), None)
710+ self.assertNotIdentical(server_conn.server_random(), None)
711+ self.assertNotIdentical(server_conn.client_random(), client_conn.client_random())
712+ self.assertNotIdentical(server_conn.server_random(), client_conn.server_random())
713+
714+ # Here are the bytes we'll try to send.
715+ important_message = 'One if by land, two if by sea.'
716+
717+ server_conn.write(important_message)
718+ self.assertEquals(
719+ self._loopback(client_conn, server_conn),
720+ (client_conn, important_message))
721+
722+ client_conn.write(important_message[::-1])
723+ self.assertEquals(
724+ self._loopback(client_conn, server_conn),
725+ (server_conn, important_message[::-1]))
726+
727+
728+ def test_socketOverridesMemory(self):
729+ """
730+ Test that L{OpenSSL.SSL.bio_read} and L{OpenSSL.SSL.bio_write} don't
731+ work on L{OpenSSL.SSL.Connection}() that use sockets.
732+ """
733+ context = Context(SSLv3_METHOD)
734+ client = socket()
735+ clientSSL = Connection(context, client)
736+ self.assertRaises( TypeError, clientSSL.bio_read, 100)
737+ self.assertRaises( TypeError, clientSSL.bio_write, "foo")
738+
739+
740+ def test_outgoingOverflow(self):
741+ """
742+ If more bytes than can be written to the memory BIO are passed to
743+ L{Connection.send} at once, the number of bytes which were written is
744+ returned and that many bytes from the beginning of the input can be
745+ read from the other end of the connection.
746+ """
747+ server = self._server()
748+ client = self._client()
749+
750+ self._loopback(client, server)
751+
752+ size = 2 ** 15
753+ sent = client.send("x" * size)
754+ # Sanity check. We're trying to test what happens when the entire
755+ # input can't be sent. If the entire input was sent, this test is
756+ # meaningless.
757+ self.assertTrue(sent < size)
758+
759+ receiver, received = self._loopback(client, server)
760+ self.assertIdentical(receiver, server)
761+
762+ # We can rely on all of these bytes being received at once because
763+ # _loopback passes 2 ** 16 to recv - more than 2 ** 15.
764+ self.assertEquals(len(received), sent)
765+
766+
767+ def test_shutdown(self):
768+ """
769+ L{Connection.bio_shutdown} signals the end of the data stream from
770+ which the L{Connection} reads.
771+ """
772+ server = self._server()
773+ server.bio_shutdown()
774+ e = self.assertRaises(Error, server.recv, 1024)
775+ # We don't want WantReadError or ZeroReturnError or anything - it's a
776+ # handshake failure.
777+ self.assertEquals(e.__class__, Error)

Subscribers

People subscribed via source and target branches

to status/vote changes: