ruby-tls 1.0.2 → 1.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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_
@@ -1,587 +1,594 @@
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
- }
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
+ extern "C" void cleanup(tls_state_t *tls_state) {
579
+ SslBox_t *SslBox = tls_state->SslBox;
580
+ if (SslBox) {
581
+ tls_state->SslBox = 0;
582
+ delete SslBox;
583
+ }
584
+ }
585
+
586
+ extern "C" void init_rubytls() {
587
+ SSL_library_init();
588
+ OpenSSL_add_ssl_algorithms();
589
+ OpenSSL_add_all_algorithms();
590
+ SSL_load_error_strings();
591
+ ERR_load_crypto_strings();
592
+
593
+ InitializeDefaultCredentials();
594
+ }