Skip to content

Commit 6675b33

Browse files
committed
Fix TOCTOU issues involving implicit type conversion
A user can supply objects with malicious #to_str or #to_int methods to trigger a use-after-free. Reorder the input validation to prevent this.
1 parent 680370b commit 6675b33

11 files changed

Lines changed: 81 additions & 43 deletions

File tree

ext/openssl/extconf.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@
4545
have_func("rb_io_maybe_wait(0, Qnil, Qnil, Qnil)", "ruby/io.h")
4646
# Ruby 3.2
4747
have_func("rb_io_timeout", "ruby/io.h")
48+
# Ruby 4.1
49+
have_func("rb_str_cstr(Qnil)", "ruby.h")
4850

4951
Logging::message "=== Checking for system dependent stuff... ===\n"
5052
have_library("nsl", "t_open")

ext/openssl/ossl.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,15 @@
2323
#define RUBY_TYPED_FROZEN_SHAREABLE 0
2424
#endif
2525

26+
#ifndef HAVE_RB_STR_CSTR
27+
static inline const char *
28+
rb_str_cstr(VALUE str)
29+
{
30+
RUBY_ASSERT(RB_TYPE_P(str, T_STRING));
31+
return StringValueCStr(str);
32+
}
33+
#endif
34+
2635
#include <openssl/opensslv.h>
2736

2837
#include <openssl/err.h>

ext/openssl/ossl_asn1.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1289,12 +1289,12 @@ ossl_asn1cons_each(VALUE self)
12891289
static VALUE
12901290
ossl_asn1obj_s_register(VALUE self, VALUE oid, VALUE sn, VALUE ln)
12911291
{
1292-
StringValueCStr(oid);
1293-
StringValueCStr(sn);
1294-
StringValueCStr(ln);
1292+
StringValue(oid);
1293+
StringValue(sn);
1294+
StringValue(ln);
12951295

1296-
if(!OBJ_create(RSTRING_PTR(oid), RSTRING_PTR(sn), RSTRING_PTR(ln)))
1297-
ossl_raise(eASN1Error, NULL);
1296+
if (!OBJ_create(rb_str_cstr(oid), rb_str_cstr(sn), rb_str_cstr(ln)))
1297+
ossl_raise(eASN1Error, "OBJ_create");
12981298

12991299
return Qtrue;
13001300
}

ext/openssl/ossl_cipher.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,7 @@ ossl_cipher_pkcs5_keyivgen(int argc, VALUE *argv, VALUE self)
289289
const EVP_MD *digest;
290290
VALUE vpass, vsalt, viter, vdigest, md_holder;
291291
unsigned char key[EVP_MAX_KEY_LENGTH], iv[EVP_MAX_IV_LENGTH], *salt = NULL;
292+
unsigned char saltbuf[PKCS5_SALT_LEN];
292293
int iter;
293294

294295
rb_scan_args(argc, argv, "13", &vpass, &vsalt, &viter, &vdigest);
@@ -297,7 +298,8 @@ ossl_cipher_pkcs5_keyivgen(int argc, VALUE *argv, VALUE self)
297298
StringValue(vsalt);
298299
if(RSTRING_LEN(vsalt) != PKCS5_SALT_LEN)
299300
ossl_raise(eCipherError, "salt must be an 8-octet string");
300-
salt = (unsigned char *)RSTRING_PTR(vsalt);
301+
memcpy(saltbuf, RSTRING_PTR(vsalt), PKCS5_SALT_LEN);
302+
salt = saltbuf;
301303
}
302304
iter = NIL_P(viter) ? 2048 : NUM2INT(viter);
303305
if (iter <= 0)

ext/openssl/ossl_config.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -190,11 +190,11 @@ config_get_value(VALUE self, VALUE section, VALUE key)
190190
CONF *conf = GetConfig(self);
191191
const char *str, *sectionp;
192192

