Merge lp:~exarkun/pyopenssl/mem-bio into lp:~exarkun/pyopenssl/trunk
- mem-bio
- Merge into 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 | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Jean-Paul Calderone | Pending | ||
Review via email: mp+6225@code.launchpad.net |
Commit message
Description of the change
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) |