Lokasi ngalangkungan proxy:   [ UP ]  
[Ngawartoskeun bug]   [Panyetelan cookie]                
Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
579 changes: 367 additions & 212 deletions ext/openssl/ossl_ssl.c

Large diffs are not rendered by default.

13 changes: 13 additions & 0 deletions ext/openssl/ossl_ssl.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,20 @@ extern VALUE mSSL;
extern VALUE cSSLSocket;
extern VALUE cSSLSession;

struct ossl_ssl_data {
VALUE self;
VALUE io;
int cb_state;

/* Used by ossl_ssl_bio_method */
int bio_eof;
};
struct ossl_ssl_data *ossl_ssl_data(const SSL *ssl);

BIO *ossl_ssl_bio_setup(struct ossl_ssl_data *p);

void Init_ossl_ssl(void);
void Init_ossl_ssl_session(void);
void Init_ossl_ssl_bio(void);

#endif /* _OSSL_SSL_H_ */
241 changes: 241 additions & 0 deletions ext/openssl/ossl_ssl_bio.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
/*
* Ruby/OpenSSL Project
* Copyright (C) 2025 Kazuki Yamaguchi <k@rhe.jp>
*/
#include "ossl.h"

static BIO_METHOD *ossl_bio_meth;
static VALUE nonblock_kwargs, sym_wait_readable, sym_wait_writable;

BIO *
ossl_ssl_bio_setup(struct ossl_ssl_data *p)
{
BIO *bio = BIO_new(ossl_bio_meth);
if (!bio)
ossl_raise(eOSSLError, "BIO_new");

BIO_set_data(bio, p);
BIO_set_init(bio, 1);
return bio;
}

struct call0_args {
VALUE (*func)(VALUE);
VALUE args;
VALUE ret;
};

static VALUE
do_nothing(VALUE _)
{
return Qnil;
}

static VALUE
call_protect1(VALUE args_)
{
struct call0_args *args = (void *)args_;
rb_set_errinfo(Qnil);
args->ret = args->func(args->args);
return Qnil;
}

static VALUE
call_protect0(VALUE args_)
{
rb_ensure(do_nothing, Qnil, call_protect1, args_);
return Qnil;
}

static VALUE
call_protect(VALUE (*func)(VALUE), VALUE args, int *state, int current)
{
if (!current)
return rb_protect(func, args, state);

VALUE errinfo = rb_errinfo();
struct call0_args call0_args = { func, args, Qnil };
rb_protect(call_protect0, (VALUE)&call0_args, state);
if (*state) {
if (!rb_obj_is_kind_of(errinfo, rb_eException))
errinfo = rb_str_new_cstr("(unknown)");
rb_warn("BIO callback raised an exception, pending jump suppressed: " \
"state=%d, errinfo=%+"PRIsVALUE, current, errinfo);
}
return call0_args.ret;
}


struct bwrite_args {
struct ossl_ssl_data *p;
BIO *bio;
const char *data;
int dlen;
int written;
};

static VALUE
bio_bwrite0(VALUE args_)
{
struct bwrite_args *args = (void *)args_;
struct ossl_ssl_data *p = args->p;

BIO_clear_retry_flags(args->bio);

VALUE fargs[] = { rb_str_new_static(args->data, args->dlen), nonblock_kwargs };
VALUE ret = rb_funcallv_kw(p->io, rb_intern("write_nonblock"),
2, fargs, RB_PASS_KEYWORDS);

if (RB_INTEGER_TYPE_P(ret)) {
args->written = NUM2INT(ret);
return Qtrue;
}
else if (ret == sym_wait_readable) {
BIO_set_retry_read(args->bio);
return Qfalse;
}
else if (ret == sym_wait_writable) {
BIO_set_retry_write(args->bio);
return Qfalse;
}
else {
rb_raise(rb_eTypeError, "write_nonblock must return an Integer, "
":wait_readable, or :wait_writable");
}
}

static int
bio_bwrite(BIO *bio, const char *data, int dlen)
{
struct ossl_ssl_data *p = BIO_get_data(bio);

struct bwrite_args args = { p, bio, data, dlen, 0 };
int state;
VALUE ok = call_protect(bio_bwrite0, (VALUE)&args, &state, p->cb_state);
if (state) {
p->cb_state = state;
return -1;
}
if (RTEST(ok))
return args.written;
return -1;
}