193-
StringValueCStr(section);
194-
StringValueCStr(key);
193+
StringValue(section);
194+
StringValue(key);
195195
/* For compatibility; NULL means "default". */
196-
sectionp = RSTRING_LEN(section) ? RSTRING_PTR(section) : NULL;
197-
str = NCONF_get_string(conf, sectionp, RSTRING_PTR(key));
196+
sectionp = RSTRING_LEN(section) ? rb_str_cstr(section) : NULL;
197+
str = NCONF_get_string(conf, sectionp, rb_str_cstr(key));
198198
if (!str) {
199199
ossl_clear_error();
200200
return Qnil;

ext/openssl/ossl_engine.c

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -312,14 +312,19 @@ ossl_engine_load_privkey(int argc, VALUE *argv, VALUE self)
312312
ENGINE *e;
313313
EVP_PKEY *pkey;
314314
VALUE id, data, obj;
315-
char *sid, *sdata;
316315

317316
rb_scan_args(argc, argv, "02", &id, &data);
318-
sid = NIL_P(id) ? NULL : StringValueCStr(id);
319-
sdata = NIL_P(data) ? NULL : StringValueCStr(data);
317+
if (!NIL_P(id))
318+
StringValue(id);
319+
if (!NIL_P(data))
320+
StringValue(data);
320321
GetEngine(self, e);
321-
pkey = ENGINE_load_private_key(e, sid, NULL, sdata);
322-
if (!pkey) ossl_raise(eEngineError, NULL);
322+
pkey = ENGINE_load_private_key(e,
323+
NIL_P(id) ? NULL : rb_str_cstr(id),
324+
NULL,
325+
NIL_P(data) ? NULL : rb_str_cstr(data));
326+
if (!pkey)
327+
ossl_raise(eEngineError, "ENGINE_load_private_key");
323328
obj = ossl_pkey_wrap(pkey);
324329
OSSL_PKEY_SET_PRIVATE(obj);
325330

@@ -341,14 +346,19 @@ ossl_engine_load_pubkey(int argc, VALUE *argv, VALUE self)
341346
ENGINE *e;
342347
EVP_PKEY *pkey;
343348
VALUE id, data;
344-
char *sid, *sdata;
345349

346350
rb_scan_args(argc, argv, "02", &id, &data);
347-
sid = NIL_P(id) ? NULL : StringValueCStr(id);
348-
sdata = NIL_P(data) ? NULL : StringValueCStr(data);
351+
if (!NIL_P(id))
352+
StringValue(id);
353+
if (!NIL_P(data))
354+
StringValue(data);
349355
GetEngine(self, e);
350-
pkey = ENGINE_load_public_key(e, sid, NULL, sdata);
351-
if (!pkey) ossl_raise(eEngineError, NULL);
356+
pkey = ENGINE_load_public_key(e,
357+
NIL_P(id) ? NULL : rb_str_cstr(id),
358+
NULL,
359+
NIL_P(data) ? NULL : rb_str_cstr(data));
360+
if (!pkey)
361+
ossl_raise(eEngineError, "ENGINE_load_public_key");
352362

353363
return ossl_pkey_wrap(pkey);
354364
}
@@ -398,9 +408,13 @@ ossl_engine_ctrl_cmd(int argc, VALUE *argv, VALUE self)
398408

399409
GetEngine(self, e);
400410
rb_scan_args(argc, argv, "11", &cmd, &val);
401-
ret = ENGINE_ctrl_cmd_string(e, StringValueCStr(cmd),
402-
NIL_P(val) ? NULL : StringValueCStr(val), 0);
403-
if (!ret) ossl_raise(eEngineError, NULL);
411+
StringValue(cmd);
412+
if (!NIL_P(val))
413+
StringValue(val);
414+
ret = ENGINE_ctrl_cmd_string(e, rb_str_cstr(cmd),
415+
NIL_P(val) ? NULL : rb_str_cstr(val), 0);
416+
if (!ret)
417+
ossl_raise(eEngineError, "ENGINE_ctrl_cmd_string");
404418

