ruby-tls 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6b5c1cc1141c7f766b1321efc906a5e7c80b43bc
4
+ data.tar.gz: 7b4ebd9ce165bb26a5b0c0b90ede1d25548cafe5
5
+ SHA512:
6
+ metadata.gz: dd6c9eabcc26971ba5f2c8d6c8776578d78b52a09f78a65c16037099056b9d6db0b2863d9fe89a5ef36510478c392edb734b35218f58a027ec249d422fa1826e
7
+ data.tar.gz: 75788c47a3e44f23db722fea7ca6b18361603c333b3adeb6f68a0fef4a1f30d0a48ec91393807c6441d416adbe8d790d28b7b275898c8e7d920a76dea24de684
@@ -0,0 +1,60 @@
1
+ EventMachine is copyrighted free software owned by Francis Cianfrocca
2
+ (blackhedd ... gmail.com). The Owner of this software permits you to
3
+ redistribute and/or modify the software under either the terms of the GPL
4
+ version 2 (see the file GPL), or the conditions below ("Ruby License"):
5
+
6
+ 1. You may make and give away verbatim copies of the source form of this
7
+ software without restriction, provided that you retain ALL of the
8
+ original copyright notices and associated disclaimers.
9
+
10
+ 2. You may modify your copy of the software in any way, provided that
11
+ you do at least ONE of the following:
12
+
13
+ a) place your modifications in the Public Domain or otherwise
14
+ make them Freely Available, such as by posting said
15
+ modifications to Usenet or an equivalent medium, or by allowing
16
+ the author to include your modifications in the software.
17
+
18
+ b) use the modified software only within your corporation or
19
+ organization.
20
+
21
+ c) give non-standard binaries non-standard names, with
22
+ instructions on where to get the original software distribution.
23
+
24
+ d) make other distribution arrangements with the Owner.
25
+
26
+ 3. You may distribute the software in object code or binary form,
27
+ provided that you do at least ONE of the following:
28
+
29
+ a) distribute the binaries and library files of the software,
30
+ together with instructions (in a manual page or equivalent)
31
+ on where to get the original distribution.
32
+
33
+ b) accompany the distribution with the machine-readable source of
34
+ the software.
35
+
36
+ c) give non-standard binaries non-standard names, with
37
+ instructions on where to get the original software distribution.
38
+
39
+ d) make other distribution arrangements with the Owner.
40
+
41
+ 4. You may modify and include parts of the software into any other
42
+ software (possibly commercial), provided you comply with the terms in
43
+ Sections 1, 2, and 3 above. But some files in the distribution
44
+ are not written by the Owner, so they may be made available to you
45
+ under different terms.
46
+
47
+ For the list of those files and their copying conditions, see the
48
+ file LEGAL.
49
+
50
+ 5. The scripts and library files supplied as input to or produced as
51
+ output from the software do not automatically fall under the
52
+ copyright of the software, but belong to whoever generated them,
53
+ and may be sold commercially, and may be aggregated with this
54
+ software.
55
+
56
+ 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
57
+ IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
58
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
59
+ PURPOSE.
60
+
@@ -0,0 +1,69 @@
1
+ # ruby-tls
2
+
3
+ Ruby-TLS decouples the management of encrypted communications, putting you in charge of the transport layer. It can be used as an alternative to Ruby's SSLSocket.
4
+
5
+
6
+ ## Install the gem
7
+
8
+ Install it with [RubyGems](https://rubygems.org/)
9
+
10
+ gem install ruby-tls
11
+
12
+ or add this to your Gemfile if you use [Bundler](http://gembundler.com/):
13
+
14
+ gem "ruby-tls"
15
+
16
+
17
+ Windows users will require an installation of OpenSSL (32bit or 64bit matching the Ruby installation) and be setup with [Ruby Installers DevKit](http://rubyinstaller.org/downloads/)
18
+
19
+
20
+ ## Usage
21
+
22
+ ```ruby
23
+ require 'rubygems'
24
+ require 'ruby-tls'
25
+
26
+ #
27
+ # Create a new TLS connection and attach callbacks
28
+ #
29
+ connection = RubyTls::Connection.new do |state|
30
+ state.handshake_cb do
31
+ puts "TLS handshake complete"
32
+ end
33
+
34
+ state.transmit_cb do |data|
35
+ puts "Data for transmission to remote"
36
+ end
37
+
38
+ state.dispatch_cb do |data|
39
+ puts "Clear text data that has been decrypted"
40
+ end
41
+
42
+ state.close_cb do |inst, data|
43
+ puts "An error occurred, the transport layer should be shutdown"
44
+ end
45
+ end
46
+
47
+ #
48
+ # Init the handshake
49
+ #
50
+ connection.start
51
+
52
+ #
53
+ # Start sending data to the remote, this will trigger the
54
+ # transmit_cb with encrypted data to send.
55
+ #
56
+ connection.encrypt('client request')
57
+
58
+ #
59
+ # Similarly when data is received from the remote it should be
60
+ # passed to connection.decrypt where the dispatch_cb will be
61
+ # called with clear text
62
+ #
63
+ ```
64
+
65
+
66
+ ## License and copyright
67
+
68
+ The core SSL code was originally extracted and isolated from [EventMachine](https://github.com/eventmachine/eventmachine/). So is licensed under the same terms, either the GPL or Ruby's License.
69
+
@@ -0,0 +1,19 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'rspec/core/rake_task'
4
+
5
+ task :default => [:compile, :test]
6
+
7
+ task :compile do
8
+ protect = ['ssl.cpp', 'ssl.h', 'page.cpp', 'page.h']
9
+ Dir["ext/tls/**/*"].each do |file|
10
+ begin
11
+ next if protect.include? File.basename(file)
12
+ FileUtils.rm file
13
+ rescue
14
+ end
15
+ end
16
+ system 'cd ext && rake'
17
+ end
18
+
19
+ RSpec::Core::RakeTask.new(:test)
@@ -0,0 +1,18 @@
1
+ require 'ffi-compiler/compile_task'
2
+
3
+ FFI::Compiler::CompileTask.new('ruby-tls-ext') do |t|
4
+ t.cflags << "-Wall -Wextra -O3"
5
+ t.cflags << "-D_GNU_SOURCE=1" if RbConfig::CONFIG["host_os"].downcase =~ /mingw/
6
+ t.cflags << "-arch x86_64 -arch i386" if t.platform.mac?
7
+ t.ldflags << "-arch x86_64 -arch i386" if t.platform.mac?
8
+
9
+ # Link to OpenSSL
10
+ if FFI::Platform.windows?
11
+ path = File.dirname(ENV["OPENSSL_CONF"])
12
+ path = File.expand_path("../", path)
13
+ t.cflags << "-I \"#{path}/include\""
14
+ t.ldflags << "-L\"#{path}/lib\" -lssleay32 -llibeay32"
15
+ else
16
+ t.ldflags << "-lssl -lcrypto"
17
+ end
18
+ end
@@ -0,0 +1,107 @@
1
+ /*****************************************************************************
2
+
3
+ $Id$
4
+
5
+ File: page.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
+
21
+ #include "page.h"
22
+
23
+
24
+ /******************
25
+ PageList::PageList
26
+ ******************/
27
+
28
+ PageList::PageList()
29
+ {
30
+ }
31
+
32
+
33
+ /*******************
34
+ PageList::~PageList
35
+ *******************/
36
+
37
+ PageList::~PageList()
38
+ {
39
+ while (HasPages())
40
+ PopFront();
41
+ }
42
+
43
+
44
+ /***************
45
+ PageList::Front
46
+ ***************/
47
+
48
+ void PageList::Front (const char **page, int *length)
49
+ {
50
+ assert (page && length);
51
+
52
+ if (HasPages()) {
53
+ Page p = Pages.front();
54
+ *page = p.Buffer;
55
+ *length = p.Size;
56
+ }
57
+ else {
58
+ *page = NULL;
59
+ *length = 0;
60
+ }
61
+ }
62
+
63
+
64
+ /******************
65
+ PageList::PopFront
66
+ ******************/
67
+
68
+ void PageList::PopFront()
69
+ {
70
+ if (HasPages()) {
71
+ Page p = Pages.front();
72
+ Pages.pop_front();
73
+ if (p.Buffer)
74
+ free ((void*)p.Buffer);
75
+ }
76
+ }
77
+
78
+
79
+ /******************
80
+ PageList::HasPages
81
+ ******************/
82
+
83
+ bool PageList::HasPages()
84
+ {
85
+ return (Pages.size() > 0) ? true : false;
86
+ }
87
+
88
+
89
+ /**************
90
+ PageList::Push
91
+ **************/
92
+
93
+ void PageList::Push (const char *buf, int size)
94
+ {
95
+ if (buf && (size > 0)) {
96
+ char *copy = (char*) malloc (size);
97
+ if (!copy)
98
+ throw runtime_error ("no memory in pagelist");
99
+ memcpy (copy, buf, size);
100
+ Pages.push_back (Page (copy, size));
101
+ }
102
+ }
103
+
104
+
105
+
106
+
107
+
@@ -0,0 +1,62 @@
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
+
29
+ #if defined(WIN32) || defined(_WIN32) || defined(__WIN32) && !defined(__CYGWIN__)
30
+ #include <windows.h>
31
+ #endif
32
+
33
+ using namespace std;
34
+
35
+
36
+ /**************
37
+ class PageList
38
+ **************/
39
+
40
+ class PageList
41
+ {
42
+ struct Page {
43
+ Page (const char *b, size_t s): Buffer(b), Size(s) {}
44
+ const char *Buffer;
45
+ size_t Size;
46
+ };
47
+
48
+ public:
49
+ PageList();
50
+ virtual ~PageList();
51
+
52
+ void Push (const char*, int);
53
+ bool HasPages();
54
+ void Front (const char**, int*);
55
+ void PopFront();
56
+
57
+ private:
58
+ deque<Page> Pages;
59
+ };
60
+
61
+
62
+ #endif // __PageManager__H_
@@ -0,0 +1,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
+
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
+