ruby-tls 1.0.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/ext/tls/ssl.cpp CHANGED
@@ -1,591 +1,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
- 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
-
175
- if (is_server) {
176
- SSL_CTX_sess_set_cache_size (pCtx, 128);
177
- SSL_CTX_set_session_id_context (pCtx, (unsigned char*)"ruby-tls", 8);
178
- }
179
- else {
180
- int e;
181
- if (privkeyfile.length() > 0) {
182
- e = SSL_CTX_use_PrivateKey_file (pCtx, privkeyfile.c_str(), SSL_FILETYPE_PEM);
183
- if (e <= 0) ERR_print_errors_fp(stderr);
184
- assert (e > 0);
185
- }
186
- if (certchainfile.length() > 0) {
187
- e = SSL_CTX_use_certificate_chain_file (pCtx, certchainfile.c_str());
188
- if (e <= 0) ERR_print_errors_fp(stderr);
189
- assert (e > 0);
190
- }
191
- }
192
- }
193
-
194
-
195
-
196
- /***************************
197
- SslContext_t::~SslContext_t
198
- ***************************/
199
-
200
- SslContext_t::~SslContext_t()
201
- {
202
- if (pCtx)
203
- SSL_CTX_free (pCtx);
204
- if (PrivateKey)
205
- EVP_PKEY_free (PrivateKey);
206
- if (Certificate)
207
- X509_free (Certificate);
208
- }
209
-
210
-
211
-
212
- /******************
213
- SslBox_t::SslBox_t
214
- ******************/
215
-
216
- SslBox_t::SslBox_t (tls_state_t *tls_state, bool is_server, const string &privkeyfile, const string &certchainfile, bool verify_peer):
217
- bIsServer (is_server),
218
- bHandshakeCompleted (false),
219
- bVerifyPeer (verify_peer),
220
- pSSL (NULL),
221
- pbioRead (NULL),
222
- pbioWrite (NULL)
223
- {
224
- /* TODO someday: make it possible to re-use SSL contexts so we don't have to create
225
- * a new one every time we come here.
226
- */
227
-
228
- Context = new SslContext_t (bIsServer, privkeyfile, certchainfile);
229
- assert (Context);
230
-
231
- pbioRead = BIO_new (BIO_s_mem());
232
- assert (pbioRead);
233
-
234
- pbioWrite = BIO_new (BIO_s_mem());
235
- assert (pbioWrite);
236
-
237
- pSSL = SSL_new (Context->pCtx);
238
- assert (pSSL);
239
- SSL_set_bio (pSSL, pbioRead, pbioWrite);
240
-
241
- // Store a pointer to the callbacks in the SSL object so we can retrieve it later
242
- SSL_set_ex_data(pSSL, 0, (void*) tls_state);
243
-
244
- if (bVerifyPeer)
245
- SSL_set_verify(pSSL, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, ssl_verify_wrapper);
246
-
247
- if (!bIsServer)
248
- SSL_connect (pSSL);
249
- }
250
-
251
-
252
-
253
- /*******************
254
- SslBox_t::~SslBox_t
255
- *******************/
256
-
257
- SslBox_t::~SslBox_t()
258
- {
259
- // Freeing pSSL will also free the associated BIOs, so DON'T free them separately.
260
- if (pSSL) {
261
- if (SSL_get_shutdown (pSSL) & SSL_RECEIVED_SHUTDOWN)
262
- SSL_shutdown (pSSL);
263
- else
264
- SSL_clear (pSSL);
265
- SSL_free (pSSL);
266
- }
267
-
268
- delete Context;
269
- }
270
-
271
-
272
-
273
- /***********************
274
- SslBox_t::PutCiphertext
275
- ***********************/
276
-
277
- bool SslBox_t::PutCiphertext (const char *buf, int bufsize)
278
- {
279
- assert (buf && (bufsize > 0));
280
-
281
- assert (pbioRead);
282
- int n = BIO_write (pbioRead, buf, bufsize);
283
-
284
- return (n == bufsize) ? true : false;
285
- }
286
-
287
-
288
- /**********************
289
- SslBox_t::GetPlaintext
290
- **********************/
291
-
292
- int SslBox_t::GetPlaintext (char *buf, int bufsize)
293
- {
294
- if (!SSL_is_init_finished (pSSL)) {
295
- int e = bIsServer ? SSL_accept (pSSL) : SSL_connect (pSSL);
296
- if (e < 0) {
297
- int er = SSL_get_error (pSSL, e);
298
- if (er != SSL_ERROR_WANT_READ) {
299
- // Return -1 for a nonfatal error, -2 for an error that should force the connection down.
300
- return (er == SSL_ERROR_SSL) ? (-2) : (-1);
301
- }
302
- else
303
- return 0;
304
- }
305
- bHandshakeCompleted = true;
306
- // If handshake finished, FALL THROUGH and return the available plaintext.
307
- }
308
-
309
- if (!SSL_is_init_finished (pSSL)) {
310
- // We can get here if a browser abandons a handshake.
311
- // The user can see a warning dialog and abort the connection.
312
- cerr << "<SSL_incomp>";
313
- return 0;
314
- }
315
-
316
- //cerr << "CIPH: " << SSL_get_cipher (pSSL) << endl;
317
-
318
- int n = SSL_read (pSSL, buf, bufsize);
319
- if (n >= 0) {
320
- return n;
321
- }
322
- else {
323
- if (SSL_get_error (pSSL, n) == SSL_ERROR_WANT_READ) {
324
- return 0;
325
- }
326
- else {
327
- return -1;
328
- }
329
- }
330
-
331
- return 0;
332
- }
333
-
334
-
335
-
336
- /**************************
337
- SslBox_t::CanGetCiphertext
338
- **************************/
339
-
340
- bool SslBox_t::CanGetCiphertext()
341
- {
342
- assert (pbioWrite);
343
- return BIO_pending (pbioWrite) ? true : false;
344
- }
345
-
346
-
347
-
348
- /***********************
349
- SslBox_t::GetCiphertext
350
- ***********************/
351
-
352
- int SslBox_t::GetCiphertext (char *buf, int bufsize)
353
- {
354
- assert (pbioWrite);
355
- assert (buf && (bufsize > 0));
356
-
357
- return BIO_read (pbioWrite, buf, bufsize);
358
- }
359
-
360
-
361
-
362
- /**********************
363
- SslBox_t::PutPlaintext
364
- **********************/
365
-
366
- int SslBox_t::PutPlaintext (const char *buf, int bufsize)
367
- {
368
- // The caller will interpret the return value as the number of bytes written.
369
- // WARNING WARNING WARNING, are there any situations in which a 0 or -1 return
370
- // from SSL_write means we should immediately retry? The socket-machine loop
371
- // will probably wait for a time-out cycle (perhaps a second) before re-trying.
372
- // THIS WOULD CAUSE A PERCEPTIBLE DELAY!
373
-
374
- /* We internally queue any outbound plaintext that can't be dispatched
375
- * because we're in the middle of a handshake or something.
376
- * When we get called, try to send any queued data first, and then
377
- * send the caller's data (or queue it). We may get called with no outbound
378
- * data, which means we try to send the outbound queue and that's all.
379
- *
380
- * Return >0 if we wrote any data, 0 if we didn't, and <0 for a fatal error.
381
- * Note that if we return 0, the connection is still considered live
382
- * and we are signalling that we have accepted the outbound data (if any).
383
- */
384
-
385
- OutboundQ.Push (buf, bufsize);
386
-
387
- if (!SSL_is_init_finished (pSSL))
388
- return 0;
389
-
390
- bool fatal = false;
391
- bool did_work = false;
392
-
393
- while (OutboundQ.HasPages()) {
394
- const char *page;
395
- int length;
396
- OutboundQ.Front (&page, &length);
397
- assert (page && (length > 0));
398
- int n = SSL_write (pSSL, page, length);
399
- if (n > 0) {
400
- did_work = true;
401
- OutboundQ.PopFront();
402
- }
403
- else {
404
- int er = SSL_get_error (pSSL, n);
405
- if ((er != SSL_ERROR_WANT_READ) && (er != SSL_ERROR_WANT_WRITE))
406
- fatal = true;
407
- break;
408
- }
409
- }
410
-
411
-
412
- if (did_work)
413
- return 1;
414
- else if (fatal)
415
- return -1;
416
- else
417
- return 0;
418
- }
419
-
420
- /**********************
421
- SslBox_t::GetPeerCert
422
- **********************/
423
-
424
- X509 *SslBox_t::GetPeerCert()
425
- {
426
- X509 *cert = NULL;
427
-
428
- if (pSSL)
429
- cert = SSL_get_peer_certificate(pSSL);
430
-
431
- return cert;
432
- }
433
-
434
-
435
-
436
-
437
-
438
-
439
-
440
-
441
- void _DispatchCiphertext(tls_state_t *tls_state)
442
- {
443
- SslBox_t *SslBox = tls_state->SslBox;
444
- assert (SslBox);
445
-
446
- char BigBuf [2048];
447
- bool did_work;
448
-
449
- do {
450
- did_work = false;
451
-
452
- // try to drain ciphertext
453
- while (SslBox->CanGetCiphertext()) {
454
- int r = SslBox->GetCiphertext(BigBuf, sizeof(BigBuf));
455
- assert (r > 0);
456
-
457
- // Queue the data for transmit
458
- tls_state->transmit_cb(tls_state, BigBuf, r);
459
-
460
- did_work = true;
461
- }
462
-
463
- // Pump the SslBox, in case it has queued outgoing plaintext
464
- // This will return >0 if data was written,
465
- // 0 if no data was written, and <0 if there was a fatal error.
466
- bool pump;
467
- do {
468
- pump = false;
469
- int w = SslBox->PutPlaintext(NULL, 0);
470
- if (w > 0) {
471
- did_work = true;
472
- pump = true;
473
- } else if (w < 0) {
474
- // Close on error
475
- tls_state->close_cb(tls_state);
476
- }
477
- } while (pump);
478
-
479
- } while (did_work);
480
- }
481
-
482
- void _CheckHandshakeStatus(tls_state_t *tls_state)
483
- {
484
- SslBox_t *SslBox = tls_state->SslBox;
485
- // keep track of weather or not this function has been called yet
486
- if (SslBox && tls_state->handshake_signaled == 0 && SslBox->IsHandshakeCompleted()) {
487
- tls_state->handshake_signaled = 1;
488
-
489
- // Optional callback
490
- if (tls_state->handshake_cb) {
491
- tls_state->handshake_cb(tls_state);
492
- }
493
- }
494
- }
495
-
496
-
497
-
498
-
499
-
500
-
501
-
502
-
503
- /******************
504
- ssl_verify_wrapper
505
- *******************/
506
-
507
- extern "C" int ssl_verify_wrapper(int preverify_ok, X509_STORE_CTX *ctx)
508
- {
509
- X509 *cert;
510
- SSL *ssl;
511
- BUF_MEM *buf;
512
- BIO *out;
513
- int result;
514
- tls_state_t *tls_state;
515
-
516
- cert = X509_STORE_CTX_get_current_cert(ctx);
517
- ssl = (SSL*) X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
518
-
519
- out = BIO_new(BIO_s_mem());
520
- PEM_write_bio_X509(out, cert);
521
- BIO_write(out, "\0", 1);
522
- BIO_get_mem_ptr(out, &buf);
523
-
524
- tls_state = (tls_state_t *) SSL_get_ex_data(ssl, 0);
525
- result = tls_state->verify_cb(tls_state, buf->data);
526
-
527
- BIO_free(out);
528
-
529
- return result;
530
- }
531
-
532
-
533
-
534
- // These are the FFI interactions:
535
- // ------------------------------
536
- extern "C" void start_tls(tls_state_t *tls_state, bool bIsServer, const char *PrivateKeyFilename, const char *CertChainFilename, bool bSslVerifyPeer)
537
- {
538
- tls_state->SslBox = new SslBox_t (tls_state, bIsServer, PrivateKeyFilename, CertChainFilename, bSslVerifyPeer);
539
- _DispatchCiphertext(tls_state);
540
- }
541
-
542
- extern "C" void encrypt_data(tls_state_t *tls_state, const char *data, int length) {
543
- SslBox_t *SslBox = tls_state->SslBox;
544
-
545
- if (length > 0 && SslBox) {
546
- int w = SslBox->PutPlaintext(data, length);
547
-
548
- if (w < 0) {
549
- // Close the connection if there was an issue
550
- tls_state->close_cb(tls_state);
551
- } else {
552
- _DispatchCiphertext(tls_state);
553
- }
554
- }
555
- }
556
-
557
- extern "C" void decrypt_data(tls_state_t *tls_state, const char *buffer, int size) {
558
- SslBox_t *SslBox = tls_state->SslBox;
559
- if (SslBox) {
560
- SslBox->PutCiphertext (buffer, size);
561
-
562
- int s;
563
- char B [2048];
564
- while ((s = SslBox->GetPlaintext(B, sizeof(B) - 1)) > 0) {
565
- _CheckHandshakeStatus(tls_state);
566
- B[s] = 0;
567
-
568
- // data recieved callback
569
- tls_state->dispatch_cb(tls_state, B, s);
570
- }
571
-
572
- // If our SSL handshake had a problem, shut down the connection.
573
- if (s == -2) {
574
- tls_state->close_cb(tls_state);
575
- return;
576
- }
577
-
578
- _CheckHandshakeStatus(tls_state);
579
- _DispatchCiphertext(tls_state);
580
- }
581
- }
582
-
583
- extern "C" X509 *get_peer_cert(tls_state_t *tls_state)
584
- {
585
- if (tls_state->SslBox)
586
- return tls_state->SslBox->GetPeerCert();
587
-
588
- return 0;
589
- }
590
-
591
-
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
+