405419
return self;
406420
}

ext/openssl/ossl_kdf.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -265,14 +265,14 @@ kdf_hkdf(int argc, VALUE *argv, VALUE self)
265265
rb_get_kwargs(opts, kwargs_ids, 4, 0, kwargs);
266266

267267
StringValue(ikm);
268-
ikmlen = RSTRING_LENINT(ikm);
269268
salt = StringValue(kwargs[0]);
270-
saltlen = RSTRING_LENINT(salt);
271269
info = StringValue(kwargs[1]);
272-
infolen = RSTRING_LENINT(info);
273270
len = (size_t)NUM2LONG(kwargs[2]);
274271
if (len > LONG_MAX)
275272
rb_raise(rb_eArgError, "length must be non-negative");
273+
ikmlen = RSTRING_LENINT(ikm);
274+
saltlen = RSTRING_LENINT(salt);
275+
infolen = RSTRING_LENINT(info);
276276
md = ossl_evp_md_fetch(kwargs[3], &md_holder);
277277

278278
str = rb_str_new(NULL, (long)len);

ext/openssl/ossl_pkcs12.c

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -90,16 +90,18 @@ ossl_pkcs12_s_create(int argc, VALUE *argv, VALUE self)
9090
{
9191
VALUE pass, name, pkey, cert, ca, key_nid, cert_nid, key_iter, mac_iter, keytype;
9292
VALUE obj;
93-
char *passphrase, *friendlyname;
93+
const char *passphrase, *friendlyname;
9494
EVP_PKEY *key;
9595
X509 *x509;
9696
STACK_OF(X509) *x509s;
9797
int nkey = 0, ncert = 0, kiter = 0, miter = 0, ktype = 0;
9898
PKCS12 *p12;
9999

100100
rb_scan_args(argc, argv, "46", &pass, &name, &pkey, &cert, &ca, &key_nid, &cert_nid, &key_iter, &mac_iter, &keytype);
101-
passphrase = NIL_P(pass) ? NULL : StringValueCStr(pass);
102-
friendlyname = NIL_P(name) ? NULL : StringValueCStr(name);
101+
if (!NIL_P(pass))
102+
StringValue(pass);
103+
if (!NIL_P(name))
104+
StringValue(name);
103105
key = GetPKeyPtr(pkey);
104106
x509 = GetX509CertPtr(cert);
105107
/* TODO: make a VALUE to nid function */
@@ -117,6 +119,8 @@ ossl_pkcs12_s_create(int argc, VALUE *argv, VALUE self)
117119
miter = NUM2INT(mac_iter);
118120
if (!NIL_P(keytype))
119121
ktype = NUM2INT(keytype);
122+
passphrase = NIL_P(pass) ? NULL : rb_str_cstr(pass);
123+
friendlyname = NIL_P(name) ? NULL : rb_str_cstr(name);
120124

121125
#if defined(OPENSSL_IS_AWSLC)
122126
if (ktype != 0) {
@@ -177,7 +181,7 @@ ossl_pkcs12_initialize(int argc, VALUE *argv, VALUE self)
177181
PKCS12 *p12;
178182
BIO *in;
179183
VALUE arg, pass, pkey, cert, ca;
180-
char *passphrase;
184+
const char *passphrase;
181185
EVP_PKEY *key;
182186
X509 *x509;
183187
STACK_OF(X509) *x509s = NULL;
@@ -192,7 +196,8 @@ ossl_pkcs12_initialize(int argc, VALUE *argv, VALUE self)
192196
RTYPEDDATA_DATA(self) = p12;
193197
return self;
194198
}
195-
passphrase = NIL_P(pass) ? NULL : StringValueCStr(pass);
199+
if (!NIL_P(pass))
200+
StringValue(pass);
196201
in = ossl_obj2bio(&arg);
197202
p12 = d2i_PKCS12_bio(in, NULL);
198203
BIO_free(in);
@@ -201,6 +206,7 @@ ossl_pkcs12_initialize(int argc, VALUE *argv, VALUE self)
201206
RTYPEDDATA_DATA(self) = p12;
202207

203208
pkey = cert = ca = Qnil;
209+
passphrase = NIL_P(pass) ? NULL : rb_str_cstr(pass);
204210
if (!PKCS12_parse(p12, passphrase, &key, &x509, &x509s))
205211
ossl_raise(ePKCS12Error, "PKCS12_parse");
206212
if (key) {

ext/openssl/ossl_pkey.c

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -251,9 +251,10 @@ pkey_ctx_apply_options_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, ctx_v))
251251

