uringmachine 0.4 → 0.5.1

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.
Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +2 -1
  3. data/CHANGELOG.md +16 -0
  4. data/README.md +44 -1
  5. data/TODO.md +12 -3
  6. data/examples/bm_snooze.rb +89 -0
  7. data/examples/bm_sqlite.rb +89 -0
  8. data/examples/bm_write.rb +56 -0
  9. data/examples/dns_client.rb +12 -0
  10. data/examples/http_server.rb +42 -43
  11. data/examples/pg.rb +85 -0
  12. data/examples/server_client.rb +64 -0
  13. data/examples/snooze.rb +44 -0
  14. data/examples/stream.rb +85 -0
  15. data/examples/write_dev_null.rb +16 -0
  16. data/ext/um/extconf.rb +81 -14
  17. data/ext/um/um.c +468 -414
  18. data/ext/um/um.h +149 -40
  19. data/ext/um/um_async_op.c +40 -0
  20. data/ext/um/um_async_op_class.c +136 -0
  21. data/ext/um/um_buffer.c +49 -0
  22. data/ext/um/um_class.c +176 -44
  23. data/ext/um/um_const.c +174 -9
  24. data/ext/um/um_ext.c +8 -0
  25. data/ext/um/um_mutex_class.c +47 -0
  26. data/ext/um/um_op.c +89 -111
  27. data/ext/um/um_queue_class.c +58 -0
  28. data/ext/um/um_ssl.c +850 -0
  29. data/ext/um/um_ssl.h +22 -0
  30. data/ext/um/um_ssl_class.c +138 -0
  31. data/ext/um/um_sync.c +273 -0
  32. data/ext/um/um_utils.c +1 -1
  33. data/lib/uringmachine/dns_resolver.rb +84 -0
  34. data/lib/uringmachine/ssl/context_builder.rb +96 -0
  35. data/lib/uringmachine/ssl.rb +394 -0
  36. data/lib/uringmachine/version.rb +1 -1
  37. data/lib/uringmachine.rb +27 -3
  38. data/supressions/ruby.supp +71 -0
  39. data/test/helper.rb +6 -0
  40. data/test/test_async_op.rb +119 -0
  41. data/test/test_ssl.rb +155 -0
  42. data/test/test_um.rb +464 -47
  43. data/uringmachine.gemspec +3 -2
  44. data/vendor/liburing/.gitignore +5 -0
  45. data/vendor/liburing/CHANGELOG +1 -0
  46. data/vendor/liburing/configure +32 -0
  47. data/vendor/liburing/examples/Makefile +1 -0
  48. data/vendor/liburing/examples/reg-wait.c +159 -0
  49. data/vendor/liburing/liburing.spec +1 -1
  50. data/vendor/liburing/src/include/liburing/io_uring.h +48 -2
  51. data/vendor/liburing/src/include/liburing.h +28 -2
  52. data/vendor/liburing/src/int_flags.h +10 -3
  53. data/vendor/liburing/src/liburing-ffi.map +13 -2
  54. data/vendor/liburing/src/liburing.map +9 -0
  55. data/vendor/liburing/src/queue.c +25 -16
  56. data/vendor/liburing/src/register.c +73 -4
  57. data/vendor/liburing/src/setup.c +46 -18
  58. data/vendor/liburing/src/setup.h +6 -0
  59. data/vendor/liburing/test/Makefile +7 -0
  60. data/vendor/liburing/test/cmd-discard.c +427 -0
  61. data/vendor/liburing/test/fifo-nonblock-read.c +69 -0
  62. data/vendor/liburing/test/file-exit-unreg.c +48 -0
  63. data/vendor/liburing/test/io_uring_passthrough.c +2 -0
  64. data/vendor/liburing/test/io_uring_register.c +13 -2
  65. data/vendor/liburing/test/napi-test.c +1 -1
  66. data/vendor/liburing/test/no-mmap-inval.c +1 -1
  67. data/vendor/liburing/test/read-mshot-empty.c +2 -0
  68. data/vendor/liburing/test/read-mshot-stdin.c +121 -0
  69. data/vendor/liburing/test/read-mshot.c +6 -0
  70. data/vendor/liburing/test/recvsend_bundle.c +2 -2
  71. data/vendor/liburing/test/reg-fd-only.c +1 -1
  72. data/vendor/liburing/test/reg-wait.c +251 -0
  73. data/vendor/liburing/test/regbuf-clone.c +458 -0
  74. data/vendor/liburing/test/resize-rings.c +643 -0
  75. data/vendor/liburing/test/rsrc_tags.c +1 -1
  76. data/vendor/liburing/test/sqpoll-sleep.c +39 -8
  77. data/vendor/liburing/test/sqwait.c +136 -0
  78. data/vendor/liburing/test/sync-cancel.c +8 -1
  79. data/vendor/liburing/test/timeout.c +13 -8
  80. metadata +52 -8
  81. data/examples/http_server_multishot.rb +0 -57
  82. data/examples/http_server_simpler.rb +0 -34
