iodine 0.7.59 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.clang-format +15 -0
- data/.rspec +1 -0
- data/.yardopts +1 -3
- data/CHANGELOG.md +132 -12
- data/Gemfile +5 -7
- data/LICENSE.txt +1 -1
- data/README.md +147 -671
- data/Rakefile +9 -16
- data/examples/chatroom.nru +9 -0
- data/examples/client +74 -0
- data/examples/config-hello.nru +9 -0
- data/examples/config-hello.ru +11 -0
- data/examples/config-hijack.ru +13 -0
- data/examples/config-shootout.ru +43 -0
- data/examples/config.nru +120 -0
- data/examples/config.rb +37 -0
- data/examples/config.ru +42 -53
- data/examples/fake +24 -0
- data/examples/hello_neorack +137 -0
- data/examples/routes.nru +29 -0
- data/examples/routes.ru +42 -0
- data/exe/iodine +218 -258
- data/ext/iodine/extconf.rb +27 -100
- data/ext/iodine/fio-stl.h +128336 -0
- data/ext/iodine/fio-stl.md +26396 -0
- data/ext/iodine/iodine.c +213 -1393
- data/ext/iodine/iodine.h +250 -55
- data/ext/iodine/iodine_arg_helper.h +329 -0
- data/ext/iodine/iodine_caller.h +234 -25
- data/ext/iodine/iodine_cli.h +434 -0
- data/ext/iodine/iodine_compression.h +312 -0
- data/ext/iodine/iodine_connection.h +3484 -34
- data/ext/iodine/iodine_core.h +363 -0
- data/ext/iodine/iodine_crypto.h +1441 -0
- data/ext/iodine/iodine_defer.h +225 -4
- data/ext/iodine/iodine_json.h +738 -4
- data/ext/iodine/iodine_listener.h +263 -0
- data/ext/iodine/iodine_log.h +297 -0
- data/ext/iodine/iodine_minimap.h +543 -0
- data/ext/iodine/iodine_mustache.h +461 -4
- data/ext/iodine/iodine_pubsub_eng.h +729 -0
- data/ext/iodine/iodine_pubsub_history.h +547 -0
- data/ext/iodine/iodine_pubsub_msg.h +228 -0
- data/ext/iodine/iodine_redis.h +322 -0
- data/ext/iodine/iodine_store.h +418 -16
- data/ext/iodine/iodine_threads.h +279 -0
- data/ext/iodine/iodine_tls.h +515 -6
- data/ext/iodine/iodine_utils.h +923 -0
- data/iodine.gemspec +37 -42
- data/lib/iodine/benchmark.rb +1690 -0
- data/lib/iodine/documentation.rb +3085 -0
- data/lib/iodine/version.rb +17 -1
- data/lib/iodine.rb +54 -255
- data/logo.svg +36 -0
- data/mustache.md +55 -0
- data/sig/iodine.rbs +4 -0
- metadata +58 -184
- data/.github/ISSUE_TEMPLATE/bug_report.md +0 -40
- data/.github/workflows/ruby.yml +0 -42
- data/.gitignore +0 -20
- data/LIMITS.md +0 -41
- data/SPEC-PubSub-Draft.md +0 -159
- data/SPEC-WebSocket-Draft.md +0 -239
- data/bin/console +0 -22
- data/bin/info.md +0 -353
- data/bin/mustache_bench.rb +0 -100
- data/bin/poc/Gemfile.lock +0 -23
- data/bin/poc/README.md +0 -37
- data/bin/poc/config.ru +0 -66
- data/bin/poc/gemfile +0 -1
- data/bin/poc/www/index.html +0 -57
- data/examples/async_task.ru +0 -92
- data/examples/bates/README.md +0 -3
- data/examples/bates/config.ru +0 -342
- data/examples/bates/david+bold.pdf +0 -0
- data/examples/bates/public/drop-pdf.png +0 -0
- data/examples/bates/public/index.html +0 -600
- data/examples/echo.ru +0 -59
- data/examples/etag.ru +0 -16
- data/examples/hello.ru +0 -29
- data/examples/pubsub_engine.ru +0 -81
- data/examples/rack3.ru +0 -12
- data/examples/redis.ru +0 -70
- data/examples/shootout.ru +0 -73
- data/examples/sub-protocols.ru +0 -90
- data/examples/tcp_client.rb +0 -66
- data/examples/x-sendfile.ru +0 -14
- data/ext/iodine/fio.c +0 -11995
- data/ext/iodine/fio.h +0 -6371
- data/ext/iodine/fio_cli.c +0 -431
- data/ext/iodine/fio_cli.h +0 -189
- data/ext/iodine/fio_json_parser.h +0 -688
- data/ext/iodine/fio_siphash.c +0 -157
- data/ext/iodine/fio_siphash.h +0 -37
- data/ext/iodine/fio_tls.h +0 -129
- data/ext/iodine/fio_tls_missing.c +0 -649
- data/ext/iodine/fio_tls_openssl.c +0 -1106
- data/ext/iodine/fio_tmpfile.h +0 -50
- data/ext/iodine/fiobj.h +0 -44
- data/ext/iodine/fiobj4fio.h +0 -21
- data/ext/iodine/fiobj_ary.c +0 -333
- data/ext/iodine/fiobj_ary.h +0 -139
- data/ext/iodine/fiobj_data.c +0 -1187
- data/ext/iodine/fiobj_data.h +0 -167
- data/ext/iodine/fiobj_hash.c +0 -409
- data/ext/iodine/fiobj_hash.h +0 -176
- data/ext/iodine/fiobj_json.c +0 -622
- data/ext/iodine/fiobj_json.h +0 -68
- data/ext/iodine/fiobj_mem.h +0 -71
- data/ext/iodine/fiobj_mustache.c +0 -317
- data/ext/iodine/fiobj_mustache.h +0 -62
- data/ext/iodine/fiobj_numbers.c +0 -344
- data/ext/iodine/fiobj_numbers.h +0 -127
- data/ext/iodine/fiobj_str.c +0 -433
- data/ext/iodine/fiobj_str.h +0 -172
- data/ext/iodine/fiobject.c +0 -620
- data/ext/iodine/fiobject.h +0 -655
- data/ext/iodine/hpack.h +0 -1923
- data/ext/iodine/http.c +0 -2743
- data/ext/iodine/http.h +0 -1002
- data/ext/iodine/http1.c +0 -910
- data/ext/iodine/http1.h +0 -29
- data/ext/iodine/http1_parser.h +0 -878
- data/ext/iodine/http_internal.c +0 -1279
- data/ext/iodine/http_internal.h +0 -237
- data/ext/iodine/http_mime_parser.h +0 -350
- data/ext/iodine/iodine_caller.c +0 -230
- data/ext/iodine/iodine_connection.c +0 -941
- data/ext/iodine/iodine_defer.c +0 -420
- data/ext/iodine/iodine_fiobj2rb.h +0 -120
- data/ext/iodine/iodine_helpers.c +0 -282
- data/ext/iodine/iodine_helpers.h +0 -12
- data/ext/iodine/iodine_http.c +0 -1181
- data/ext/iodine/iodine_http.h +0 -23
- data/ext/iodine/iodine_json.c +0 -302
- data/ext/iodine/iodine_mustache.c +0 -567
- data/ext/iodine/iodine_pubsub.c +0 -580
- data/ext/iodine/iodine_pubsub.h +0 -26
- data/ext/iodine/iodine_rack_io.c +0 -273
- data/ext/iodine/iodine_rack_io.h +0 -20
- data/ext/iodine/iodine_store.c +0 -146
- data/ext/iodine/iodine_tcp.c +0 -346
- data/ext/iodine/iodine_tcp.h +0 -13
- data/ext/iodine/iodine_tls.c +0 -261
- data/ext/iodine/mustache_parser.h +0 -1546
- data/ext/iodine/redis_engine.c +0 -957
- data/ext/iodine/redis_engine.h +0 -79
- data/ext/iodine/resp_parser.h +0 -317
- data/ext/iodine/websocket_parser.h +0 -506
- data/ext/iodine/websockets.c +0 -743
- data/ext/iodine/websockets.h +0 -185
- data/lib/iodine/connection.rb +0 -61
- data/lib/iodine/json.rb +0 -42
- data/lib/iodine/mustache.rb +0 -113
- data/lib/iodine/pubsub.rb +0 -55
- data/lib/iodine/rack_utils.rb +0 -43
- data/lib/iodine/tls.rb +0 -16
- data/lib/rack/handler/iodine.rb +0 -33
- data/logo.png +0 -0
data/ext/iodine/iodine_tls.h
CHANGED
|
@@ -1,13 +1,522 @@
|
|
|
1
|
-
#ifndef
|
|
2
|
-
|
|
1
|
+
#ifndef H___IODINE_TLS___H
|
|
2
|
+
/** ****************************************************************************
|
|
3
|
+
Iodine::TLS - TLS/SSL Context Management
|
|
3
4
|
|
|
5
|
+
This module provides TLS/SSL support for Iodine connections, wrapping the
|
|
6
|
+
facil.io TLS functionality and exposing it to Ruby.
|
|
7
|
+
|
|
8
|
+
## TLS Backend Options
|
|
9
|
+
|
|
10
|
+
Iodine supports two TLS backends:
|
|
11
|
+
|
|
12
|
+
1. **Embedded TLS 1.3** (`:iodine`) - Always available via fio-stl.h
|
|
13
|
+
- Lightweight, no external dependencies
|
|
14
|
+
- TLS 1.3 only (modern, secure)
|
|
15
|
+
|
|
16
|
+
2. **OpenSSL** (`:openssl`) - Available if compiled with OpenSSL support
|
|
17
|
+
- Full TLS version support (1.0, 1.1, 1.2, 1.3)
|
|
18
|
+
- Broader compatibility with older clients
|
|
19
|
+
|
|
20
|
+
## Ruby API
|
|
21
|
+
|
|
22
|
+
### Iodine::TLS Class
|
|
23
|
+
|
|
24
|
+
tls = Iodine::TLS.new
|
|
25
|
+
tls.add_cert(name: "example.com")
|
|
26
|
+
tls.add_cert(cert: "cert.pem", key: "key.pem")
|
|
27
|
+
tls.add_cert(cert: "cert.pem", key: "key.pem", password: ENV['TLS_PASS'])
|
|
28
|
+
|
|
29
|
+
### Class Methods
|
|
30
|
+
|
|
31
|
+
- `Iodine::TLS.default` - Returns the current default TLS backend (:iodine or
|
|
32
|
+
:openssl)
|
|
33
|
+
- `Iodine::TLS.default = :iodine` - Sets the default TLS backend
|
|
34
|
+
|
|
35
|
+
### Instance Methods
|
|
36
|
+
|
|
37
|
+
- `add_cert(name:, cert:, key:, password:)` - Adds a certificate to the TLS
|
|
38
|
+
context. If only `name` is provided, a self-signed certificate is generated.
|
|
39
|
+
- `trust(path = nil)` - Adds a public certificate (PEM file) to the trust
|
|
40
|
+
list, enabling peer (client) certificate authentication. Without a path,
|
|
41
|
+
trusts the system's default trust registry.
|
|
42
|
+
|
|
43
|
+
### Constants
|
|
44
|
+
|
|
45
|
+
- `Iodine::TLS::SUPPORTED` - Always `true` (embedded TLS always available)
|
|
46
|
+
- `Iodine::TLS::OPENSSL_AVAILABLE` - `true` if OpenSSL support was compiled in
|
|
47
|
+
- `Iodine::TLS::EMBEDDED_AVAILABLE` - Always `true`
|
|
48
|
+
|
|
49
|
+
## Usage with Iodine.listen
|
|
50
|
+
|
|
51
|
+
# Using default TLS (auto-generates self-signed cert)
|
|
52
|
+
Iodine.listen(url: "https://0.0.0.0:3000", handler: app)
|
|
53
|
+
|
|
54
|
+
# Using custom TLS context
|
|
55
|
+
tls = Iodine::TLS.new
|
|
56
|
+
tls.add_cert(cert: "server.pem", key: "server.key")
|
|
57
|
+
Iodine.listen(url: "https://0.0.0.0:3000", handler: app, tls: tls)
|
|
58
|
+
|
|
59
|
+
# Specifying TLS backend
|
|
60
|
+
Iodine.listen(url: "https://0.0.0.0:3000", handler: app, tls_io: :openssl)
|
|
61
|
+
|
|
62
|
+
***************************************************************************** */
|
|
63
|
+
#define H___IODINE_TLS___H
|
|
4
64
|
#include "iodine.h"
|
|
5
65
|
|
|
6
|
-
|
|
66
|
+
static VALUE iodine_rb_TLS_CERTIFICATE;
|
|
67
|
+
|
|
68
|
+
/* *****************************************************************************
|
|
69
|
+
TLS Wrapper - Internal Implementation
|
|
70
|
+
***************************************************************************** */
|
|
71
|
+
|
|
72
|
+
static void iodine_tls_free(void *ptr_) {
|
|
73
|
+
fio_io_tls_s **p = (fio_io_tls_s **)ptr_;
|
|
74
|
+
fio_io_tls_free(*p);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
static const rb_data_type_t IODINE_TLS_DATA_TYPE = {
|
|
78
|
+
.wrap_struct_name = "IodineTLS",
|
|
79
|
+
.function =
|
|
80
|
+
{
|
|
81
|
+
.dfree = iodine_tls_free,
|
|
82
|
+
},
|
|
83
|
+
.data = NULL,
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
static VALUE iodine_tls_alloc(VALUE klass) {
|
|
87
|
+
fio_io_tls_s **tls;
|
|
88
|
+
VALUE o =
|
|
89
|
+
TypedData_Make_Struct(klass, fio_io_tls_s *, &IODINE_TLS_DATA_TYPE, tls);
|
|
90
|
+
*tls = fio_io_tls_new();
|
|
91
|
+
if (!*tls)
|
|
92
|
+
rb_raise(rb_eNoMemError, "Couldn't allocate resources for TLS object");
|
|
93
|
+
return o;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
static fio_io_tls_s *iodine_tls_get(VALUE self) {
|
|
97
|
+
fio_io_tls_s **p;
|
|
98
|
+
TypedData_Get_Struct(self, fio_io_tls_s *, &IODINE_TLS_DATA_TYPE, p);
|
|
99
|
+
return *p;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/* *****************************************************************************
|
|
103
|
+
The Functions to be Wrapped
|
|
104
|
+
***************************************************************************** */
|
|
105
|
+
|
|
106
|
+
#if 0
|
|
107
|
+
/**
|
|
108
|
+
* Adds a certificate a new SSL/TLS context / settings object (SNI support).
|
|
109
|
+
*
|
|
110
|
+
* fio_io_tls_cert_add(tls, "www.example.com",
|
|
111
|
+
* "public_key.pem",
|
|
112
|
+
* "private_key.pem", NULL );
|
|
113
|
+
*
|
|
114
|
+
* NOTE: Except for the `tls` and `server_name` arguments, all arguments might
|
|
115
|
+
* be `NULL`, which a context builder (`fio_io_functions_s`) should treat as a
|
|
116
|
+
* request for a self-signed certificate. It may be silently ignored.
|
|
117
|
+
*/
|
|
118
|
+
SFUNC fio_io_tls_s *fio_io_tls_cert_add(fio_io_tls_s *,
|
|
119
|
+
const char *server_name,
|
|
120
|
+
const char *public_cert_file,
|
|
121
|
+
const char *private_key_file,
|
|
122
|
+
const char *pk_password);
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Adds an ALPN protocol callback to the SSL/TLS context.
|
|
126
|
+
*
|
|
127
|
+
* The first protocol added will act as the default protocol to be selected.
|
|
128
|
+
*
|
|
129
|
+
* A `NULL` protocol name will be silently ignored.
|
|
130
|
+
*
|
|
131
|
+
* A `NULL` callback (`on_selected`) will be silently replaced with a no-op.
|
|
132
|
+
*/
|
|
133
|
+
SFUNC fio_io_tls_s *fio_io_tls_alpn_add(fio_io_tls_s *tls,
|
|
134
|
+
const char *protocol_name,
|
|
135
|
+
void (*on_selected)(fio_s *));
|
|
136
|
+
|
|
137
|
+
/** Calls the `on_selected` callback for the `fio_io_tls_s` object. */
|
|
138
|
+
SFUNC int fio_io_tls_alpn_select(fio_io_tls_s *tls,
|
|
139
|
+
const char *protocol_name,
|
|
140
|
+
size_t name_length,
|
|
141
|
+
fio_s *);
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Adds a certificate to the "trust" list, which automatically adds a peer
|
|
145
|
+
* verification requirement.
|
|
146
|
+
*
|
|
147
|
+
* If `public_cert_file` is `NULL`, implementation is expected to add the
|
|
148
|
+
* system's default trust registry.
|
|
149
|
+
*
|
|
150
|
+
* Note: when the `fio_io_tls_s` object is used for server connections, this should
|
|
151
|
+
* limit connections to clients that connect using a trusted certificate.
|
|
152
|
+
*
|
|
153
|
+
* fio_io_tls_trust_add(tls, "google-ca.pem" );
|
|
154
|
+
*/
|
|
155
|
+
SFUNC fio_io_tls_s *fio_io_tls_trust_add(fio_io_tls_s *, const char *public_cert_file);
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Returns the number of `fio_io_tls_cert_add` instructions.
|
|
159
|
+
*
|
|
160
|
+
* This could be used when deciding if to add a NULL instruction (self-signed).
|
|
161
|
+
*
|
|
162
|
+
* If `fio_io_tls_cert_add` was never called, zero (0) is returned.
|
|
163
|
+
*/
|
|
164
|
+
SFUNC uintptr_t fio_io_tls_cert_count(fio_io_tls_s *tls);
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Returns the number of registered ALPN protocol names.
|
|
168
|
+
*
|
|
169
|
+
* This could be used when deciding if protocol selection should be delegated to
|
|
170
|
+
* the ALPN mechanism, or whether a protocol should be immediately assigned.
|
|
171
|
+
*
|
|
172
|
+
* If no ALPN protocols are registered, zero (0) is returned.
|
|
173
|
+
*/
|
|
174
|
+
SFUNC uintptr_t fio_io_tls_alpn_count(fio_io_tls_s *tls);
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Returns the number of `fio_io_tls_trust_add` instructions.
|
|
178
|
+
*
|
|
179
|
+
* This could be used when deciding if to disable peer verification or not.
|
|
180
|
+
*
|
|
181
|
+
* If `fio_io_tls_trust_add` was never called, zero (0) is returned.
|
|
182
|
+
*/
|
|
183
|
+
SFUNC uintptr_t fio_io_tls_trust_count(fio_io_tls_s *tls);
|
|
184
|
+
|
|
185
|
+
/** Arguments (and info) for `fio_io_tls_each`. */
|
|
186
|
+
typedef struct fio_io_tls_each_s {
|
|
187
|
+
fio_io_tls_s *tls;
|
|
188
|
+
void *udata;
|
|
189
|
+
void *udata2;
|
|
190
|
+
int (*each_cert)(struct fio_io_tls_each_s *,
|
|
191
|
+
const char *server_name,
|
|
192
|
+
const char *public_cert_file,
|
|
193
|
+
const char *private_key_file,
|
|
194
|
+
const char *pk_password);
|
|
195
|
+
int (*each_alpn)(struct fio_io_tls_each_s *,
|
|
196
|
+
const char *protocol_name,
|
|
197
|
+
void (*on_selected)(fio_s *));
|
|
198
|
+
int (*each_trust)(struct fio_io_tls_each_s *, const char *public_cert_file);
|
|
199
|
+
} fio_io_tls_each_s;
|
|
200
|
+
|
|
201
|
+
/** Calls callbacks for certificate, trust certificate and ALPN added. */
|
|
202
|
+
SFUNC int fio_io_tls_each(fio_io_tls_each_s);
|
|
203
|
+
|
|
204
|
+
/** `fio_io_tls_each` helper macro, see `fio_io_tls_each_s` for named arguments. */
|
|
205
|
+
#define fio_io_tls_each(tls_, ...) \
|
|
206
|
+
fio_io_tls_each(((fio_io_tls_each_s){.tls = tls_, __VA_ARGS__}))
|
|
207
|
+
|
|
208
|
+
/** If `NULL` returns current default, otherwise sets it. */
|
|
209
|
+
SFUNC fio_io_functions_s fio_io_tls_default_io_functions(fio_io_functions_s *);
|
|
210
|
+
#endif
|
|
211
|
+
|
|
212
|
+
/* *****************************************************************************
|
|
213
|
+
Wrapper API
|
|
214
|
+
***************************************************************************** */
|
|
7
215
|
|
|
8
|
-
|
|
9
|
-
|
|
216
|
+
// clang-format off
|
|
217
|
+
/**
|
|
218
|
+
Assigns the TLS context a public certificate, allowing remote parties to
|
|
219
|
+
validate the connection's identity.
|
|
10
220
|
|
|
11
|
-
|
|
221
|
+
A self signed certificate is automatically created if the `name` argument
|
|
222
|
+
is specified and either (or both) of the `cert` (public certificate) or `key`
|
|
223
|
+
(private key) arguments are missing.
|
|
12
224
|
|
|
225
|
+
Some implementations allow servers to have more than a single certificate, which
|
|
226
|
+
will be selected using the SNI extension. I believe the existing OpenSSL
|
|
227
|
+
implementation supports this option (untested).
|
|
228
|
+
|
|
229
|
+
Iodine::TLS#add_cert(name = nil,
|
|
230
|
+
cert = nil,
|
|
231
|
+
key = nil,
|
|
232
|
+
password = nil)
|
|
233
|
+
|
|
234
|
+
Certificates and keys should be String objects leading to a PEM file.
|
|
235
|
+
|
|
236
|
+
This method also accepts named arguments. i.e.:
|
|
237
|
+
|
|
238
|
+
tls = Iodine::TLS.new
|
|
239
|
+
tls.add_cert name: "example.com"
|
|
240
|
+
tls.add_cert cert: "my_cert.pem", key: "my_key.pem"
|
|
241
|
+
tls.add_cert cert: "my_cert.pem", key: "my_key.pem", password: ENV['TLS_PASS']
|
|
242
|
+
|
|
243
|
+
Since TLS setup is crucial for security, an initialization error will result in
|
|
244
|
+
Iodine crashing with an error message. This is expected behavior.
|
|
245
|
+
*/ // clang-format on
|
|
246
|
+
static VALUE iodine_tls_cert_add(int argc, VALUE *argv, VALUE self) {
|
|
247
|
+
fio_io_tls_s *tls = iodine_tls_get(self);
|
|
248
|
+
fio_buf_info_s server_name = FIO_BUF_INFO1((char *)"localhost");
|
|
249
|
+
fio_buf_info_s public_cert_file = FIO_BUF_INFO0;
|
|
250
|
+
fio_buf_info_s private_key_file = FIO_BUF_INFO0;
|
|
251
|
+
fio_buf_info_s pk_password = FIO_BUF_INFO0;
|
|
252
|
+
iodine_rb2c_arg(argc,
|
|
253
|
+
argv,
|
|
254
|
+
IODINE_ARG_BUF(server_name, 0, "name", 0),
|
|
255
|
+
IODINE_ARG_BUF(public_cert_file, 0, "cert", 0),
|
|
256
|
+
IODINE_ARG_BUF(private_key_file, 0, "key", 0),
|
|
257
|
+
IODINE_ARG_BUF(pk_password, 0, "password", 0));
|
|
258
|
+
fio_io_tls_cert_add(tls,
|
|
259
|
+
server_name.buf,
|
|
260
|
+
public_cert_file.buf,
|
|
261
|
+
private_key_file.buf,
|
|
262
|
+
pk_password.buf);
|
|
263
|
+
return self;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/** @deprecated use {Iodine::TLS.add_cert}. */
|
|
267
|
+
static VALUE iodine_tls_cert_add_old_name(int argc, VALUE *argv, VALUE self) {
|
|
268
|
+
return iodine_tls_cert_add(argc, argv, self);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/* *****************************************************************************
|
|
272
|
+
TLS Trust Store (Peer Certificate Authentication)
|
|
273
|
+
***************************************************************************** */
|
|
274
|
+
|
|
275
|
+
// clang-format off
|
|
276
|
+
/**
|
|
277
|
+
Adds a public certificate (or certificate bundle) to the "trust" list,
|
|
278
|
+
enabling peer (client) certificate authentication / authorization.
|
|
279
|
+
|
|
280
|
+
tls = Iodine::TLS.new
|
|
281
|
+
tls.trust("clients-ca.pem")
|
|
282
|
+
|
|
283
|
+
Calling `trust` without a path (or with `nil`) trusts the system's default
|
|
284
|
+
trust registry.
|
|
285
|
+
|
|
286
|
+
Once the trust list is non-empty, servers using this TLS context request a
|
|
287
|
+
certificate from connecting clients and verify its chain against the trusted
|
|
288
|
+
certificates. The peer's certificate chain can then be reviewed by the
|
|
289
|
+
application using {Iodine::Connection#certificate} and
|
|
290
|
+
{Iodine::Connection#each_certificate}.
|
|
291
|
+
|
|
292
|
+
@param path [String, nil] path to a PEM file with one or more trusted
|
|
293
|
+
certificates (`nil` for the system's trust registry).
|
|
294
|
+
@return [Iodine::TLS] returns itself, so calls may be chained.
|
|
295
|
+
*/ // clang-format on
|
|
296
|
+
static VALUE iodine_tls_trust(int argc, VALUE *argv, VALUE self) {
|
|
297
|
+
fio_io_tls_s *tls = iodine_tls_get(self);
|
|
298
|
+
fio_buf_info_s path = FIO_BUF_INFO0;
|
|
299
|
+
iodine_rb2c_arg(argc, argv, IODINE_ARG_BUF(path, 0, "path", 0));
|
|
300
|
+
/* NULL path marks the system's default trust registry as trusted */
|
|
301
|
+
fio_io_tls_trust_add(tls, path.buf);
|
|
302
|
+
return self;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/* *****************************************************************************
|
|
306
|
+
TLS Default Backend Getter/Setter
|
|
307
|
+
***************************************************************************** */
|
|
308
|
+
|
|
309
|
+
/** Getter for Iodine::TLS.default - returns the current default TLS backend */
|
|
310
|
+
static VALUE iodine_tls_default_get(VALUE klass) {
|
|
311
|
+
/* Query the current C-level default */
|
|
312
|
+
fio_io_functions_s current = fio_io_tls_default_functions(NULL);
|
|
313
|
+
|
|
314
|
+
/* Get the known backend function pointers for comparison */
|
|
315
|
+
fio_io_functions_s tls13 = fio_tls13_io_functions();
|
|
316
|
+
|
|
317
|
+
/* Compare build_context function pointer to determine active backend */
|
|
318
|
+
if (current.build_context == tls13.build_context) {
|
|
319
|
+
return ID2SYM(rb_intern("iodine"));
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
#ifdef HAVE_OPENSSL
|
|
323
|
+
fio_io_functions_s openssl = fio_openssl_io_functions();
|
|
324
|
+
if (current.build_context == openssl.build_context) {
|
|
325
|
+
return ID2SYM(rb_intern("openssl"));
|
|
326
|
+
}
|
|
13
327
|
#endif
|
|
328
|
+
|
|
329
|
+
/* Fallback - should not reach here */
|
|
330
|
+
rb_raise(rb_eStandardError, "known values not found (:iodine or :openssl)");
|
|
331
|
+
return ID2SYM(rb_intern("err"));
|
|
332
|
+
(void)klass;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/** Setter for Iodine::TLS.default= - validates and sets the default backend */
|
|
336
|
+
static VALUE iodine_tls_default_set(VALUE klass, VALUE backend) {
|
|
337
|
+
ID backend_id;
|
|
338
|
+
fio_io_functions_s current = fio_io_tls_default_functions(NULL);
|
|
339
|
+
|
|
340
|
+
if (!RB_TYPE_P(backend, RUBY_T_SYMBOL))
|
|
341
|
+
rb_raise(rb_eTypeError, "default must be a Symbol (:iodine or :openssl)");
|
|
342
|
+
|
|
343
|
+
backend_id = rb_sym2id(backend);
|
|
344
|
+
if (backend_id == rb_intern("iodine")) {
|
|
345
|
+
/* Set embedded TLS 1.3 as default */
|
|
346
|
+
current = fio_tls13_io_functions();
|
|
347
|
+
} else if (backend_id == rb_intern("openssl")) {
|
|
348
|
+
#ifndef HAVE_OPENSSL
|
|
349
|
+
rb_raise(rb_eRuntimeError, "OpenSSL not available (not compiled in)");
|
|
350
|
+
#else
|
|
351
|
+
/* Set OpenSSL as default */
|
|
352
|
+
current = fio_openssl_io_functions();
|
|
353
|
+
#endif
|
|
354
|
+
} else {
|
|
355
|
+
rb_raise(rb_eArgError, "default must be :iodine or :openssl");
|
|
356
|
+
}
|
|
357
|
+
fio_io_tls_default_functions(¤t);
|
|
358
|
+
return backend;
|
|
359
|
+
(void)klass;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/* *****************************************************************************
|
|
363
|
+
Iodine::TLS::Certificate - Peer Certificate Snapshot
|
|
364
|
+
***************************************************************************** */
|
|
365
|
+
|
|
366
|
+
/** Maps a public key algorithm to a Ruby Symbol. */
|
|
367
|
+
static VALUE iodine_tls_certificate_key_algo_sym(fio_x509_key_algo_e algo) {
|
|
368
|
+
switch (algo) {
|
|
369
|
+
case FIO_X509_KEY_RSA: return ID2SYM(rb_intern("rsa"));
|
|
370
|
+
case FIO_X509_KEY_ECDSA_P256: return ID2SYM(rb_intern("ecdsa_p256"));
|
|
371
|
+
case FIO_X509_KEY_ECDSA_P384: return ID2SYM(rb_intern("ecdsa_p384"));
|
|
372
|
+
case FIO_X509_KEY_ED25519: return ID2SYM(rb_intern("ed25519"));
|
|
373
|
+
default: return ID2SYM(rb_intern("unknown"));
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/** Maps a signature algorithm to a Ruby Symbol. */
|
|
378
|
+
static VALUE
|
|
379
|
+
iodine_tls_certificate_signature_algo_sym(fio_x509_signature_algo_e algo) {
|
|
380
|
+
switch (algo) {
|
|
381
|
+
case FIO_X509_SIGNATURE_RSA_PKCS1_SHA256:
|
|
382
|
+
return ID2SYM(rb_intern("rsa_pkcs1_sha256"));
|
|
383
|
+
case FIO_X509_SIGNATURE_RSA_PKCS1_SHA384:
|
|
384
|
+
return ID2SYM(rb_intern("rsa_pkcs1_sha384"));
|
|
385
|
+
case FIO_X509_SIGNATURE_RSA_PKCS1_SHA512:
|
|
386
|
+
return ID2SYM(rb_intern("rsa_pkcs1_sha512"));
|
|
387
|
+
case FIO_X509_SIGNATURE_RSA_PSS_SHA256:
|
|
388
|
+
return ID2SYM(rb_intern("rsa_pss_sha256"));
|
|
389
|
+
case FIO_X509_SIGNATURE_RSA_PSS_SHA384:
|
|
390
|
+
return ID2SYM(rb_intern("rsa_pss_sha384"));
|
|
391
|
+
case FIO_X509_SIGNATURE_RSA_PSS_SHA512:
|
|
392
|
+
return ID2SYM(rb_intern("rsa_pss_sha512"));
|
|
393
|
+
case FIO_X509_SIGNATURE_ECDSA_SHA256:
|
|
394
|
+
return ID2SYM(rb_intern("ecdsa_sha256"));
|
|
395
|
+
case FIO_X509_SIGNATURE_ECDSA_SHA384:
|
|
396
|
+
return ID2SYM(rb_intern("ecdsa_sha384"));
|
|
397
|
+
case FIO_X509_SIGNATURE_ED25519: return ID2SYM(rb_intern("ed25519"));
|
|
398
|
+
default: return ID2SYM(rb_intern("unknown"));
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/** Converts a (possibly empty) buffer view to a Ruby String (or `nil`). */
|
|
403
|
+
#define IODINE___CERT_BUF2STR(bufinfo) \
|
|
404
|
+
((bufinfo).buf ? rb_str_new((const char *)(bufinfo).buf, \
|
|
405
|
+
(long)(bufinfo).len) \
|
|
406
|
+
: Qnil)
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* Builds an Iodine::TLS::Certificate snapshot from a parsed X.509
|
|
410
|
+
* certificate.
|
|
411
|
+
*
|
|
412
|
+
* All fields are copied eagerly, since the `fio_x509_cert_s` views are
|
|
413
|
+
* transient (valid only until the next `fio_io_peer_info_next` call on ANY
|
|
414
|
+
* connection or until the connection is closed).
|
|
415
|
+
*/
|
|
416
|
+
static VALUE iodine_tls_certificate_new(const fio_x509_cert_s *cert) {
|
|
417
|
+
VALUE o = rb_obj_alloc(iodine_rb_TLS_CERTIFICATE);
|
|
418
|
+
rb_ivar_set(o, rb_intern("@der"), IODINE___CERT_BUF2STR(cert->der));
|
|
419
|
+
rb_ivar_set(o, rb_intern("@serial"), IODINE___CERT_BUF2STR(cert->serial));
|
|
420
|
+
rb_ivar_set(o, rb_intern("@subject"), IODINE___CERT_BUF2STR(cert->subject));
|
|
421
|
+
rb_ivar_set(o, rb_intern("@issuer"), IODINE___CERT_BUF2STR(cert->issuer));
|
|
422
|
+
rb_ivar_set(o, rb_intern("@cn"), IODINE___CERT_BUF2STR(cert->cn));
|
|
423
|
+
rb_ivar_set(o,
|
|
424
|
+
rb_intern("@signature"),
|
|
425
|
+
IODINE___CERT_BUF2STR(cert->signature));
|
|
426
|
+
rb_ivar_set(o, rb_intern("@san_dns"), IODINE___CERT_BUF2STR(cert->san_dns));
|
|
427
|
+
rb_ivar_set(o, rb_intern("@san_ip"), IODINE___CERT_BUF2STR(cert->san_ip));
|
|
428
|
+
rb_ivar_set(o,
|
|
429
|
+
rb_intern("@not_before"),
|
|
430
|
+
rb_time_new((time_t)cert->not_before, 0));
|
|
431
|
+
rb_ivar_set(o,
|
|
432
|
+
rb_intern("@not_after"),
|
|
433
|
+
rb_time_new((time_t)cert->not_after, 0));
|
|
434
|
+
rb_ivar_set(o,
|
|
435
|
+
rb_intern("@fingerprint"),
|
|
436
|
+
rb_str_new((const char *)cert->fingerprint, 32));
|
|
437
|
+
rb_ivar_set(o,
|
|
438
|
+
rb_intern("@key_algo"),
|
|
439
|
+
iodine_tls_certificate_key_algo_sym(cert->key_algo));
|
|
440
|
+
rb_ivar_set(o,
|
|
441
|
+
rb_intern("@signature_algo"),
|
|
442
|
+
iodine_tls_certificate_signature_algo_sym(cert->signature_algo));
|
|
443
|
+
rb_ivar_set(o,
|
|
444
|
+
rb_intern("@key_usage"),
|
|
445
|
+
cert->has_key_usage ? UINT2NUM(cert->key_usage) : Qnil);
|
|
446
|
+
rb_ivar_set(o, rb_intern("@version"), INT2FIX(cert->version));
|
|
447
|
+
rb_ivar_set(o, rb_intern("@chain_index"), INT2FIX(cert->chain_index));
|
|
448
|
+
rb_ivar_set(o, rb_intern("@verified"), cert->verified ? Qtrue : Qfalse);
|
|
449
|
+
rb_ivar_set(o, rb_intern("@ca"), cert->is_ca ? Qtrue : Qfalse);
|
|
450
|
+
return rb_obj_freeze(o);
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
#undef IODINE___CERT_BUF2STR
|
|
454
|
+
|
|
455
|
+
/** Returns `true` if the TLS backend verified this certificate's chain. */
|
|
456
|
+
static VALUE iodine_tls_certificate_verified_p(VALUE self) {
|
|
457
|
+
return rb_ivar_get(self, rb_intern("@verified"));
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
/** Returns `true` if the certificate is a CA (Basic Constraints). */
|
|
461
|
+
static VALUE iodine_tls_certificate_ca_p(VALUE self) {
|
|
462
|
+
return rb_ivar_get(self, rb_intern("@ca"));
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/* *****************************************************************************
|
|
466
|
+
Initialize Iodine::TLS
|
|
467
|
+
***************************************************************************** */
|
|
468
|
+
|
|
469
|
+
static void Init_Iodine_TLS(void) { /** Initialize Iodine::TLS */
|
|
470
|
+
/** Used to setup a TLS contexts for connections (incoming / outgoing). */
|
|
471
|
+
VALUE m = iodine_rb_IODINE_TLS =
|
|
472
|
+
rb_define_class_under(iodine_rb_IODINE, "TLS", rb_cObject);
|
|
473
|
+
rb_define_alloc_func(m, iodine_tls_alloc);
|
|
474
|
+
rb_define_method(m, "add_cert", iodine_tls_cert_add, -1);
|
|
475
|
+
rb_define_method(m, "use_certificate", iodine_tls_cert_add_old_name, -1);
|
|
476
|
+
rb_define_method(m, "trust", iodine_tls_trust, -1);
|
|
477
|
+
|
|
478
|
+
/* TLS default backend getter/setter methods */
|
|
479
|
+
rb_define_singleton_method(m, "default", iodine_tls_default_get, 0);
|
|
480
|
+
rb_define_singleton_method(m, "default=", iodine_tls_default_set, 1);
|
|
481
|
+
|
|
482
|
+
/* TLS backend availability constants */
|
|
483
|
+
rb_const_set(m,
|
|
484
|
+
rb_intern("SUPPORTED"),
|
|
485
|
+
Qtrue); /* Always true - embedded TLS always available */
|
|
486
|
+
#ifdef HAVE_OPENSSL
|
|
487
|
+
rb_const_set(m, rb_intern("OPENSSL_AVAILABLE"), Qtrue);
|
|
488
|
+
#else
|
|
489
|
+
rb_const_set(m, rb_intern("OPENSSL_AVAILABLE"), Qfalse);
|
|
490
|
+
#endif
|
|
491
|
+
/* Embedded TLS 1.3 is always available via fio-stl.h */
|
|
492
|
+
rb_const_set(m, rb_intern("EMBEDDED_AVAILABLE"), Qtrue);
|
|
493
|
+
|
|
494
|
+
/* Iodine::TLS::Certificate - peer certificate snapshot (value object) */
|
|
495
|
+
{
|
|
496
|
+
/** An immutable snapshot of an X.509 certificate. */
|
|
497
|
+
VALUE c = iodine_rb_TLS_CERTIFICATE =
|
|
498
|
+
rb_define_class_under(iodine_rb_IODINE_TLS, "Certificate", rb_cObject);
|
|
499
|
+
/* certificates originate from TLS connections, not Ruby code */
|
|
500
|
+
rb_undef_method(rb_singleton_class(c), "new");
|
|
501
|
+
rb_define_attr(c, "der", 1, 0);
|
|
502
|
+
rb_define_attr(c, "serial", 1, 0);
|
|
503
|
+
rb_define_attr(c, "subject", 1, 0);
|
|
504
|
+
rb_define_attr(c, "issuer", 1, 0);
|
|
505
|
+
rb_define_attr(c, "cn", 1, 0);
|
|
506
|
+
rb_define_attr(c, "signature", 1, 0);
|
|
507
|
+
rb_define_attr(c, "san_dns", 1, 0);
|
|
508
|
+
rb_define_attr(c, "san_ip", 1, 0);
|
|
509
|
+
rb_define_attr(c, "not_before", 1, 0);
|
|
510
|
+
rb_define_attr(c, "not_after", 1, 0);
|
|
511
|
+
rb_define_attr(c, "fingerprint", 1, 0);
|
|
512
|
+
rb_define_attr(c, "key_algo", 1, 0);
|
|
513
|
+
rb_define_attr(c, "signature_algo", 1, 0);
|
|
514
|
+
rb_define_attr(c, "key_usage", 1, 0);
|
|
515
|
+
rb_define_attr(c, "version", 1, 0);
|
|
516
|
+
rb_define_attr(c, "chain_index", 1, 0);
|
|
517
|
+
rb_define_method(c, "verified?", iodine_tls_certificate_verified_p, 0);
|
|
518
|
+
rb_define_method(c, "ca?", iodine_tls_certificate_ca_p, 0);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
#endif /* H___IODINE_TLS___H */
|