struct bread_args {
struct ossl_ssl_data *p;
BIO *bio;
char *data;
int dlen;
int readbytes;
};

static VALUE
bio_bread0(VALUE args_)
{
struct bread_args *args = (void *)args_;
struct ossl_ssl_data *p = args->p;

BIO_clear_retry_flags(args->bio);

VALUE fargs[] = { INT2NUM(args->dlen), nonblock_kwargs };
VALUE ret = rb_funcallv_kw(p->io, rb_intern("read_nonblock"),
2, fargs, RB_PASS_KEYWORDS);

if (RB_TYPE_P(ret, T_STRING)) {
int len = RSTRING_LENINT(ret);
if (len > args->dlen)
rb_raise(rb_eTypeError, "read_nonblock returned too much data");
memcpy(args->data, RSTRING_PTR(ret), len);
args->readbytes = len;
return Qtrue;
}
else if (NIL_P(ret)) {
// In OpenSSL 3.0 or later: BIO_set_flags(args->bio, BIO_FLAGS_IN_EOF);
p->bio_eof = 1;
return Qtrue;
}
else if (ret == sym_wait_readable) {
BIO_set_retry_read(args->bio);
return Qfalse;
}
else if (ret == sym_wait_writable) {
BIO_set_retry_write(args->bio);
return Qfalse;
}
else {
rb_raise(rb_eTypeError, "write_nonblock must return an Integer, "
":wait_readable, or :wait_writable");
}
}

static int
bio_bread(BIO *bio, char *data, int dlen)
{
struct ossl_ssl_data *p = BIO_get_data(bio);

struct bread_args args = { p, bio, data, dlen, 0 };
int state;
VALUE ok = call_protect(bio_bread0, (VALUE)&args, &state, p->cb_state);
if (state) {
p->cb_state = state;
return -1;
}
if (RTEST(ok))
return args.readbytes;
return -1;
}

static VALUE
bio_flush0(VALUE p_)
{
struct ossl_ssl_data *p = (void *)p_;
/*
* If the underlying IO-like object does not respond to flush, let's just
* assume that it does not need to be flushed.
*/
return rb_check_funcall(p->io, rb_intern("flush"), 0, NULL);
}

static long
bio_ctrl(BIO *bio, int cmd, long larg, void *parg)
{
struct ossl_ssl_data *p = BIO_get_data(bio);
int state;

switch (cmd) {
case BIO_CTRL_EOF:
return p->bio_eof;
case BIO_CTRL_FLUSH:
call_protect(bio_flush0, (VALUE)p, &state, p->cb_state);
if (state) {
p->cb_state = state;
return 0;
}
return 1;
default:
return 0;
}
}

void
Init_ossl_ssl_bio(void)
{
ossl_bio_meth = BIO_meth_new(BIO_TYPE_SOURCE_SINK, "Ruby IO-like object");
if (!ossl_bio_meth)
ossl_raise(eOSSLError, "BIO_meth_new");
if (!BIO_meth_set_write(ossl_bio_meth, bio_bwrite) ||
!BIO_meth_set_read(ossl_bio_meth, bio_bread) ||
!BIO_meth_set_ctrl(ossl_bio_meth, bio_ctrl)) {
BIO_meth_free(ossl_bio_meth);
ossl_bio_meth = NULL;
ossl_raise(eOSSLError, "BIO_meth_set_*");
}

nonblock_kwargs = rb_hash_new();
rb_hash_aset(nonblock_kwargs, ID2SYM(rb_intern_const("exception")), Qfalse);
rb_global_variable(&nonblock_kwargs);

sym_wait_readable = ID2SYM(rb_intern_const("wait_readable"));
sym_wait_writable = ID2SYM(rb_intern_const("wait_writable"));
}