data/ext/um/um_ssl.c ADDED
@@ -0,0 +1,850 @@
1
+ /*
2
+ Adopted from:
3
+ https://github.com/puma/puma/blob/master/ext/puma_http11/mini_ssl.c
4
+
5
+ License (BSD-3):
6
+ https://github.com/puma/puma/blob/master/LICENSE
7
+ */
8
+
9
+ #define RSTRING_NOT_MODIFIED 1
10
+
11
+ #include <ruby.h>
12
+ #include <ruby/version.h>
13
+ #include <ruby/io.h>
14
+
15
+ #ifdef HAVE_OPENSSL_BIO_H
16
+
17
+ #include <openssl/bio.h>
18
+ #include <openssl/ssl.h>
19
+ #include <openssl/dh.h>
20
+ #include <openssl/err.h>
21
+ #include <openssl/x509.h>
22
+
23
+ #ifndef SSL_OP_NO_COMPRESSION
24
+ #define SSL_OP_NO_COMPRESSION 0
25
+ #endif
26
+
27
+ typedef struct {
28
+ BIO* read;
29
+ BIO* write;
30
+ SSL* ssl;
31
+ SSL_CTX* ctx;
32
+ } ms_conn;
33
+
34
+ typedef struct {
35
+ unsigned char* buf;
36
+ int bytes;
37
+ } ms_cert_buf;
38
+
39
+ VALUE eError;
40
+
41
+ NORETURN(void raise_file_error(const char* caller, const char *filename));
42
+
43
+ void raise_file_error(const char* caller, const char *filename) {
44
+ rb_raise(eError, "%s: error in file '%s': %s", caller, filename, ERR_error_string(ERR_get_error(), NULL));
45
+ }
46
+
47
+ NORETURN(void raise_param_error(const char* caller, const char *param));
48
+
49
+ void raise_param_error(const char* caller, const char *param) {
50
+ rb_raise(eError, "%s: error with parameter '%s': %s", caller, param, ERR_error_string(ERR_get_error(), NULL));
51
+ }
52
+
53
+ void engine_free(void *ptr) {
54
+ ms_conn *conn = ptr;
55
+ ms_cert_buf* cert_buf = (ms_cert_buf*)SSL_get_app_data(conn->ssl);
56
+ if(cert_buf) {
57
+ OPENSSL_free(cert_buf->buf);
58
+ free(cert_buf);
59
+ }
60
+ SSL_free(conn->ssl);
61
+ SSL_CTX_free(conn->ctx);
62
+
63
+ free(conn);
64
+ }
65
+
66
+ const rb_data_type_t engine_data_type = {
67
+ "MiniSSL/ENGINE",
68
+ { 0, engine_free, 0 },
69
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
70
+ };
71
+
72
+ #ifndef HAVE_SSL_CTX_SET_DH_AUTO
73
+ DH *get_dh2048(void) {
74
+ /* `openssl dhparam -C 2048`
75
+ * -----BEGIN DH PARAMETERS-----
76
+ * MIIBCAKCAQEAjmh1uQHdTfxOyxEbKAV30fUfzqMDF/ChPzjfyzl2jcrqQMhrk76o
77
+ * 2NPNXqxHwsddMZ1RzvU8/jl+uhRuPWjXCFZbhET4N1vrviZM3VJhV8PPHuiVOACO
78
+ * y32jFd+Szx4bo2cXSK83hJ6jRd+0asP1awWjz9/06dFkrILCXMIfQLo0D8rqmppn
79
+ * EfDDAwuudCpM9kcDmBRAm9JsKbQ6gzZWjkc5+QWSaQofojIHbjvj3xzguaCJn+oQ
80
+ * vHWM+hsAnaOgEwCyeZ3xqs+/5lwSbkE/tqJW98cEZGygBUVo9jxZRZx6KOfjpdrb
81
+ * yenO9LJr/qtyrZB31WJbqxI0m0AKTAO8UwIBAg==
82
+ * -----END DH PARAMETERS-----
83
+ */
84
+ static unsigned char dh2048_p[] = {
85
+ 0x8E, 0x68, 0x75, 0xB9, 0x01, 0xDD, 0x4D, 0xFC, 0x4E, 0xCB,
86
+ 0x11, 0x1B, 0x28, 0x05, 0x77, 0xD1, 0xF5, 0x1F, 0xCE, 0xA3,
87
+ 0x03, 0x17, 0xF0, 0xA1, 0x3F, 0x38, 0xDF, 0xCB, 0x39, 0x76,
88
+ 0x8D, 0xCA, 0xEA, 0x40, 0xC8, 0x6B, 0x93, 0xBE, 0xA8, 0xD8,
89
+ 0xD3, 0xCD, 0x5E, 0xAC, 0x47, 0xC2, 0xC7, 0x5D, 0x31, 0x9D,
90
+ 0x51, 0xCE, 0xF5, 0x3C, 0xFE, 0x39, 0x7E, 0xBA, 0x14, 0x6E,
91
+ 0x3D, 0x68, 0xD7, 0x08, 0x56, 0x5B, 0x84, 0x44, 0xF8, 0x37,
92
+ 0x5B, 0xEB, 0xBE, 0x26, 0x4C, 0xDD, 0x52, 0x61, 0x57, 0xC3,
93
+ 0xCF, 0x1E, 0xE8, 0x95, 0x38, 0x00, 0x8E, 0xCB, 0x7D, 0xA3,
94
+ 0x15, 0xDF, 0x92, 0xCF, 0x1E, 0x1B, 0xA3, 0x67, 0x17, 0x48,
95
+ 0xAF, 0x37, 0x84, 0x9E, 0xA3, 0x45, 0xDF, 0xB4, 0x6A, 0xC3,
96
+ 0xF5, 0x6B, 0x05, 0xA3, 0xCF, 0xDF, 0xF4, 0xE9, 0xD1, 0x64,
97
+ 0xAC, 0x82, 0xC2, 0x5C, 0xC2, 0x1F, 0x40, 0xBA, 0x34, 0x0F,
98
+ 0xCA, 0xEA, 0x9A, 0x9A, 0x67, 0x11, 0xF0, 0xC3, 0x03, 0x0B,
99
+ 0xAE, 0x74, 0x2A, 0x4C, 0xF6, 0x47, 0x03, 0x98, 0x14, 0x40,
100
+ 0x9B, 0xD2, 0x6C, 0x29, 0xB4, 0x3A, 0x83, 0x36, 0x56, 0x8E,
101
+ 0x47, 0x39, 0xF9, 0x05, 0x92, 0x69, 0x0A, 0x1F, 0xA2, 0x32,
102
+ 0x07, 0x6E, 0x3B, 0xE3, 0xDF, 0x1C, 0xE0, 0xB9, 0xA0, 0x89,
103
+ 0x9F, 0xEA, 0x10, 0xBC, 0x75, 0x8C, 0xFA, 0x1B, 0x00, 0x9D,
104
+ 0xA3, 0xA0, 0x13, 0x00, 0xB2, 0x79, 0x9D, 0xF1, 0xAA, 0xCF,
105
+ 0xBF, 0xE6, 0x5C, 0x12, 0x6E, 0x41, 0x3F, 0xB6, 0xA2, 0x56,
106
+ 0xF7, 0xC7, 0x04, 0x64, 0x6C, 0xA0, 0x05, 0x45, 0x68, 0xF6,
107
+ 0x3C, 0x59, 0x45, 0x9C, 0x7A, 0x28, 0xE7, 0xE3, 0xA5, 0xDA,
108
+ 0xDB, 0xC9, 0xE9, 0xCE, 0xF4, 0xB2, 0x6B, 0xFE, 0xAB, 0x72,
109
+ 0xAD, 0x90, 0x77, 0xD5, 0x62, 0x5B, 0xAB, 0x12, 0x34, 0x9B,
110
+ 0x40, 0x0A, 0x4C, 0x03, 0xBC, 0x53
111
+ };
112
+ static unsigned char dh2048_g[] = { 0x02 };
113
+
114
+ DH *dh;
115
+ #if !(OPENSSL_VERSION_NUMBER < 0x10100005L)
116
+ BIGNUM *p, *g;
117
+ #endif
118
+
119
+ dh = DH_new();
120
+
121
+ #if OPENSSL_VERSION_NUMBER < 0x10100005L
122
+ dh->p = BN_bin2bn(dh2048_p, sizeof(dh2048_p), NULL);
123
+ dh->g = BN_bin2bn(dh2048_g, sizeof(dh2048_g), NULL);
124
+
125
+ if ((dh->p == NULL) || (dh->g == NULL)) {
126
+ DH_free(dh);
127
+ return NULL;
128
+ }
129
+ #else
130
+ p = BN_bin2bn(dh2048_p, sizeof(dh2048_p), NULL);
131
+ g = BN_bin2bn(dh2048_g, sizeof(dh2048_g), NULL);
132
+
133
+ if (p == NULL || g == NULL || !DH_set0_pqg(dh, p, NULL, g)) {
134
+ DH_free(dh);
135
+ BN_free(p);
136
+ BN_free(g);
137
+ return NULL;
138
+ }
139
+ #endif
140
+
141
+ return dh;
142
+ }
143
+ #endif
144
+
145
+ static void
146
+ sslctx_free(void *ptr) {
147
+ SSL_CTX *ctx = ptr;
148
+ SSL_CTX_free(ctx);
149
+ }
150
+
151
+ static const rb_data_type_t sslctx_type = {
152
+ "MiniSSL/SSLContext",
153
+ {
154
+ 0, sslctx_free,
155
+ },
156
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
157
+ };
158
+
159
+ ms_conn* engine_alloc(VALUE klass, VALUE* obj) {
160
+ ms_conn* conn;
161
+
162
+ *obj = TypedData_Make_Struct(klass, ms_conn, &engine_data_type, conn);
163
+
164
+ conn->read = BIO_new(BIO_s_mem());
165
+ BIO_set_nbio(conn->read, 1);
166
+
167
+ conn->write = BIO_new(BIO_s_mem());
168
+ BIO_set_nbio(conn->write, 1);
169
+
170
+ conn->ssl = 0;
171
+ conn->ctx = 0;
172
+
173
+ return conn;
174
+ }
175
+
176
+ static int engine_verify_callback(int preverify_ok, X509_STORE_CTX* ctx) {
177
+ X509* err_cert;
178
+ SSL* ssl;
179
+ int bytes;
180
+ unsigned char* buf = NULL;
181
+
182
+ if(!preverify_ok) {
183
+ err_cert = X509_STORE_CTX_get_current_cert(ctx);
184
+ if(err_cert) {
185
+ /*
186
+ * Save the failed certificate for inspection/logging.
187
+ */
188
+ bytes = i2d_X509(err_cert, &buf);
189
+ if(bytes > 0) {
190
+ ms_cert_buf* cert_buf = (ms_cert_buf*)malloc(sizeof(ms_cert_buf));
191
+ cert_buf->buf = buf;
192
+ cert_buf->bytes = bytes;
193
+ ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
194
+ SSL_set_app_data(ssl, cert_buf);
195
+ }
196
+ }
197
+ }
198
+
199
+ return preverify_ok;
200
+ }
201
+
202
+ static int password_callback(char *buf, int size, int rwflag, void *userdata) {
203
+ const char *password = (const char *) userdata;
204
+ size_t len = strlen(password);
205
+
206
+ if (len > (size_t) size) {
207
+ return 0;
208
+ }
209
+
210
+ memcpy(buf, password, len);
211
+ return (int) len;
212
+ }
213
+
214
+ static VALUE
215
+ sslctx_alloc(VALUE klass) {
216
+ SSL_CTX *ctx;
217
+ long mode = 0 |
218
+ SSL_MODE_ENABLE_PARTIAL_WRITE |
219
+ SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER |
220
+ SSL_MODE_RELEASE_BUFFERS;
221
+
222
+ #ifdef HAVE_TLS_SERVER_METHOD
223
+ ctx = SSL_CTX_new(TLS_method());
224
+ // printf("\nctx using TLS_method security_level %d\n", SSL_CTX_get_security_level(ctx));
225
+ #else
226
+ ctx = SSL_CTX_new(SSLv23_method());
227
+ #endif
228
+ if (!ctx) {
229
+ rb_raise(eError, "SSL_CTX_new");
230
+ }
231
+ SSL_CTX_set_mode(ctx, mode);
232
+
233
+ return TypedData_Wrap_Struct(klass, &sslctx_type, ctx);
234
+ }
235
+
236
+ VALUE
237
+ sslctx_initialize(VALUE self, VALUE micro_ssl_ctx) {
238
+ SSL_CTX* ctx;
239
+ int ssl_options;
240
+ VALUE key, cert, ca, verify_mode, ssl_cipher_filter, ssl_ciphersuites, no_tlsv1, no_tlsv1_1,
241
+ verification_flags, session_id_bytes, cert_pem, key_pem, key_password_command, key_password;
242
+ BIO *bio;
243
+ X509 *x509 = NULL;
244
+ EVP_PKEY *pkey;
245
+ pem_password_cb *password_cb = NULL;
246
+ const char *password = NULL;
247
+ #ifdef HAVE_SSL_CTX_SET_MIN_PROTO_VERSION
248
+ int min;
249
+ #endif
250
+ #ifndef HAVE_SSL_CTX_SET_DH_AUTO
251
+ DH *dh;
252
+ #endif
253
+ #if OPENSSL_VERSION_NUMBER < 0x10002000L
254
+ EC_KEY *ecdh;
255
+ #endif
256
+ #ifdef HAVE_SSL_CTX_SET_SESSION_CACHE_MODE
257
+ VALUE reuse, reuse_cache_size, reuse_timeout;
258
+
259
+ reuse = rb_funcall(micro_ssl_ctx, rb_intern_const("reuse"), 0);
260
+ reuse_cache_size = rb_funcall(micro_ssl_ctx, rb_intern_const("reuse_cache_size"), 0);
261
+ reuse_timeout = rb_funcall(micro_ssl_ctx, rb_intern_const("reuse_timeout"), 0);
262
+ #endif
263
+
264
+ key = rb_funcall(micro_ssl_ctx, rb_intern_const("key"), 0);
265
+
266
+ key_password_command = rb_funcall(micro_ssl_ctx, rb_intern_const("key_password_command"), 0);
267
+
268
+ cert = rb_funcall(micro_ssl_ctx, rb_intern_const("cert"), 0);
269
+
270
+ ca = rb_funcall(micro_ssl_ctx, rb_intern_const("ca"), 0);
271
+
272
+ cert_pem = rb_funcall(micro_ssl_ctx, rb_intern_const("cert_pem"), 0);
273
+
274
+ key_pem = rb_funcall(micro_ssl_ctx, rb_intern_const("key_pem"), 0);
275
+
276
+ verify_mode = rb_funcall(micro_ssl_ctx, rb_intern_const("verify_mode"), 0);
277
+
278
+ ssl_cipher_filter = rb_funcall(micro_ssl_ctx, rb_intern_const("ssl_cipher_filter"), 0);
279
+
280
+ ssl_ciphersuites = rb_funcall(micro_ssl_ctx, rb_intern_const("ssl_ciphersuites"), 0);
281
+
282
+ no_tlsv1 = rb_funcall(micro_ssl_ctx, rb_intern_const("no_tlsv1"), 0);
283
+
284
+ no_tlsv1_1 = rb_funcall(micro_ssl_ctx, rb_intern_const("no_tlsv1_1"), 0);
285
+
286
+ TypedData_Get_Struct(self, SSL_CTX, &sslctx_type, ctx);
287
+
288
+ if (!NIL_P(cert)) {
289
+ StringValue(cert);
290
+
291
+ if (SSL_CTX_use_certificate_chain_file(ctx, RSTRING_PTR(cert)) != 1) {
292
+ raise_file_error("SSL_CTX_use_certificate_chain_file", RSTRING_PTR(cert));
293
+ }
294
+ }
295
+
296
+ if (!NIL_P(key_password_command)) {
297
+ key_password = rb_funcall(micro_ssl_ctx, rb_intern_const("key_password"), 0);
298
+
299
+ if (!NIL_P(key_password)) {
300
+ StringValue(key_password);
301
+ password_cb = password_callback;
302
+ password = RSTRING_PTR(key_password);
303
+ SSL_CTX_set_default_passwd_cb(ctx, password_cb);
304
+ SSL_CTX_set_default_passwd_cb_userdata(ctx, (void *) password);
305
+ }
306
+ }
307
+
308
+ if (!NIL_P(key)) {
309
+ StringValue(key);
310
+
311
+ if (SSL_CTX_use_PrivateKey_file(ctx, RSTRING_PTR(key), SSL_FILETYPE_PEM) != 1) {
312
+ raise_file_error("SSL_CTX_use_PrivateKey_file", RSTRING_PTR(key));
313
+ }
314
+ }
315
+
316
+ if (!NIL_P(cert_pem)) {
317
+ X509 *ca = NULL;
318
+ unsigned long err;
319
+
320
+ bio = BIO_new(BIO_s_mem());
321
+ BIO_puts(bio, RSTRING_PTR(cert_pem));
322
+
323
+ /**
324
+ * Much of this pulled as a simplified version of the `use_certificate_chain_file` method
325
+ * from openssl's `ssl_rsa.c` file.
326
+ */
327
+
328
+ /* first read the cert as the first item in the pem file */
329
+ x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
330
+ if (NULL == x509) {
331
+ BIO_free_all(bio);
332
+ raise_param_error("PEM_read_bio_X509", "cert_pem");
333
+ }
334
+
335
+ /* Add the cert to the context */
336
+ /* 1 is success - otherwise check the error codes */
337
+ if (1 != SSL_CTX_use_certificate(ctx, x509)) {
338
+ BIO_free_all(bio);
339
+ raise_param_error("SSL_CTX_use_certificate", "cert_pem");
340
+ }
341
+
342
+ X509_free(x509); /* no longer need our reference */
343
+
344
+ /* Now lets load up the rest of the certificate chain */
345
+ /* 1 is success 0 is error */
346
+ if (0 == SSL_CTX_clear_chain_certs(ctx)) {
347
+ BIO_free_all(bio);
348
+ raise_param_error("SSL_CTX_clear_chain_certs","cert_pem");
349
+ }
350
+
351
+ while (1) {
352
+ ca = PEM_read_bio_X509(bio, NULL, NULL, NULL);
353
+
354
+ if (NULL == ca) {
355
+ break;
356
+ }
357
+
358
+ if (0 == SSL_CTX_add0_chain_cert(ctx, ca)) {
359
+ BIO_free_all(bio);
360
+ raise_param_error("SSL_CTX_add0_chain_cert","cert_pem");
361
+ }
362
+ /* don't free ca - its now owned by the context */
363
+ }
364
+
365
+ /* ca is NULL - so its either the end of the file or an error */
366
+ err = ERR_peek_last_error();
367
+
368
+ /* If its the end of the file - then we are done, in any case free the bio */
369
+ BIO_free_all(bio);
370
+
371
+ if ((ERR_GET_LIB(err) == ERR_LIB_PEM) && (ERR_GET_REASON(err) == PEM_R_NO_START_LINE)) {
372
+ ERR_clear_error();
373
+ } else {
374
+ raise_param_error("PEM_read_bio_X509","cert_pem");
375
+ }
376
+ }
377
+
378
+ if (!NIL_P(key_pem)) {
379
+ bio = BIO_new(BIO_s_mem());
380
+ BIO_puts(bio, RSTRING_PTR(key_pem));
381
+ pkey = PEM_read_bio_PrivateKey(bio, NULL, password_cb, (void *) password);
382
+
383
+ if (SSL_CTX_use_PrivateKey(ctx, pkey) != 1) {
384
+ BIO_free(bio);
385
+ raise_file_error("SSL_CTX_use_PrivateKey", RSTRING_PTR(key_pem));
386
+ }
387
+ EVP_PKEY_free(pkey);
388
+ BIO_free(bio);
389
+ }
390
+
391
+ verification_flags = rb_funcall(micro_ssl_ctx, rb_intern_const("verification_flags"), 0);
392
+
393
+ if (!NIL_P(verification_flags)) {
394
+ X509_VERIFY_PARAM *param = SSL_CTX_get0_param(ctx);
395
+ X509_VERIFY_PARAM_set_flags(param, NUM2INT(verification_flags));
396
+ SSL_CTX_set1_param(ctx, param);
397
+ }
398
+
399
+ if (!NIL_P(ca)) {
400
+ StringValue(ca);
401
+ if (SSL_CTX_load_verify_locations(ctx, RSTRING_PTR(ca), NULL) != 1) {
402
+ raise_file_error("SSL_CTX_load_verify_locations", RSTRING_PTR(ca));
403
+ }
404
+ }
405
+
406
+ ssl_options = SSL_OP_CIPHER_SERVER_PREFERENCE | SSL_OP_SINGLE_ECDH_USE | SSL_OP_NO_COMPRESSION;
407
+
408
+ #ifdef HAVE_SSL_CTX_SET_MIN_PROTO_VERSION
409
+ if (RTEST(no_tlsv1_1)) {
410
+ min = TLS1_2_VERSION;
411
+ }
412
+ else if (RTEST(no_tlsv1)) {
413
+ min = TLS1_1_VERSION;
414
+ }
415
+ else {
416
+ min = TLS1_VERSION;
417
+ }
418
+
419
+ SSL_CTX_set_min_proto_version(ctx, min);
420
+
421
+ #else
422
+ /* As of 1.0.2f, SSL_OP_SINGLE_DH_USE key use is always on */
423
+ ssl_options |= SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_SINGLE_DH_USE;
424
+
425
+ if (RTEST(no_tlsv1)) {
426
+ ssl_options |= SSL_OP_NO_TLSv1;
427
+ }
428
+ if(RTEST(no_tlsv1_1)) {
429
+ ssl_options |= SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1;
430
+ }
431
+ #endif
432
+
433
+ #ifdef HAVE_SSL_CTX_SET_SESSION_CACHE_MODE
434
+ if (!NIL_P(reuse)) {
435
+ SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_SERVER);
436
+ if (!NIL_P(reuse_cache_size)) {
437
+ SSL_CTX_sess_set_cache_size(ctx, NUM2INT(reuse_cache_size));
438
+ }
439
+ if (!NIL_P(reuse_timeout)) {
440
+ SSL_CTX_set_timeout(ctx, NUM2INT(reuse_timeout));
441
+ }
442
+ } else {
443
+ SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
444
+ }
445
+ #endif
446
+
447
+ SSL_CTX_set_options(ctx, ssl_options);
448
+
449
+ if (!NIL_P(ssl_cipher_filter)) {
450
+ StringValue(ssl_cipher_filter);
451
+ SSL_CTX_set_cipher_list(ctx, RSTRING_PTR(ssl_cipher_filter));
452
+ }
453
+ else {
454
+ SSL_CTX_set_cipher_list(ctx, "HIGH:!aNULL@STRENGTH");
455
+ }
456
+
457
+ #if HAVE_SSL_CTX_SET_CIPHERSUITES
458
+ // Only override OpenSSL default ciphersuites if config option is supplied.
459
+ if (!NIL_P(ssl_ciphersuites)) {
460
+ StringValue(ssl_ciphersuites);
461
+ SSL_CTX_set_ciphersuites(ctx, RSTRING_PTR(ssl_ciphersuites));
462
+ }
463
+ #endif
464
+
465
+ #if OPENSSL_VERSION_NUMBER < 0x10002000L
466
+ // Remove this case if OpenSSL 1.0.1 (now EOL) support is no longer needed.
467
+ ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
468
+ if (ecdh) {
469
+ SSL_CTX_set_tmp_ecdh(ctx, ecdh);
470
+ EC_KEY_free(ecdh);
471
+ }
472
+ #elif OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
473
+ SSL_CTX_set_ecdh_auto(ctx, 1);
474
+ #endif
475
+
476
+ if (NIL_P(verify_mode)) {
477
+ /* SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL); */
478
+ } else {
479
+ SSL_CTX_set_verify(ctx, NUM2INT(verify_mode), engine_verify_callback);
480
+ }
481
+
482
+ // Random.bytes available in Ruby 2.5 and later, Random::DEFAULT deprecated in 3.0
483
+ session_id_bytes = rb_funcall(
484
+ #ifdef HAVE_RANDOM_BYTES
485
+ rb_cRandom,
486
+ #else
487
+ rb_const_get(rb_cRandom, rb_intern_const("DEFAULT")),
488
+ #endif
489
+ rb_intern_const("bytes"),
490
+ 1, ULL2NUM(SSL_MAX_SSL_SESSION_ID_LENGTH));
491
+
492
+ SSL_CTX_set_session_id_context(ctx,
493
+ (unsigned char *) RSTRING_PTR(session_id_bytes),
494
+ SSL_MAX_SSL_SESSION_ID_LENGTH);
495
+
496
+ // printf("\ninitialize end security_level %d\n", SSL_CTX_get_security_level(ctx));
497
+
498
+ #ifdef HAVE_SSL_CTX_SET_DH_AUTO
499
+ // https://www.openssl.org/docs/man3.0/man3/SSL_CTX_set_dh_auto.html
500
+ SSL_CTX_set_dh_auto(ctx, 1);
501
+ #else
502
+ dh = get_dh2048();
503
+ SSL_CTX_set_tmp_dh(ctx, dh);
504
+ #endif
505
+
506
+ rb_obj_freeze(self);
507
+ return self;
508
+ }
509
+
510
+ VALUE engine_init_server(VALUE self, VALUE sslctx) {
511
+ ms_conn* conn;
512
+ VALUE obj;
513
+ SSL_CTX* ctx;
514
+ SSL* ssl;
515
+
516
+ conn = engine_alloc(self, &obj);
517
+
518
+ TypedData_Get_Struct(sslctx, SSL_CTX, &sslctx_type, ctx);
519
+
520
+ ssl = SSL_new(ctx);
521
+ conn->ssl = ssl;
522
+ SSL_set_app_data(ssl, NULL);
523
+ SSL_set_bio(ssl, conn->read, conn->write);
524
+ SSL_set_accept_state(ssl);
525
+ return obj;
526
+ }
527
+
528
+ VALUE engine_init_client(VALUE klass) {
529
+ VALUE obj;
530
+ ms_conn* conn = engine_alloc(klass, &obj);
531
+ #ifdef HAVE_DTLS_METHOD
532
+ conn->ctx = SSL_CTX_new(DTLS_method());
533
+ #else
534
+ conn->ctx = SSL_CTX_new(DTLSv1_method());
535
+ #endif
536
+ conn->ssl = SSL_new(conn->ctx);
537
+ SSL_set_app_data(conn->ssl, NULL);
538
+ SSL_set_verify(conn->ssl, SSL_VERIFY_NONE, NULL);
539
+
540
+ SSL_set_bio(conn->ssl, conn->read, conn->write);
541
+
542
+ SSL_set_connect_state(conn->ssl);
543
+ return obj;
544
+ }
545
+
546
+ VALUE engine_inject(VALUE self, VALUE str) {
547
+ ms_conn* conn;
548
+ long used;
549
+
550
+ TypedData_Get_Struct(self, ms_conn, &engine_data_type, conn);
551
+
552
+ StringValue(str);
553
+
554
+ used = BIO_write(conn->read, RSTRING_PTR(str), (int)RSTRING_LEN(str));
555
+
556
+ if(used == 0 || used == -1) {
557
+ return Qfalse;
558
+ }
559
+
560
+ return INT2FIX(used);
561
+ }
562
+
563
+ NORETURN(void raise_error(SSL* ssl, int result));
564
+
565
+ void raise_error(SSL* ssl, int result) {
566
+ char buf[512];
567
+ char msg[768];
568
+ const char* err_str;
569
+ int err = errno;
570
+ int mask = 4095;
571
+ int ssl_err = SSL_get_error(ssl, result);
572
+ int verify_err = (int) SSL_get_verify_result(ssl);
573
+
574
+ if(SSL_ERROR_SYSCALL == ssl_err) {
575
+ snprintf(msg, sizeof(msg), "System error: %s - %d", strerror(err), err);
576
+
577
+ } else if(SSL_ERROR_SSL == ssl_err) {
578
+ if(X509_V_OK != verify_err) {
579
+ err_str = X509_verify_cert_error_string(verify_err);
580
+ snprintf(msg, sizeof(msg),
581
+ "OpenSSL certificate verification error: %s - %d",
582
+ err_str, verify_err);
583
+
584
+ } else {
585
+ err = (int) ERR_get_error();
586
+ ERR_error_string_n(err, buf, sizeof(buf));
587
+ snprintf(msg, sizeof(msg), "OpenSSL error: %s - %d", buf, err & mask);
588
+ }
589
+ } else {
590
+ snprintf(msg, sizeof(msg), "Unknown OpenSSL error: %d", ssl_err);
591
+ }
592
+
593
+ ERR_clear_error();
594
+ rb_raise(eError, "%s", msg);
595
+ }
596
+
597
+ VALUE engine_read(VALUE self) {
598
+ ms_conn* conn;
599
+ char buf[512];
600
+ int bytes, error;
601
+
602
+ TypedData_Get_Struct(self, ms_conn, &engine_data_type, conn);
603
+
604
+ ERR_clear_error();
605
+
606
+ bytes = SSL_read(conn->ssl, (void*)buf, sizeof(buf));
607
+
608
+ if(bytes > 0) {
609
+ return rb_str_new(buf, bytes);
610
+ }
611
+
612
+ if(SSL_want_read(conn->ssl)) return Qnil;
613
+
614
+ error = SSL_get_error(conn->ssl, bytes);
615
+
616
+ if(error == SSL_ERROR_ZERO_RETURN) {
617
+ rb_eof_error();
618
+ } else {
619
+ raise_error(conn->ssl, bytes);
620
+ }
621
+
622
+ return Qnil;
623
+ }
624
+
625
+ VALUE engine_write(VALUE self, VALUE str) {
626
+ ms_conn* conn;
627
+ int bytes;
628
+
629
+ TypedData_Get_Struct(self, ms_conn, &engine_data_type, conn);
630
+
631
+ StringValue(str);
632
+
633
+ ERR_clear_error();
634
+
635
+ bytes = SSL_write(conn->ssl, (void*)RSTRING_PTR(str), (int)RSTRING_LEN(str));
636
+ if(bytes > 0) {
637
+ return INT2FIX(bytes);
638
+ }
639
+
640
+ if(SSL_want_write(conn->ssl)) return Qnil;
641
+
642
+ raise_error(conn->ssl, bytes);
643
+
644
+ return Qnil;
645
+ }
646
+
647
+ VALUE engine_extract(VALUE self) {
648
+ ms_conn* conn;
649
+ int bytes;
650
+ size_t pending;
651
+ // https://www.openssl.org/docs/manmaster/man3/BIO_f_buffer.html
652
+ // crypto/bio/bf_buff.c DEFAULT_BUFFER_SIZE
653
+ char buf[4096];
654
+
655
+ TypedData_Get_Struct(self, ms_conn, &engine_data_type, conn);
656
+
657
+ pending = BIO_pending(conn->write);
658
+ if(pending > 0) {
659
+ bytes = BIO_read(conn->write, buf, sizeof(buf));
660
+ if(bytes > 0) {
661
+ return rb_str_new(buf, bytes);
662
+ } else if(!BIO_should_retry(conn->write)) {
663
+ raise_error(conn->ssl, bytes);
664
+ }
665
+ }
666
+
667
+ return Qnil;
668
+ }
669
+
670
+ VALUE engine_shutdown(VALUE self) {
671
+ ms_conn* conn;
672
+ int ok;
673
+
674
+ TypedData_Get_Struct(self, ms_conn, &engine_data_type, conn);
675
+
676
+ ERR_clear_error();
677
+
678
+ ok = SSL_shutdown(conn->ssl);
679
+ if (ok == 0) {
680
+ return Qfalse;
681
+ }
682
+
683
+ return Qtrue;
684
+ }
685
+
686
+ VALUE engine_init(VALUE self) {
687
+ ms_conn* conn;
688
+
689
+ TypedData_Get_Struct(self, ms_conn, &engine_data_type, conn);
690
+
691
+ return SSL_in_init(conn->ssl) ? Qtrue : Qfalse;
692
+ }
693
+
694
+ VALUE engine_peercert(VALUE self) {
695
+ ms_conn* conn;
696
+ X509* cert;
697
+ int bytes;
698
+ unsigned char* buf = NULL;
699
+ ms_cert_buf* cert_buf = NULL;
700
+ VALUE rb_cert_buf;
701
+
702
+ TypedData_Get_Struct(self, ms_conn, &engine_data_type, conn);
703
+
704
+ #ifdef HAVE_SSL_GET1_PEER_CERTIFICATE
705
+ cert = SSL_get1_peer_certificate(conn->ssl);
706
+ #else
707
+ cert = SSL_get_peer_certificate(conn->ssl);
708
+ #endif
709
+ if(!cert) {
710
+ /*
711
+ * See if there was a failed certificate associated with this client.
712
+ */
713
+ cert_buf = (ms_cert_buf*)SSL_get_app_data(conn->ssl);
714
+ if(!cert_buf) {
715
+ return Qnil;
716
+ }
717
+ buf = cert_buf->buf;
718
+ bytes = cert_buf->bytes;
719
+
720
+ } else {
721
+ bytes = i2d_X509(cert, &buf);
722
+ X509_free(cert);
723
+
724
+ if(bytes < 0) {
725
+ return Qnil;
726
+ }
727
+ }
728
+
729
+ rb_cert_buf = rb_str_new((const char*)(buf), bytes);
730
+ if(!cert_buf) {
731
+ OPENSSL_free(buf);
732
+ }
733
+
734
+ return rb_cert_buf;
735
+ }
736
+
737
+ /* @see UringMachine::SSL::Socket#ssl_version_state
738
+ * @version 5.0.0
739
+ */
740
+ static VALUE
741
+ engine_ssl_vers_st(VALUE self) {
742
+ ms_conn* conn;
743
+ TypedData_Get_Struct(self, ms_conn, &engine_data_type, conn);
744
+ return rb_ary_new3(2, rb_str_new2(SSL_get_version(conn->ssl)), rb_str_new2(SSL_state_string(conn->ssl)));
745
+ }
746
+
747
+ VALUE noop(VALUE self) {
748
+ return Qnil;
749
+ }
750
+
751
+ void Init_micro_ssl(VALUE mod) {
752
+ VALUE cSSL, cEngine, cCtx;
753
+
754
+ /* Fake operation for documentation (RDoc, YARD) */
755
+ #if 0 == 1
756
+ mod = rb_define_module("UringMachine");
757
+ #endif
758
+
759
+ SSL_library_init();
760
+ OpenSSL_add_ssl_algorithms();
761
+ SSL_load_error_strings();
762
+ ERR_load_crypto_strings();
763
+
764
+ cSSL = rb_define_module_under(mod, "SSL");
765
+
766
+ cEngine = rb_define_class_under(cSSL, "Engine", rb_cObject);
767
+ rb_undef_alloc_func(cEngine);
768
+
769
+ cCtx = rb_define_class_under(cSSL, "SSLContext", rb_cObject);
770
+ rb_define_alloc_func(cCtx, sslctx_alloc);
771
+ rb_define_method(cCtx, "initialize", sslctx_initialize, 1);
772
+ rb_undef_method(cCtx, "initialize_copy");
773
+
774
+
775
+ // OpenSSL Build / Runtime/Load versions
776
+
777
+ /* Version of OpenSSL that UringMachine was compiled with */
778
+ rb_define_const(cSSL, "OPENSSL_VERSION", rb_str_new2(OPENSSL_VERSION_TEXT));
779
+
780
+ #if !defined(LIBRESSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x10100000
781
+ /* Version of OpenSSL that UringMachine loaded with */
782
+ rb_define_const(cSSL, "OPENSSL_LIBRARY_VERSION", rb_str_new2(OpenSSL_version(OPENSSL_VERSION)));
783
+ #else
784
+ rb_define_const(cSSL, "OPENSSL_LIBRARY_VERSION", rb_str_new2(SSLeay_version(SSLEAY_VERSION)));
785
+ #endif
786
+
787
+ #if defined(OPENSSL_NO_SSL3) || defined(OPENSSL_NO_SSL3_METHOD)
788
+ /* True if SSL3 is not available */
789
+ rb_define_const(cSSL, "OPENSSL_NO_SSL3", Qtrue);
790
+ #else
791
+ rb_define_const(cSSL, "OPENSSL_NO_SSL3", Qfalse);
792
+ #endif
793
+
794
+ #if defined(OPENSSL_NO_TLS1) || defined(OPENSSL_NO_TLS1_METHOD)
795
+ /* True if TLS1 is not available */
796
+ rb_define_const(cSSL, "OPENSSL_NO_TLS1", Qtrue);
797
+ #else
798
+ rb_define_const(cSSL, "OPENSSL_NO_TLS1", Qfalse);
799
+ #endif
800
+
801
+ #if defined(OPENSSL_NO_TLS1_1) || defined(OPENSSL_NO_TLS1_1_METHOD)
802
+ /* True if TLS1_1 is not available */
803
+ rb_define_const(cSSL, "OPENSSL_NO_TLS1_1", Qtrue);
804
+ #else
805
+ rb_define_const(cSSL, "OPENSSL_NO_TLS1_1", Qfalse);
806
+ #endif
807
+
808
+ rb_define_singleton_method(cSSL, "check", noop, 0);
809
+
810
+ eError = rb_define_class_under(cSSL, "SSLError", rb_eStandardError);
811
+
812
+ rb_define_singleton_method(cEngine, "server", engine_init_server, 1);
813
+ rb_define_singleton_method(cEngine, "client", engine_init_client, 0);
814
+
815
+ rb_define_method(cEngine, "inject", engine_inject, 1);
816
+ rb_define_method(cEngine, "read", engine_read, 0);
817
+
818
+ rb_define_method(cEngine, "write", engine_write, 1);
819
+ rb_define_method(cEngine, "extract", engine_extract, 0);
820
+
821
+ rb_define_method(cEngine, "shutdown", engine_shutdown, 0);
822
+
823
+ rb_define_method(cEngine, "init?", engine_init, 0);
824
+
825
+ /* @!attribute [r] peercert
826
+ * Returns `nil` when `MiniSSL::Context#verify_mode` is set to `VERIFY_NONE`.
827
+ * @return [String, nil] DER encoded cert
828
+ */
829
+ rb_define_method(cEngine, "peercert", engine_peercert, 0);
830
+
831
+ rb_define_method(cEngine, "ssl_vers_st", engine_ssl_vers_st, 0);
832
+ }
833
+
834
+ #else
835
+
836
+ NORETURN(VALUE raise_error(VALUE self));
837
+
838
+ VALUE raise_error(VALUE self) {
839
+ rb_raise(rb_eStandardError, "SSL not available in this build");
840
+ }
841
+
842
+ void Init_micro_ssl(VALUE mod) {
843
+ VALUE cSSL;
844
+
845
+ cSSL = rb_define_module_under(mod, "SSL");
846
+ rb_define_class_under(cSSL, "SSLError", rb_eStandardError);
847
+
848
+ rb_define_singleton_method(cSSL, "check", raise_error, 0);
849
+ }
850
+ #endif