252252
if (SYMBOL_P(key))
253253
key = rb_sym2str(key);
254+
StringValue(key);
254255
value = rb_String(value);
255256

256-
if (EVP_PKEY_CTX_ctrl_str(ctx, StringValueCStr(key), StringValueCStr(value)) <= 0)
257+
if (EVP_PKEY_CTX_ctrl_str(ctx, rb_str_cstr(key), rb_str_cstr(value)) <= 0)
257258
ossl_raise(ePKeyError, "EVP_PKEY_CTX_ctrl_str(ctx, %+"PRIsVALUE", %+"PRIsVALUE")",
258259
key, value);
259260
return Qnil;
@@ -641,7 +642,6 @@ lookup_pkey_type(VALUE type)
641642
const EVP_PKEY_ASN1_METHOD *ameth;
642643
int pkey_id;
643644

644-
StringValue(type);
645645
/*
646646
* XXX: EVP_PKEY_asn1_find_str() looks up a PEM type string. Should we use
647647
* OBJ_txt2nid() instead (and then somehow check if the NID is an acceptable
@@ -670,11 +670,12 @@ ossl_pkey_new_raw_private_key(VALUE self, VALUE type, VALUE key)
670670
EVP_PKEY *pkey;
671671
size_t keylen;
672672

673+
StringValue(type);
673674
StringValue(key);
674675
keylen = RSTRING_LEN(key);
675676

676677
#ifdef OSSL_USE_PROVIDER
677-
pkey = EVP_PKEY_new_raw_private_key_ex(NULL, StringValueCStr(type), NULL,
678+
pkey = EVP_PKEY_new_raw_private_key_ex(NULL, rb_str_cstr(type), NULL,
678679
(unsigned char *)RSTRING_PTR(key),
679680
keylen);
680681
if (!pkey)
@@ -702,11 +703,12 @@ ossl_pkey_new_raw_public_key(VALUE self, VALUE type, VALUE key)
702703
EVP_PKEY *pkey;
703704
size_t keylen;
704705

706+
StringValue(type);
705707
StringValue(key);
706708
keylen = RSTRING_LEN(key);
707709

708710
#ifdef OSSL_USE_PROVIDER
709-
pkey = EVP_PKEY_new_raw_public_key_ex(NULL, StringValueCStr(type), NULL,
711+
pkey = EVP_PKEY_new_raw_public_key_ex(NULL, rb_str_cstr(type), NULL,
710712
(unsigned char *)RSTRING_PTR(key),
711713
keylen);
712714
if (!pkey)

ext/openssl/ossl_rand.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,9 @@ ossl_rand_seed(VALUE self, VALUE str)
5252
static VALUE
5353
ossl_rand_add(VALUE self, VALUE str, VALUE entropy)
5454
{
55+
double randomness = NUM2DBL(entropy);
5556
StringValue(str);
56-
RAND_add(RSTRING_PTR(str), RSTRING_LENINT(str), NUM2DBL(entropy));
57+
RAND_add(RSTRING_PTR(str), RSTRING_LENINT(str), randomness);
5758

5859
return self;
5960
}

0 commit comments

Comments
 (0)