58 changes: 42 additions & 16 deletions ext/openssl/ossl_x509store.c
Original file line number Diff line number Diff line change
Expand Up @@ -116,10 +116,9 @@ static void
ossl_x509store_mark(void *ptr)
{
X509_STORE *store = ptr;
// Note: this reference is stored as @verify_callback so we don't need to mark it.
// However we do need to ensure GC compaction won't move it, hence why
// we call rb_gc_mark here.
rb_gc_mark((VALUE)X509_STORE_get_ex_data(store, store_ex_verify_cb_idx));
VALUE verify_cb =
(VALUE)X509_STORE_get_ex_data(store, store_ex_verify_cb_idx);
rb_gc_mark_movable(verify_cb);
}

static void
Expand All @@ -128,12 +127,26 @@ ossl_x509store_free(void *ptr)
X509_STORE_free(ptr);
}

static void
ossl_x509store_compact(void *ptr)
{
X509_STORE *store = ptr;
VALUE verify_cb =
(VALUE)X509_STORE_get_ex_data(store, store_ex_verify_cb_idx);
if (verify_cb) {
(void)X509_STORE_set_ex_data(store, store_ex_verify_cb_idx,
(void *)rb_gc_location(verify_cb));
}
}

static const rb_data_type_t ossl_x509store_type = {
"OpenSSL/X509/STORE",
{
ossl_x509store_mark, ossl_x509store_free,
.wrap_struct_name = "OpenSSL/X509/STORE",
.function = {
.dmark = ossl_x509store_mark,
.dfree = ossl_x509store_free,
.dcompact = ossl_x509store_compact,
},
0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
.flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};

/*
Expand Down Expand Up @@ -570,10 +583,9 @@ static void
ossl_x509stctx_mark(void *ptr)
{
X509_STORE_CTX *ctx = ptr;
// Note: this reference is stored as @verify_callback so we don't need to mark it.
// However we do need to ensure GC compaction won't move it, hence why
// we call rb_gc_mark here.
rb_gc_mark((VALUE)X509_STORE_CTX_get_ex_data(ctx, stctx_ex_verify_cb_idx));
VALUE verify_cb =
(VALUE)X509_STORE_CTX_get_ex_data(ctx, stctx_ex_verify_cb_idx);
rb_gc_mark_movable(verify_cb);
}

static void
Expand All @@ -585,12 +597,26 @@ ossl_x509stctx_free(void *ptr)
X509_STORE_CTX_free(ctx);
}

static void
ossl_x509stctx_compact(void *ptr)
{
X509_STORE_CTX *ctx = ptr;
VALUE verify_cb =
(VALUE)X509_STORE_CTX_get_ex_data(ctx, stctx_ex_verify_cb_idx);
if (verify_cb) {
(void)X509_STORE_CTX_set_ex_data(ctx, stctx_ex_verify_cb_idx,
(void *)rb_gc_location(verify_cb));
}
}

static const rb_data_type_t ossl_x509stctx_type = {
"OpenSSL/X509/STORE_CTX",
{
ossl_x509stctx_mark, ossl_x509stctx_free,
.wrap_struct_name = "OpenSSL/X509/STORE_CTX",
.function = {
.dmark = ossl_x509stctx_mark,
.dfree = ossl_x509stctx_free,
.dcompact = ossl_x509stctx_compact,
},
0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
.flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};

static VALUE
Expand Down
2 changes: 1 addition & 1 deletion lib/openssl/buffering.rb
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ def initialize(*)
super
@eof = false
@rbuffer = Buffer.new
@sync = @io.sync
@sync = to_io.respond_to?(:sync) ? to_io.sync : true
end

#
Expand Down
16 changes: 0 additions & 16 deletions lib/openssl/ssl.rb
Original file line number Diff line number Diff line change
Expand Up @@ -336,10 +336,6 @@ class SSLSocket

attr_reader :hostname

# The underlying IO object.
attr_reader :io
alias :to_io :io

# The SSLContext object used in this connection.
attr_reader :context

Expand Down Expand Up @@ -428,18 +424,6 @@ def using_anon_cipher?
ctx.ciphers.include?(cipher)
end

def client_cert_cb
@context.client_cert_cb
end

def session_new_cb
@context.session_new_cb
end

def session_get_cb
@context.session_get_cb
end

class << self

# call-seq:
Expand Down
Loading
Loading