ruby-tls 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/ext/tls/page.h CHANGED
@@ -1,61 +1,61 @@
1
- /*****************************************************************************
2
-
3
- $Id$
4
-
5
- File: page.h
6
- Date: 30Apr06
7
-
8
- Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
9
- Gmail: blackhedd
10
-
11
- This program is free software; you can redistribute it and/or modify
12
- it under the terms of either: 1) the GNU General Public License
13
- as published by the Free Software Foundation; either version 2 of the
14
- License, or (at your option) any later version; or 2) Ruby's License.
15
-
16
- See the file COPYING for complete licensing information.
17
-
18
- *****************************************************************************/
19
-
20
-
21
- #ifndef __PageManager__H_
22
- #define __PageManager__H_
23
-
24
-
25
- #include <deque>
26
- #include <stdexcept>
27
- #include <assert.h>
28
- #include <stdlib.h>
29
- #include <string.h>
30
-
31
-
32
- using namespace std;
33
-
34
-
35
- /**************
36
- class PageList
37
- **************/
38
-
39
- class PageList
40
- {
41
- struct Page {
42
- Page (const char *b, size_t s): Buffer(b), Size(s) {}
43
- const char *Buffer;
44
- size_t Size;
45
- };
46
-
47
- public:
48
- PageList();
49
- virtual ~PageList();
50
-
51
- void Push (const char*, int);
52
- bool HasPages();
53
- void Front (const char**, int*);
54
- void PopFront();
55
-
56
- private:
57
- deque<Page> Pages;
58
- };
59
-
60
-
61
- #endif // __PageManager__H_
1
+ /*****************************************************************************
2
+
3
+ $Id$
4
+
5
+ File: page.h
6
+ Date: 30Apr06
7
+
8
+ Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
9
+ Gmail: blackhedd
10
+
11
+ This program is free software; you can redistribute it and/or modify
12
+ it under the terms of either: 1) the GNU General Public License
13
+ as published by the Free Software Foundation; either version 2 of the
14
+ License, or (at your option) any later version; or 2) Ruby's License.
15
+
16
+ See the file COPYING for complete licensing information.
17
+
18
+ *****************************************************************************/
19
+
20
+
21
+ #ifndef __PageManager__H_
22
+ #define __PageManager__H_
23
+
24
+
25
+ #include <deque>
26
+ #include <stdexcept>
27
+ #include <assert.h>
28
+ #include <stdlib.h>
29
+ #include <string.h>
30
+
31
+
32
+ using namespace std;
33
+
34
+
35
+ /**************
36
+ class PageList
37
+ **************/
38
+
39
+ class PageList
40
+ {
41
+ struct Page {
42
+ Page (const char *b, size_t s): Buffer(b), Size(s) {}
43
+ const char *Buffer;
44
+ size_t Size;
45
+ };
46
+
47
+ public:
48
+ PageList();
49
+ virtual ~PageList();
50
+
51
+ void Push (const char*, int);
52
+ bool HasPages();
53
+ void Front (const char**, int*);
54
+ void PopFront();
55
+
56
+ private:
57
+ deque<Page> Pages;
58
+ };
59
+
60
+
61
+ #endif // __PageManager__H_
data/ext/tls/ssl.cpp CHANGED
@@ -1,593 +1,587 @@
1
- /*****************************************************************************
2
-
3
- $Id$
4
-
5
- File: ssl.cpp
6
- Date: 30Apr06
7
-
8
- Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
9
- Gmail: blackhedd
10
-
11
- This program is free software; you can redistribute it and/or modify
12
- it under the terms of either: 1) the GNU General Public License
13
- as published by the Free Software Foundation; either version 2 of the
14
- License, or (at your option) any later version; or 2) Ruby's License.
15
-
16
- See the file COPYING for complete licensing information.
17
-
18
- *****************************************************************************/
19
-
20
- #include "ssl.h"
21
-
22
-
23
- bool SslContext_t::bLibraryInitialized = false;
24
-
25
-
26
-
27
- static void InitializeDefaultCredentials();
28
- static EVP_PKEY *DefaultPrivateKey = NULL;
29
- static X509 *DefaultCertificate = NULL;
30
-
31
- static char PrivateMaterials[] = {
32
- "-----BEGIN RSA PRIVATE KEY-----\n"
33
- "MIICXAIBAAKBgQDCYYhcw6cGRbhBVShKmbWm7UVsEoBnUf0cCh8AX+MKhMxwVDWV\n"
34
- "Igdskntn3cSJjRtmgVJHIK0lpb/FYHQB93Ohpd9/Z18pDmovfFF9nDbFF0t39hJ/\n"
35
- "AqSzFB3GiVPoFFZJEE1vJqh+3jzsSF5K56bZ6azz38VlZgXeSozNW5bXkQIDAQAB\n"
36
- "AoGALA89gIFcr6BIBo8N5fL3aNHpZXjAICtGav+kTUpuxSiaym9cAeTHuAVv8Xgk\n"
37
- "H2Wbq11uz+6JMLpkQJH/WZ7EV59DPOicXrp0Imr73F3EXBfR7t2EQDYHPMthOA1D\n"
38
- "I9EtCzvV608Ze90hiJ7E3guGrGppZfJ+eUWCPgy8CZH1vRECQQDv67rwV/oU1aDo\n"
39
- "6/+d5nqjeW6mWkGqTnUU96jXap8EIw6B+0cUKskwx6mHJv+tEMM2748ZY7b0yBlg\n"
40
- "w4KDghbFAkEAz2h8PjSJG55LwqmXih1RONSgdN9hjB12LwXL1CaDh7/lkEhq0PlK\n"
41
- "PCAUwQSdM17Sl0Xxm2CZiekTSlwmHrtqXQJAF3+8QJwtV2sRJp8u2zVe37IeH1cJ\n"
42
- "xXeHyjTzqZ2803fnjN2iuZvzNr7noOA1/Kp+pFvUZUU5/0G2Ep8zolPUjQJAFA7k\n"
43
- "xRdLkzIx3XeNQjwnmLlncyYPRv+qaE3FMpUu7zftuZBnVCJnvXzUxP3vPgKTlzGa\n"
44
- "dg5XivDRfsV+okY5uQJBAMV4FesUuLQVEKb6lMs7rzZwpeGQhFDRfywJzfom2TLn\n"
45
- "2RdJQQ3dcgnhdVDgt5o1qkmsqQh8uJrJ9SdyLIaZQIc=\n"
46
- "-----END RSA PRIVATE KEY-----\n"
47
- "-----BEGIN CERTIFICATE-----\n"
48
- "MIID6TCCA1KgAwIBAgIJANm4W/Tzs+s+MA0GCSqGSIb3DQEBBQUAMIGqMQswCQYD\n"
49
- "VQQGEwJVUzERMA8GA1UECBMITmV3IFlvcmsxETAPBgNVBAcTCE5ldyBZb3JrMRYw\n"
50
- "FAYDVQQKEw1TdGVhbWhlYXQubmV0MRQwEgYDVQQLEwtFbmdpbmVlcmluZzEdMBsG\n"
51
- "A1UEAxMUb3BlbmNhLnN0ZWFtaGVhdC5uZXQxKDAmBgkqhkiG9w0BCQEWGWVuZ2lu\n"
52
- "ZWVyaW5nQHN0ZWFtaGVhdC5uZXQwHhcNMDYwNTA1MTcwNjAzWhcNMjQwMjIwMTcw\n"
53
- "NjAzWjCBqjELMAkGA1UEBhMCVVMxETAPBgNVBAgTCE5ldyBZb3JrMREwDwYDVQQH\n"
54
- "EwhOZXcgWW9yazEWMBQGA1UEChMNU3RlYW1oZWF0Lm5ldDEUMBIGA1UECxMLRW5n\n"
55
- "aW5lZXJpbmcxHTAbBgNVBAMTFG9wZW5jYS5zdGVhbWhlYXQubmV0MSgwJgYJKoZI\n"
56
- "hvcNAQkBFhllbmdpbmVlcmluZ0BzdGVhbWhlYXQubmV0MIGfMA0GCSqGSIb3DQEB\n"
57
- "AQUAA4GNADCBiQKBgQDCYYhcw6cGRbhBVShKmbWm7UVsEoBnUf0cCh8AX+MKhMxw\n"
58
- "VDWVIgdskntn3cSJjRtmgVJHIK0lpb/FYHQB93Ohpd9/Z18pDmovfFF9nDbFF0t3\n"
59
- "9hJ/AqSzFB3GiVPoFFZJEE1vJqh+3jzsSF5K56bZ6azz38VlZgXeSozNW5bXkQID\n"
60
- "AQABo4IBEzCCAQ8wHQYDVR0OBBYEFPJvPd1Fcmd8o/Tm88r+NjYPICCkMIHfBgNV\n"
61
- "HSMEgdcwgdSAFPJvPd1Fcmd8o/Tm88r+NjYPICCkoYGwpIGtMIGqMQswCQYDVQQG\n"
62
- "EwJVUzERMA8GA1UECBMITmV3IFlvcmsxETAPBgNVBAcTCE5ldyBZb3JrMRYwFAYD\n"
63
- "VQQKEw1TdGVhbWhlYXQubmV0MRQwEgYDVQQLEwtFbmdpbmVlcmluZzEdMBsGA1UE\n"
64
- "AxMUb3BlbmNhLnN0ZWFtaGVhdC5uZXQxKDAmBgkqhkiG9w0BCQEWGWVuZ2luZWVy\n"
65
- "aW5nQHN0ZWFtaGVhdC5uZXSCCQDZuFv087PrPjAMBgNVHRMEBTADAQH/MA0GCSqG\n"
66
- "SIb3DQEBBQUAA4GBAC1CXey/4UoLgJiwcEMDxOvW74plks23090iziFIlGgcIhk0\n"
67
- "Df6hTAs7H3MWww62ddvR8l07AWfSzSP5L6mDsbvq7EmQsmPODwb6C+i2aF3EDL8j\n"
68
- "uw73m4YIGI0Zw2XdBpiOGkx2H56Kya6mJJe/5XORZedh1wpI7zki01tHYbcy\n"
69
- "-----END CERTIFICATE-----\n"};
70
-
71
- /* These private materials were made with:
72
- * openssl req -new -x509 -keyout cakey.pem -out cacert.pem -nodes -days 6500
73
- * TODO: We need a full-blown capability to work with user-supplied
74
- * keypairs and properly-signed certificates.
75
- */
76
-
77
-
78
- /*****************
79
- builtin_passwd_cb
80
- *****************/
81
-
82
- extern "C" int builtin_passwd_cb (char *buf, int bufsize, int rwflag, void *userdata)
83
- {
84
- strcpy (buf, "kittycat");
85
- return 8;
86
- }
87
-
88
- /****************************
89
- InitializeDefaultCredentials
90
- ****************************/
91
-
92
- static void InitializeDefaultCredentials()
93
- {
94
- BIO *bio = BIO_new_mem_buf (PrivateMaterials, -1);
95
- assert (bio);
96
-
97
- if (DefaultPrivateKey) {
98
- // we may come here in a restart.
99
- EVP_PKEY_free (DefaultPrivateKey);
100
- DefaultPrivateKey = NULL;
101
- }
102
- PEM_read_bio_PrivateKey (bio, &DefaultPrivateKey, builtin_passwd_cb, 0);
103
-
104
- if (DefaultCertificate) {
105
- // we may come here in a restart.
106
- X509_free (DefaultCertificate);
107
- DefaultCertificate = NULL;
108
- }
109
- PEM_read_bio_X509 (bio, &DefaultCertificate, NULL, 0);
110
-
111
- BIO_free (bio);
112
- }
113
-
114
-
115
-
116
- /**************************
117
- SslContext_t::SslContext_t
118
- **************************/
119
-
120
- SslContext_t::SslContext_t (bool is_server, const string &privkeyfile, const string &certchainfile):
121
- pCtx (NULL),
122
- PrivateKey (NULL),
123
- Certificate (NULL)
124
- {
125
- /* TODO: the usage of the specified private-key and cert-chain filenames only applies to
126
- * client-side connections at this point. Server connections currently use the default materials.
127
- * That needs to be fixed asap.
128
- * Also, in this implementation, server-side connections use statically defined X-509 defaults.
129
- * One thing I'm really not clear on is whether or not you have to explicitly free X509 and EVP_PKEY
130
- * objects when we call our destructor, or whether just calling SSL_CTX_free is enough.
131
- */
132
-
133
- if (!bLibraryInitialized) {
134
- bLibraryInitialized = true;
135
- SSL_library_init();
136
- OpenSSL_add_ssl_algorithms();
137
- OpenSSL_add_all_algorithms();
138
- SSL_load_error_strings();
139
- ERR_load_crypto_strings();
140
-
141
- InitializeDefaultCredentials();
142
- }
143
-
144
- bIsServer = is_server;
145
- pCtx = SSL_CTX_new (is_server ? SSLv23_server_method() : SSLv23_client_method());
146
- if (!pCtx)
147
- throw std::runtime_error ("no SSL context");
148
-
149
- SSL_CTX_set_options (pCtx, SSL_OP_ALL);
150
- //SSL_CTX_set_options (pCtx, (SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3));
151
- #ifdef SSL_MODE_RELEASE_BUFFERS
152
- SSL_CTX_set_mode (pCtx, SSL_MODE_RELEASE_BUFFERS);
153
- #endif
154
-
155
- if (is_server) {
156
- // The SSL_CTX calls here do NOT allocate memory.
157
- int e;
158
- if (privkeyfile.length() > 0)
159
- e = SSL_CTX_use_PrivateKey_file (pCtx, privkeyfile.c_str(), SSL_FILETYPE_PEM);
160
- else
161
- e = SSL_CTX_use_PrivateKey (pCtx, DefaultPrivateKey);
162
- if (e <= 0) ERR_print_errors_fp(stderr);
163
- assert (e > 0);
164
-
165
- if (certchainfile.length() > 0)
166
- e = SSL_CTX_use_certificate_chain_file (pCtx, certchainfile.c_str());
167
- else
168
- e = SSL_CTX_use_certificate (pCtx, DefaultCertificate);
169
- if (e <= 0) ERR_print_errors_fp(stderr);
170
- assert (e > 0);
171
- }
172
-
173
- //SSL_CTX_set_cipher_list (pCtx, "ALL:!ADH:!LOW:!EXP:!DES-CBC3-SHA:@STRENGTH");
174
- // Improve security http://blog.cloudflare.com/staying-on-top-of-tls-attacks
175
- SSL_CTX_set_cipher_list (pCtx, "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-RC4-SHA:ECDHE-RSA-AES128-SHA:AES128-GCM-SHA256:RC4:HIGH:!MD5:!aNULL:!EDH:!CAMELLIA:@STRENGTH");
176
-
177
- if (is_server) {
178
- SSL_CTX_sess_set_cache_size (pCtx, 128);
179
- SSL_CTX_set_session_id_context (pCtx, (unsigned char*)"ruby-tls", 8);
180
- }
181
- else {
182
- int e;
183
- if (privkeyfile.length() > 0) {
184
- e = SSL_CTX_use_PrivateKey_file (pCtx, privkeyfile.c_str(), SSL_FILETYPE_PEM);
185
- if (e <= 0) ERR_print_errors_fp(stderr);
186
- assert (e > 0);
187
- }
188
- if (certchainfile.length() > 0) {
189
- e = SSL_CTX_use_certificate_chain_file (pCtx, certchainfile.c_str());
190
- if (e <= 0) ERR_print_errors_fp(stderr);
191
- assert (e > 0);
192
- }
193
- }
194
- }
195
-
196
-
197
-
198
- /***************************
199
- SslContext_t::~SslContext_t
200
- ***************************/
201
-
202
- SslContext_t::~SslContext_t()
203
- {
204
- if (pCtx)
205
- SSL_CTX_free (pCtx);
206
- if (PrivateKey)
207
- EVP_PKEY_free (PrivateKey);
208
- if (Certificate)
209
- X509_free (Certificate);
210
- }
211
-
212
-
213
-
214
- /******************
215
- SslBox_t::SslBox_t
216
- ******************/
217
-
218
- SslBox_t::SslBox_t (tls_state_t *tls_state, bool is_server, const string &privkeyfile, const string &certchainfile, bool verify_peer):
219
- bIsServer (is_server),
220
- bHandshakeCompleted (false),
221
- bVerifyPeer (verify_peer),
222
- pSSL (NULL),
223
- pbioRead (NULL),
224
- pbioWrite (NULL)
225
- {
226
- /* TODO someday: make it possible to re-use SSL contexts so we don't have to create
227
- * a new one every time we come here.
228
- */
229
-
230
- Context = new SslContext_t (bIsServer, privkeyfile, certchainfile);
231
- assert (Context);
232
-
233
- pbioRead = BIO_new (BIO_s_mem());
234
- assert (pbioRead);
235
-
236
- pbioWrite = BIO_new (BIO_s_mem());
237
- assert (pbioWrite);
238
-
239
- pSSL = SSL_new (Context->pCtx);
240
- assert (pSSL);
241
- SSL_set_bio (pSSL, pbioRead, pbioWrite);
242
-
243
- // Store a pointer to the callbacks in the SSL object so we can retrieve it later
244
- SSL_set_ex_data(pSSL, 0, (void*) tls_state);
245
-
246
- if (bVerifyPeer)
247
- SSL_set_verify(pSSL, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, ssl_verify_wrapper);
248
-
249
- if (!bIsServer)
250
- SSL_connect (pSSL);
251
- }
252
-
253
-
254
-
255
- /*******************
256
- SslBox_t::~SslBox_t
257
- *******************/
258
-
259
- SslBox_t::~SslBox_t()
260
- {
261
- // Freeing pSSL will also free the associated BIOs, so DON'T free them separately.
262
- if (pSSL) {
263
- if (SSL_get_shutdown (pSSL) & SSL_RECEIVED_SHUTDOWN)
264
- SSL_shutdown (pSSL);
265
- else
266
- SSL_clear (pSSL);
267
- SSL_free (pSSL);
268
- }
269
-
270
- delete Context;
271
- }
272
-
273
-
274
-
275
- /***********************
276
- SslBox_t::PutCiphertext
277
- ***********************/
278
-
279
- bool SslBox_t::PutCiphertext (const char *buf, int bufsize)
280
- {
281
- assert (buf && (bufsize > 0));
282
-
283
- assert (pbioRead);
284
- int n = BIO_write (pbioRead, buf, bufsize);
285
-
286
- return (n == bufsize) ? true : false;
287
- }
288
-
289
-
290
- /**********************
291
- SslBox_t::GetPlaintext
292
- **********************/
293
-
294
- int SslBox_t::GetPlaintext (char *buf, int bufsize)
295
- {
296
- if (!SSL_is_init_finished (pSSL)) {
297
- int e = bIsServer ? SSL_accept (pSSL) : SSL_connect (pSSL);
298
- if (e < 0) {
299
- int er = SSL_get_error (pSSL, e);
300
- if (er != SSL_ERROR_WANT_READ) {
301
- // Return -1 for a nonfatal error, -2 for an error that should force the connection down.
302
- return (er == SSL_ERROR_SSL) ? (-2) : (-1);
303
- }
304
- else
305
- return 0;
306
- }
307
- bHandshakeCompleted = true;
308
- // If handshake finished, FALL THROUGH and return the available plaintext.
309
- }
310
-
311
- if (!SSL_is_init_finished (pSSL)) {
312
- // We can get here if a browser abandons a handshake.
313
- // The user can see a warning dialog and abort the connection.
314
- cerr << "<SSL_incomp>";
315
- return 0;
316
- }
317
-
318
- //cerr << "CIPH: " << SSL_get_cipher (pSSL) << endl;
319
-
320
- int n = SSL_read (pSSL, buf, bufsize);
321
- if (n >= 0) {
322
- return n;
323
- }
324
- else {
325
- if (SSL_get_error (pSSL, n) == SSL_ERROR_WANT_READ) {
326
- return 0;
327
- }
328
- else {
329
- return -1;
330
- }
331
- }
332
-
333
- return 0;
334
- }
335
-
336
-
337
-
338
- /**************************
339
- SslBox_t::CanGetCiphertext
340
- **************************/
341
-
342
- bool SslBox_t::CanGetCiphertext()
343
- {
344
- assert (pbioWrite);
345
- return BIO_pending (pbioWrite) ? true : false;
346
- }
347
-
348
-
349
-
350
- /***********************
351
- SslBox_t::GetCiphertext
352
- ***********************/
353
-
354
- int SslBox_t::GetCiphertext (char *buf, int bufsize)
355
- {
356
- assert (pbioWrite);
357
- assert (buf && (bufsize > 0));
358
-
359
- return BIO_read (pbioWrite, buf, bufsize);
360
- }
361
-
362
-
363
-
364
- /**********************
365
- SslBox_t::PutPlaintext
366
- **********************/
367
-
368
- int SslBox_t::PutPlaintext (const char *buf, int bufsize)
369
- {
370
- // The caller will interpret the return value as the number of bytes written.
371
- // WARNING WARNING WARNING, are there any situations in which a 0 or -1 return
372
- // from SSL_write means we should immediately retry? The socket-machine loop
373
- // will probably wait for a time-out cycle (perhaps a second) before re-trying.
374
- // THIS WOULD CAUSE A PERCEPTIBLE DELAY!
375
-
376
- /* We internally queue any outbound plaintext that can't be dispatched
377
- * because we're in the middle of a handshake or something.
378
- * When we get called, try to send any queued data first, and then
379
- * send the caller's data (or queue it). We may get called with no outbound
380
- * data, which means we try to send the outbound queue and that's all.
381
- *
382
- * Return >0 if we wrote any data, 0 if we didn't, and <0 for a fatal error.
383
- * Note that if we return 0, the connection is still considered live
384
- * and we are signalling that we have accepted the outbound data (if any).
385
- */
386
-
387
- OutboundQ.Push (buf, bufsize);
388
-
389
- if (!SSL_is_init_finished (pSSL))
390
- return 0;
391
-
392
- bool fatal = false;
393
- bool did_work = false;
394
-
395
- while (OutboundQ.HasPages()) {
396
- const char *page;
397
- int length;
398
- OutboundQ.Front (&page, &length);
399
- assert (page && (length > 0));
400
- int n = SSL_write (pSSL, page, length);
401
- if (n > 0) {
402
- did_work = true;
403
- OutboundQ.PopFront();
404
- }
405
- else {
406
- int er = SSL_get_error (pSSL, n);
407
- if ((er != SSL_ERROR_WANT_READ) && (er != SSL_ERROR_WANT_WRITE))
408
- fatal = true;
409
- break;
410
- }
411
- }
412
-
413
-
414
- if (did_work)
415
- return 1;
416
- else if (fatal)
417
- return -1;
418
- else
419
- return 0;
420
- }
421
-
422
- /**********************
423
- SslBox_t::GetPeerCert
424
- **********************/
425
-
426
- X509 *SslBox_t::GetPeerCert()
427
- {
428
- X509 *cert = NULL;
429
-
430
- if (pSSL)
431
- cert = SSL_get_peer_certificate(pSSL);
432
-
433
- return cert;
434
- }
435
-
436
-
437
-
438
-
439
-
440
-
441
-
442
-
443
- void _DispatchCiphertext(tls_state_t *tls_state)
444
- {
445
- SslBox_t *SslBox = tls_state->SslBox;
446
- assert (SslBox);
447
-
448
- char BigBuf [2048];
449
- bool did_work;
450
-
451
- do {
452
- did_work = false;
453
-
454
- // try to drain ciphertext
455
- while (SslBox->CanGetCiphertext()) {
456
- int r = SslBox->GetCiphertext(BigBuf, sizeof(BigBuf));
457
- assert (r > 0);
458
-
459
- // Queue the data for transmit
460
- tls_state->transmit_cb(tls_state, BigBuf, r);
461
-
462
- did_work = true;
463
- }
464
-
465
- // Pump the SslBox, in case it has queued outgoing plaintext
466
- // This will return >0 if data was written,
467
- // 0 if no data was written, and <0 if there was a fatal error.
468
- bool pump;
469
- do {
470
- pump = false;
471
- int w = SslBox->PutPlaintext(NULL, 0);
472
- if (w > 0) {
473
- did_work = true;
474
- pump = true;
475
- } else if (w < 0) {
476
- // Close on error
477
- tls_state->close_cb(tls_state);
478
- }
479
- } while (pump);
480
-
481
- } while (did_work);
482
- }
483
-
484
- void _CheckHandshakeStatus(tls_state_t *tls_state)
485
- {
486
- SslBox_t *SslBox = tls_state->SslBox;
487
- // keep track of weather or not this function has been called yet
488
- if (SslBox && tls_state->handshake_signaled == 0 && SslBox->IsHandshakeCompleted()) {
489
- tls_state->handshake_signaled = 1;
490
-
491
- // Optional callback
492
- if (tls_state->handshake_cb) {
493
- tls_state->handshake_cb(tls_state);
494
- }
495
- }
496
- }
497
-
498
-
499
-
500
-
501
-
502
-
503
-
504
-
505
- /******************
506
- ssl_verify_wrapper
507
- *******************/
508
-
509
- extern "C" int ssl_verify_wrapper(int preverify_ok, X509_STORE_CTX *ctx)
510
- {
511
- X509 *cert;
512
- SSL *ssl;
513
- BUF_MEM *buf;
514
- BIO *out;
515
- int result;
516
- tls_state_t *tls_state;
517
-
518
- cert = X509_STORE_CTX_get_current_cert(ctx);
519
- ssl = (SSL*) X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
520
-
521
- out = BIO_new(BIO_s_mem());
522
- PEM_write_bio_X509(out, cert);
523
- BIO_write(out, "\0", 1);
524
- BIO_get_mem_ptr(out, &buf);
525
-
526
- tls_state = (tls_state_t *) SSL_get_ex_data(ssl, 0);
527
- result = tls_state->verify_cb(tls_state, buf->data);
528
-
529
- BIO_free(out);
530
-
531
- return result;
532
- }
533
-
534
-
535
-
536
- // These are the FFI interactions:
537
- // ------------------------------
538
- extern "C" void start_tls(tls_state_t *tls_state, bool bIsServer, const char *PrivateKeyFilename, const char *CertChainFilename, bool bSslVerifyPeer)
539
- {
540
- tls_state->SslBox = new SslBox_t (tls_state, bIsServer, PrivateKeyFilename, CertChainFilename, bSslVerifyPeer);
541
- _DispatchCiphertext(tls_state);
542
- }
543
-
544
- extern "C" void encrypt_data(tls_state_t *tls_state, const char *data, int length) {
545
- SslBox_t *SslBox = tls_state->SslBox;
546
-
547
- if (length > 0 && SslBox) {
548
- int w = SslBox->PutPlaintext(data, length);
549
-
550
- if (w < 0) {
551
- // Close the connection if there was an issue
552
- tls_state->close_cb(tls_state);
553
- } else {
554
- _DispatchCiphertext(tls_state);
555
- }
556
- }
557
- }
558
-
559
- extern "C" void decrypt_data(tls_state_t *tls_state, const char *buffer, int size) {
560
- SslBox_t *SslBox = tls_state->SslBox;
561
- if (SslBox) {
562
- SslBox->PutCiphertext (buffer, size);
563
-
564
- int s;
565
- char B [2048];
566
- while ((s = SslBox->GetPlaintext(B, sizeof(B) - 1)) > 0) {
567
- _CheckHandshakeStatus(tls_state);
568
- B[s] = 0;
569
-
570
- // data recieved callback
571
- tls_state->dispatch_cb(tls_state, B, s);
572
- }
573
-
574
- // If our SSL handshake had a problem, shut down the connection.
575
- if (s == -2) {
576
- tls_state->close_cb(tls_state);
577
- return;
578
- }
579
-
580
- _CheckHandshakeStatus(tls_state);
581
- _DispatchCiphertext(tls_state);
582
- }
583
- }
584
-
585
- extern "C" X509 *get_peer_cert(tls_state_t *tls_state)
586
- {
587
- if (tls_state->SslBox)
588
- return tls_state->SslBox->GetPeerCert();
589
-
590
- return 0;
591
- }
592
-
593
-
1
+ /*****************************************************************************
2
+
3
+ $Id$
4
+
5
+ File: ssl.cpp
6
+ Date: 30Apr06
7
+
8
+ Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
9
+ Gmail: blackhedd
10
+
11
+ This program is free software; you can redistribute it and/or modify
12
+ it under the terms of either: 1) the GNU General Public License
13
+ as published by the Free Software Foundation; either version 2 of the
14
+ License, or (at your option) any later version; or 2) Ruby's License.
15
+
16
+ See the file COPYING for complete licensing information.
17
+
18
+ *****************************************************************************/
19
+
20
+ #include "ssl.h"
21
+
22
+
23
+ static void InitializeDefaultCredentials();
24
+ static EVP_PKEY *DefaultPrivateKey = NULL;
25
+ static X509 *DefaultCertificate = NULL;
26
+
27
+ static char PrivateMaterials[] = {
28
+ "-----BEGIN RSA PRIVATE KEY-----\n"
29
+ "MIICXAIBAAKBgQDCYYhcw6cGRbhBVShKmbWm7UVsEoBnUf0cCh8AX+MKhMxwVDWV\n"
30
+ "Igdskntn3cSJjRtmgVJHIK0lpb/FYHQB93Ohpd9/Z18pDmovfFF9nDbFF0t39hJ/\n"
31
+ "AqSzFB3GiVPoFFZJEE1vJqh+3jzsSF5K56bZ6azz38VlZgXeSozNW5bXkQIDAQAB\n"
32
+ "AoGALA89gIFcr6BIBo8N5fL3aNHpZXjAICtGav+kTUpuxSiaym9cAeTHuAVv8Xgk\n"
33
+ "H2Wbq11uz+6JMLpkQJH/WZ7EV59DPOicXrp0Imr73F3EXBfR7t2EQDYHPMthOA1D\n"
34
+ "I9EtCzvV608Ze90hiJ7E3guGrGppZfJ+eUWCPgy8CZH1vRECQQDv67rwV/oU1aDo\n"
35
+ "6/+d5nqjeW6mWkGqTnUU96jXap8EIw6B+0cUKskwx6mHJv+tEMM2748ZY7b0yBlg\n"
36
+ "w4KDghbFAkEAz2h8PjSJG55LwqmXih1RONSgdN9hjB12LwXL1CaDh7/lkEhq0PlK\n"
37
+ "PCAUwQSdM17Sl0Xxm2CZiekTSlwmHrtqXQJAF3+8QJwtV2sRJp8u2zVe37IeH1cJ\n"
38
+ "xXeHyjTzqZ2803fnjN2iuZvzNr7noOA1/Kp+pFvUZUU5/0G2Ep8zolPUjQJAFA7k\n"
39
+ "xRdLkzIx3XeNQjwnmLlncyYPRv+qaE3FMpUu7zftuZBnVCJnvXzUxP3vPgKTlzGa\n"
40
+ "dg5XivDRfsV+okY5uQJBAMV4FesUuLQVEKb6lMs7rzZwpeGQhFDRfywJzfom2TLn\n"
41
+ "2RdJQQ3dcgnhdVDgt5o1qkmsqQh8uJrJ9SdyLIaZQIc=\n"
42
+ "-----END RSA PRIVATE KEY-----\n"
43
+ "-----BEGIN CERTIFICATE-----\n"
44
+ "MIID6TCCA1KgAwIBAgIJANm4W/Tzs+s+MA0GCSqGSIb3DQEBBQUAMIGqMQswCQYD\n"
45
+ "VQQGEwJVUzERMA8GA1UECBMITmV3IFlvcmsxETAPBgNVBAcTCE5ldyBZb3JrMRYw\n"
46
+ "FAYDVQQKEw1TdGVhbWhlYXQubmV0MRQwEgYDVQQLEwtFbmdpbmVlcmluZzEdMBsG\n"
47
+ "A1UEAxMUb3BlbmNhLnN0ZWFtaGVhdC5uZXQxKDAmBgkqhkiG9w0BCQEWGWVuZ2lu\n"
48
+ "ZWVyaW5nQHN0ZWFtaGVhdC5uZXQwHhcNMDYwNTA1MTcwNjAzWhcNMjQwMjIwMTcw\n"
49
+ "NjAzWjCBqjELMAkGA1UEBhMCVVMxETAPBgNVBAgTCE5ldyBZb3JrMREwDwYDVQQH\n"
50
+ "EwhOZXcgWW9yazEWMBQGA1UEChMNU3RlYW1oZWF0Lm5ldDEUMBIGA1UECxMLRW5n\n"
51
+ "aW5lZXJpbmcxHTAbBgNVBAMTFG9wZW5jYS5zdGVhbWhlYXQubmV0MSgwJgYJKoZI\n"
52
+ "hvcNAQkBFhllbmdpbmVlcmluZ0BzdGVhbWhlYXQubmV0MIGfMA0GCSqGSIb3DQEB\n"
53
+ "AQUAA4GNADCBiQKBgQDCYYhcw6cGRbhBVShKmbWm7UVsEoBnUf0cCh8AX+MKhMxw\n"
54
+ "VDWVIgdskntn3cSJjRtmgVJHIK0lpb/FYHQB93Ohpd9/Z18pDmovfFF9nDbFF0t3\n"
55
+ "9hJ/AqSzFB3GiVPoFFZJEE1vJqh+3jzsSF5K56bZ6azz38VlZgXeSozNW5bXkQID\n"
56
+ "AQABo4IBEzCCAQ8wHQYDVR0OBBYEFPJvPd1Fcmd8o/Tm88r+NjYPICCkMIHfBgNV\n"
57
+ "HSMEgdcwgdSAFPJvPd1Fcmd8o/Tm88r+NjYPICCkoYGwpIGtMIGqMQswCQYDVQQG\n"
58
+ "EwJVUzERMA8GA1UECBMITmV3IFlvcmsxETAPBgNVBAcTCE5ldyBZb3JrMRYwFAYD\n"
59
+ "VQQKEw1TdGVhbWhlYXQubmV0MRQwEgYDVQQLEwtFbmdpbmVlcmluZzEdMBsGA1UE\n"
60
+ "AxMUb3BlbmNhLnN0ZWFtaGVhdC5uZXQxKDAmBgkqhkiG9w0BCQEWGWVuZ2luZWVy\n"
61
+ "aW5nQHN0ZWFtaGVhdC5uZXSCCQDZuFv087PrPjAMBgNVHRMEBTADAQH/MA0GCSqG\n"
62
+ "SIb3DQEBBQUAA4GBAC1CXey/4UoLgJiwcEMDxOvW74plks23090iziFIlGgcIhk0\n"
63
+ "Df6hTAs7H3MWww62ddvR8l07AWfSzSP5L6mDsbvq7EmQsmPODwb6C+i2aF3EDL8j\n"
64
+ "uw73m4YIGI0Zw2XdBpiOGkx2H56Kya6mJJe/5XORZedh1wpI7zki01tHYbcy\n"
65
+ "-----END CERTIFICATE-----\n"};
66
+
67
+ /* These private materials were made with:
68
+ * openssl req -new -x509 -keyout cakey.pem -out cacert.pem -nodes -days 6500
69
+ * TODO: We need a full-blown capability to work with user-supplied
70
+ * keypairs and properly-signed certificates.
71
+ */
72
+
73
+
74
+ /*****************
75
+ builtin_passwd_cb
76
+ *****************/
77
+
78
+ extern "C" int builtin_passwd_cb (char *buf, int bufsize, int rwflag, void *userdata)
79
+ {
80
+ strcpy (buf, "kittycat");
81
+ return 8;
82
+ }
83
+
84
+ /****************************
85
+ InitializeDefaultCredentials
86
+ ****************************/
87
+
88
+ static void InitializeDefaultCredentials()
89
+ {
90
+ BIO *bio = BIO_new_mem_buf (PrivateMaterials, -1);
91
+ assert (bio);
92
+
93
+ if (DefaultPrivateKey) {
94
+ // we may come here in a restart.
95
+ EVP_PKEY_free (DefaultPrivateKey);
96
+ DefaultPrivateKey = NULL;
97
+ }
98
+ PEM_read_bio_PrivateKey (bio, &DefaultPrivateKey, builtin_passwd_cb, 0);
99
+
100
+ if (DefaultCertificate) {
101
+ // we may come here in a restart.
102
+ X509_free (DefaultCertificate);
103
+ DefaultCertificate = NULL;
104
+ }
105
+ PEM_read_bio_X509 (bio, &DefaultCertificate, NULL, 0);
106
+
107
+ BIO_free (bio);
108
+ }
109
+
110
+
111
+
112
+ /**************************
113
+ SslContext_t::SslContext_t
114
+ **************************/
115
+
116
+ SslContext_t::SslContext_t (bool is_server, const string &privkeyfile, const string &certchainfile):
117
+ pCtx (NULL),
118
+ PrivateKey (NULL),
119
+ Certificate (NULL)
120
+ {
121
+ /* TODO: the usage of the specified private-key and cert-chain filenames only applies to
122
+ * client-side connections at this point. Server connections currently use the default materials.
123
+ * That needs to be fixed asap.
124
+ * Also, in this implementation, server-side connections use statically defined X-509 defaults.
125
+ * One thing I'm really not clear on is whether or not you have to explicitly free X509 and EVP_PKEY
126
+ * objects when we call our destructor, or whether just calling SSL_CTX_free is enough.
127
+ */
128
+
129
+ bIsServer = is_server;
130
+ pCtx = SSL_CTX_new (is_server ? SSLv23_server_method() : SSLv23_client_method());
131
+ if (!pCtx)
132
+ throw std::runtime_error ("no SSL context");
133
+
134
+ SSL_CTX_set_options (pCtx, SSL_OP_ALL);
135
+ //SSL_CTX_set_options (pCtx, (SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3));
136
+ #ifdef SSL_MODE_RELEASE_BUFFERS
137
+ SSL_CTX_set_mode (pCtx, SSL_MODE_RELEASE_BUFFERS);
138
+ #endif
139
+
140
+ if (is_server) {
141
+ // The SSL_CTX calls here do NOT allocate memory.
142
+ int e;
143
+ if (privkeyfile.length() > 0)
144
+ e = SSL_CTX_use_PrivateKey_file (pCtx, privkeyfile.c_str(), SSL_FILETYPE_PEM);
145
+ else
146
+ e = SSL_CTX_use_PrivateKey (pCtx, DefaultPrivateKey);
147
+ if (e <= 0) ERR_print_errors_fp(stderr);
148
+ assert (e > 0);
149
+
150
+ if (certchainfile.length() > 0)
151
+ e = SSL_CTX_use_certificate_chain_file (pCtx, certchainfile.c_str());
152
+ else
153
+ e = SSL_CTX_use_certificate (pCtx, DefaultCertificate);
154
+ if (e <= 0) ERR_print_errors_fp(stderr);
155
+ assert (e > 0);
156
+ }
157
+
158
+ //SSL_CTX_set_cipher_list (pCtx, "ALL:!ADH:!LOW:!EXP:!DES-CBC3-SHA:@STRENGTH");
159
+ // Improve security http://blog.cloudflare.com/staying-on-top-of-tls-attacks
160
+ SSL_CTX_set_cipher_list (pCtx, "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-RC4-SHA:ECDHE-RSA-AES128-SHA:AES128-GCM-SHA256:RC4:HIGH:!MD5:!aNULL:!EDH:!CAMELLIA:@STRENGTH");
161
+
162
+ if (is_server) {
163
+ SSL_CTX_sess_set_cache_size (pCtx, 128);
164
+ SSL_CTX_set_session_id_context (pCtx, (unsigned char*)"ruby-tls", 8);
165
+ }
166
+ else {
167
+ int e;
168
+ if (privkeyfile.length() > 0) {
169
+ e = SSL_CTX_use_PrivateKey_file (pCtx, privkeyfile.c_str(), SSL_FILETYPE_PEM);
170
+ if (e <= 0) ERR_print_errors_fp(stderr);
171
+ assert (e > 0);
172
+ }
173
+ if (certchainfile.length() > 0) {
174
+ e = SSL_CTX_use_certificate_chain_file (pCtx, certchainfile.c_str());
175
+ if (e <= 0) ERR_print_errors_fp(stderr);
176
+ assert (e > 0);
177
+ }
178
+ }
179
+ }
180
+
181
+
182
+
183
+ /***************************
184
+ SslContext_t::~SslContext_t
185
+ ***************************/
186
+
187
+ SslContext_t::~SslContext_t()
188
+ {
189
+ if (pCtx)
190
+ SSL_CTX_free (pCtx);
191
+ if (PrivateKey)
192
+ EVP_PKEY_free (PrivateKey);
193
+ if (Certificate)
194
+ X509_free (Certificate);
195
+ }
196
+
197
+
198
+
199
+ /******************
200
+ SslBox_t::SslBox_t
201
+ ******************/
202
+
203
+ SslBox_t::SslBox_t (tls_state_t *tls_state, bool is_server, const string &privkeyfile, const string &certchainfile, bool verify_peer):
204
+ bIsServer (is_server),
205
+ bHandshakeCompleted (false),
206
+ bVerifyPeer (verify_peer),
207
+ pSSL (NULL),
208
+ pbioRead (NULL),
209
+ pbioWrite (NULL)
210
+ {
211
+ /* TODO someday: make it possible to re-use SSL contexts so we don't have to create
212
+ * a new one every time we come here.
213
+ */
214
+
215
+ Context = new SslContext_t (bIsServer, privkeyfile, certchainfile);
216
+ assert (Context);
217
+
218
+ pbioRead = BIO_new (BIO_s_mem());
219
+ assert (pbioRead);
220
+
221
+ pbioWrite = BIO_new (BIO_s_mem());
222
+ assert (pbioWrite);
223
+
224
+ pSSL = SSL_new (Context->pCtx);
225
+ assert (pSSL);
226
+ SSL_set_bio (pSSL, pbioRead, pbioWrite);
227
+
228
+ // Store a pointer to the callbacks in the SSL object so we can retrieve it later
229
+ SSL_set_ex_data(pSSL, 0, (void*) tls_state);
230
+
231
+ if (bVerifyPeer)
232
+ SSL_set_verify(pSSL, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, ssl_verify_wrapper);
233
+
234
+ if (!bIsServer)
235
+ SSL_connect (pSSL);
236
+ }
237
+
238
+
239
+
240
+ /*******************
241
+ SslBox_t::~SslBox_t
242
+ *******************/
243
+
244
+ SslBox_t::~SslBox_t()
245
+ {
246
+ // Freeing pSSL will also free the associated BIOs, so DON'T free them separately.
247
+ if (pSSL) {
248
+ if (SSL_get_shutdown (pSSL) & SSL_RECEIVED_SHUTDOWN)
249
+ SSL_shutdown (pSSL);
250
+ else
251
+ SSL_clear (pSSL);
252
+ SSL_free (pSSL);
253
+ }
254
+
255
+ delete Context;
256
+ }
257
+
258
+
259
+
260
+ /***********************
261
+ SslBox_t::PutCiphertext
262
+ ***********************/
263
+
264
+ bool SslBox_t::PutCiphertext (const char *buf, int bufsize)
265
+ {
266
+ assert (buf && (bufsize > 0));
267
+
268
+ assert (pbioRead);
269
+ int n = BIO_write (pbioRead, buf, bufsize);
270
+
271
+ return (n == bufsize) ? true : false;
272
+ }
273
+
274
+
275
+ /**********************
276
+ SslBox_t::GetPlaintext
277
+ **********************/
278
+
279
+ int SslBox_t::GetPlaintext (char *buf, int bufsize)
280
+ {
281
+ if (!SSL_is_init_finished (pSSL)) {
282
+ int e = bIsServer ? SSL_accept (pSSL) : SSL_connect (pSSL);
283
+ if (e < 0) {
284
+ int er = SSL_get_error (pSSL, e);
285
+ if (er != SSL_ERROR_WANT_READ) {
286
+ // Return -1 for a nonfatal error, -2 for an error that should force the connection down.
287
+ return (er == SSL_ERROR_SSL) ? (-2) : (-1);
288
+ }
289
+ else
290
+ return 0;
291
+ }
292
+ bHandshakeCompleted = true;
293
+ // If handshake finished, FALL THROUGH and return the available plaintext.
294
+ }
295
+
296
+ if (!SSL_is_init_finished (pSSL)) {
297
+ // We can get here if a browser abandons a handshake.
298
+ // The user can see a warning dialog and abort the connection.
299
+ cerr << "<SSL_incomp>";
300
+ return 0;
301
+ }
302
+
303
+ //cerr << "CIPH: " << SSL_get_cipher (pSSL) << endl;
304
+
305
+ int n = SSL_read (pSSL, buf, bufsize);
306
+ if (n >= 0) {
307
+ return n;
308
+ }
309
+ else {
310
+ if (SSL_get_error (pSSL, n) == SSL_ERROR_WANT_READ) {
311
+ return 0;
312
+ }
313
+ else {
314
+ return -1;
315
+ }
316
+ }
317
+
318
+ return 0;
319
+ }
320
+
321
+
322
+
323
+ /**************************
324
+ SslBox_t::CanGetCiphertext
325
+ **************************/
326
+
327
+ bool SslBox_t::CanGetCiphertext()
328
+ {
329
+ assert (pbioWrite);
330
+ return BIO_pending (pbioWrite) ? true : false;
331
+ }
332
+
333
+
334
+
335
+ /***********************
336
+ SslBox_t::GetCiphertext
337
+ ***********************/
338
+
339
+ int SslBox_t::GetCiphertext (char *buf, int bufsize)
340
+ {
341
+ assert (pbioWrite);
342
+ assert (buf && (bufsize > 0));
343
+
344
+ return BIO_read (pbioWrite, buf, bufsize);
345
+ }
346
+
347
+
348
+
349
+ /**********************
350
+ SslBox_t::PutPlaintext
351
+ **********************/
352
+
353
+ int SslBox_t::PutPlaintext (const char *buf, int bufsize)
354
+ {
355
+ // The caller will interpret the return value as the number of bytes written.
356
+ // WARNING WARNING WARNING, are there any situations in which a 0 or -1 return
357
+ // from SSL_write means we should immediately retry? The socket-machine loop
358
+ // will probably wait for a time-out cycle (perhaps a second) before re-trying.
359
+ // THIS WOULD CAUSE A PERCEPTIBLE DELAY!
360
+
361
+ /* We internally queue any outbound plaintext that can't be dispatched
362
+ * because we're in the middle of a handshake or something.
363
+ * When we get called, try to send any queued data first, and then
364
+ * send the caller's data (or queue it). We may get called with no outbound
365
+ * data, which means we try to send the outbound queue and that's all.
366
+ *
367
+ * Return >0 if we wrote any data, 0 if we didn't, and <0 for a fatal error.
368
+ * Note that if we return 0, the connection is still considered live
369
+ * and we are signalling that we have accepted the outbound data (if any).
370
+ */
371
+
372
+ OutboundQ.Push (buf, bufsize);
373
+
374
+ if (!SSL_is_init_finished (pSSL))
375
+ return 0;
376
+
377
+ bool fatal = false;
378
+ bool did_work = false;
379
+
380
+ while (OutboundQ.HasPages()) {
381
+ const char *page;
382
+ int length;
383
+ OutboundQ.Front (&page, &length);
384
+ assert (page && (length > 0));
385
+ int n = SSL_write (pSSL, page, length);
386
+ if (n > 0) {
387
+ did_work = true;
388
+ OutboundQ.PopFront();
389
+ }
390
+ else {
391
+ int er = SSL_get_error (pSSL, n);
392
+ if ((er != SSL_ERROR_WANT_READ) && (er != SSL_ERROR_WANT_WRITE))
393
+ fatal = true;
394
+ break;
395
+ }
396
+ }
397
+
398
+
399
+ if (did_work)
400
+ return 1;
401
+ else if (fatal)
402
+ return -1;
403
+ else
404
+ return 0;
405
+ }
406
+
407
+ /**********************
408
+ SslBox_t::GetPeerCert
409
+ **********************/
410
+
411
+ X509 *SslBox_t::GetPeerCert()
412
+ {
413
+ X509 *cert = NULL;
414
+
415
+ if (pSSL)
416
+ cert = SSL_get_peer_certificate(pSSL);
417
+
418
+ return cert;
419
+ }
420
+
421
+
422
+
423
+
424
+
425
+
426
+
427
+
428
+ void _DispatchCiphertext(tls_state_t *tls_state)
429
+ {
430
+ SslBox_t *SslBox = tls_state->SslBox;
431
+ assert (SslBox);
432
+
433
+ char BigBuf [2048];
434
+ bool did_work;
435
+
436
+ do {
437
+ did_work = false;
438
+
439
+ // try to drain ciphertext
440
+ while (SslBox->CanGetCiphertext()) {
441
+ int r = SslBox->GetCiphertext(BigBuf, sizeof(BigBuf));
442
+ assert (r > 0);
443
+
444
+ // Queue the data for transmit
445
+ tls_state->transmit_cb(tls_state, BigBuf, r);
446
+
447
+ did_work = true;
448
+ }
449
+
450
+ // Pump the SslBox, in case it has queued outgoing plaintext
451
+ // This will return >0 if data was written,
452
+ // 0 if no data was written, and <0 if there was a fatal error.
453
+ bool pump;
454
+ do {
455
+ pump = false;
456
+ int w = SslBox->PutPlaintext(NULL, 0);
457
+ if (w > 0) {
458
+ did_work = true;
459
+ pump = true;
460
+ } else if (w < 0) {
461
+ // Close on error
462
+ tls_state->close_cb(tls_state);
463
+ }
464
+ } while (pump);
465
+
466
+ } while (did_work);
467
+ }
468
+
469
+ void _CheckHandshakeStatus(tls_state_t *tls_state)
470
+ {
471
+ SslBox_t *SslBox = tls_state->SslBox;
472
+ // keep track of weather or not this function has been called yet
473
+ if (SslBox && tls_state->handshake_signaled == 0 && SslBox->IsHandshakeCompleted()) {
474
+ tls_state->handshake_signaled = 1;
475
+
476
+ // Optional callback
477
+ if (tls_state->handshake_cb) {
478
+ tls_state->handshake_cb(tls_state);
479
+ }
480
+ }
481
+ }
482
+
483
+
484
+
485
+
486
+
487
+
488
+
489
+
490
+ /******************
491
+ ssl_verify_wrapper
492
+ *******************/
493
+
494
+ extern "C" int ssl_verify_wrapper(int preverify_ok, X509_STORE_CTX *ctx)
495
+ {
496
+ X509 *cert;
497
+ SSL *ssl;
498
+ BUF_MEM *buf;
499
+ BIO *out;
500
+ int result;
501
+ tls_state_t *tls_state;
502
+
503
+ cert = X509_STORE_CTX_get_current_cert(ctx);
504
+ ssl = (SSL*) X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
505
+
506
+ out = BIO_new(BIO_s_mem());
507
+ PEM_write_bio_X509(out, cert);
508
+ BIO_write(out, "\0", 1);
509
+ BIO_get_mem_ptr(out, &buf);
510
+
511
+ tls_state = (tls_state_t *) SSL_get_ex_data(ssl, 0);
512
+ result = tls_state->verify_cb(tls_state, buf->data);
513
+
514
+ BIO_free(out);
515
+
516
+ return result;
517
+ }
518
+
519
+
520
+
521
+ // These are the FFI interactions:
522
+ // ------------------------------
523
+ extern "C" void start_tls(tls_state_t *tls_state, bool bIsServer, const char *PrivateKeyFilename, const char *CertChainFilename, bool bSslVerifyPeer)
524
+ {
525
+ tls_state->SslBox = new SslBox_t (tls_state, bIsServer, PrivateKeyFilename, CertChainFilename, bSslVerifyPeer);
526
+ _DispatchCiphertext(tls_state);
527
+ }
528
+
529
+ extern "C" void encrypt_data(tls_state_t *tls_state, const char *data, int length) {
530
+ SslBox_t *SslBox = tls_state->SslBox;
531
+
532
+ if (length > 0 && SslBox) {
533
+ int w = SslBox->PutPlaintext(data, length);
534
+
535
+ if (w < 0) {
536
+ // Close the connection if there was an issue
537
+ tls_state->close_cb(tls_state);
538
+ } else {
539
+ _DispatchCiphertext(tls_state);
540
+ }
541
+ }
542
+ }
543
+
544
+ extern "C" void decrypt_data(tls_state_t *tls_state, const char *buffer, int size) {
545
+ SslBox_t *SslBox = tls_state->SslBox;
546
+ if (SslBox) {
547
+ SslBox->PutCiphertext (buffer, size);
548
+
549
+ int s;
550
+ char B [2048];
551
+ while ((s = SslBox->GetPlaintext(B, sizeof(B) - 1)) > 0) {
552
+ _CheckHandshakeStatus(tls_state);
553
+ B[s] = 0;
554
+
555
+ // data recieved callback
556
+ tls_state->dispatch_cb(tls_state, B, s);
557
+ }
558
+
559
+ // If our SSL handshake had a problem, shut down the connection.
560
+ if (s == -2) {
561
+ tls_state->close_cb(tls_state);
562
+ return;
563
+ }
564
+
565
+ _CheckHandshakeStatus(tls_state);
566
+ _DispatchCiphertext(tls_state);
567
+ }
568
+ }
569
+
570
+ extern "C" X509 *get_peer_cert(tls_state_t *tls_state)
571
+ {
572
+ if (tls_state->SslBox)
573
+ return tls_state->SslBox->GetPeerCert();
574
+
575
+ return 0;
576
+ }
577
+
578
+
579
+ extern "C" void init_rubytls() {
580
+ SSL_library_init();
581
+ OpenSSL_add_ssl_algorithms();
582
+ OpenSSL_add_all_algorithms();
583
+ SSL_load_error_strings();
584
+ ERR_load_crypto_strings();
585
+
586
+ InitializeDefaultCredentials();
587
+ }