class OpenSSL::SSL::SSLContext
An SSLContext используется для установки различных параметров, касающихся сертификатов, алгоритмов, проверки, кэширования сессий и т. д. SSLContext используется для создания SSLSocket.
Все атрибуты должны быть установлены до создания SSLSocket, так как SSLContext после этого заморозится.
Константы
- DEFAULT_2048
- METHODS
-
Список доступных методов SSL/TLS. Эта константа предоставляется только для обратной совместимости.
- METHODS_MAP
- SESSION_CACHE_BOTH
-
Сессии как клиента, так и сервера добавляются в кэш сессий
- SESSION_CACHE_CLIENT
-
Сессии клиента добавляются в кэш сессий
- SESSION_CACHE_NO_AUTO_CLEAR
-
Обычно кэш сессий проверяется на наличие истекших сессий каждые 255 подключений. Поскольку это может привести к задержке, которую нельзя контролировать, автоматическое очищение может быть отключено, и flush_sessions можно вызывать явно.
- SESSION_CACHE_NO_INTERNAL
-
Включает как SESSION_CACHE_NO_INTERNAL_LOOKUP, так и SESSION_CACHE_NO_INTERNAL_STORE.
- SESSION_CACHE_NO_INTERNAL_LOOKUP
-
Всегда выполняются внешние запросы к сессиям, даже если они находятся во внутреннем кэше.
Этот флаг не влияет на клиентов
- SESSION_CACHE_NO_INTERNAL_STORE
-
Никогда не сохранять сессии автоматически во внутреннем хранилище.
- SESSION_CACHE_OFF
-
Кэширование сессий для клиента и сервера отключено
- SESSION_CACHE_SERVER
-
Сессии сервера добавляются в кэш сессий
Атрибуты
Объект Enumerable строк. Каждая строка представляет протокол, который будет объявлен как список поддерживаемых протоколов для согласования протоколов прикладного уровня (Application-Layer Protocol Negotiation). Поддерживается в OpenSSL 1.0.2 и выше. Не оказывает влияния на стороне сервера. Если не задано явно, расширение ALPN не будет включено в рукопожатие.
Пример
ctx.alpn_protocols = ["http/1.1", "spdy/2", "h2"]
Обратный вызов, вызываемый на стороне сервера, когда серверу необходимо выбрать протокол из списка, отправленного клиентом. Поддерживается в OpenSSL 1.0.2 и выше. Обратный вызов должен возвращать протокол из тех, которые объявлены клиентом. Если ни один из них не подходит, возникновение ошибки в обратном вызове приведет к сбою рукопожатия. Отсутствие явной установки этого обратного вызова означает отсутствие поддержки расширения ALPN на сервере — любые протоколы, объявленные клиентом, будут игнорироваться.
Пример
ctx.alpn_select_cb = lambda do |protocols| # inspect the protocols and select one protocols.first end
Путь к файлу, содержащему сертификат центра сертификации (CA) в формате PEM.
Путь к каталогу, содержащему сертификаты CA в формате PEM.
Файлы ищутся по хэш-значению имени X509 субъекта.
Контекстный сертификат
Атрибуты cert, key и extra_chain_cert устарели. Рекомендуется использовать add_certificate.
Объект OpenSSL::X509::Store, используемый для проверки сертификатов.
Сертификат или массив сертификатов, которые будут отправлены клиенту.
Обратный вызов, вызываемый, когда сервер запрашивает клиентский сертификат, а сертификат не установлен.
Обратный вызов вызывается с объектом Session и должен возвращать массив, содержащий OpenSSL::X509::Certificate и OpenSSL::PKey. Возврат любого другого значения приостановит рукопожатие.
Массив дополнительных сертификатов X509, которые будут добавлены в цепочку сертификатов.
Атрибуты cert, key и extra_chain_cert устарели. Рекомендуется использовать add_certificate.
Контекстный закрытый ключ
Атрибуты cert, key и extra_chain_cert устарели. Рекомендуется использовать add_certificate.
Объект Enumerable строк. Каждая строка представляет протокол, который будет объявлен как список поддерживаемых протоколов для согласования следующего протокола (Next Protocol Negotiation). Поддерживается в OpenSSL 1.0.1 и выше. Не оказывает влияния на стороне клиента. Если не задано явно, расширение NPN не будет отправлено сервером в рукопожатии.
Пример
ctx.npn_protocols = ["http/1.1", "spdy/2"]
Обратный вызов, вызываемый на стороне клиента, когда клиенту необходимо выбрать протокол из списка, отправленного сервером. Поддерживается в OpenSSL 1.0.1 и выше. Клиент ДОЛЖЕН выбрать протокол из тех, которые объявлены сервером. Если ни один из них не подходит, возникновение ошибки в обратном вызове приведет к сбою рукопожатия. Отсутствие явной установки этого обратного вызова означает отсутствие поддержки расширения NPN на клиенте — любые протоколы, объявленные сервером, будут игнорироваться.
Пример
ctx.npn_select_cb = lambda do |protocols| # inspect the protocols and select one protocols.first end
Обратный вызов, вызываемый при инициации нового рукопожатия. Может использоваться для полного отключения повторного согласования.
Обратный вызов вызывается с активным объектом SSLSocket. Возвращаемое значение обратного вызова не имеет значения, обычный возврат указывает на «утверждение» повторного согласования и продолжит процесс. Чтобы запретить повторное согласование и отменить процесс, в обратном вызове можно вызвать ошибку.
Отключение повторного согласования клиента
При работе сервера часто желательно полностью отключить повторное согласование клиента. Вы можете использовать обратный вызов следующим образом, чтобы реализовать эту функцию:
num_handshakes = 0
ctx.renegotiation_cb = lambda do |ssl|
num_handshakes += 1
raise RuntimeError.new("Client renegotiation disabled") if num_handshakes > 1
end
Обратный вызов, вызываемый во время подключения для различения нескольких имен серверов.
Обратный вызов вызывается с объектом SSLSocket и именем сервера. Обратный вызов должен возвращать объект SSLContext для имени сервера или nil.
Устанавливает контекст, в котором сеанс может быть повторно использован. Это позволяет различать сеансы для нескольких приложений, например, по имени.
Обратный вызов, вызываемый при согласовании нового сеанса.
Обратный вызов вызывается с объектом SSLSocket. Если false возвращается, сеанс будет удален из внутреннего кэша.
Обратный вызов, вызываемый при удалении сеанса из внутреннего кэша.
Обратный вызов вызывается с объектом SSLContext и объектом Session.
ВАЖНОЕ ЗАМЕЧАНИЕ: В настоящее время невозможно безопасно использовать это в многопоточном приложении. Обратный вызов вызывается внутри глобального блокировщика, и он может случайным образом вызывать взаимную блокировку при переключении потоков Ruby.
Максимальный срок службы сеанса в секундах.
Максимальный срок службы сеанса в секундах.
Обратный вызов, вызываемый, когда требуются параметры DH.
Обратный вызов вызывается с объектом Session для обмена ключами, флагом, указывающим на использование экспортного шифра, и требуемой длиной ключа.
Обратный вызов должен возвращать экземпляр OpenSSL::PKey::DH правильной длины ключа.
Обратный вызов, вызываемый, когда требуются параметры ECDH.
Обратный вызов вызывается с сессией для обмена ключами, флагом, указывающим на использование шифра экспорта, и требуемой длиной ключа.
Обратный вызов устарел. Он не работает с последними версиями OpenSSL. Используйте вместо этого #ecdh_curves=.
Обратный вызов для дополнительной проверки сертификата. Обратный вызов вызывается для каждого сертификата в цепочке.
Обратный вызов вызывается с двумя значениями. preverify_ok указывает, прошла ли проверка (true) или нет (false). store_context — это OpenSSL::X509::StoreContext, содержащий контекст, используемый для проверки сертификата.
Если обратный вызов возвращает false, проверка цепочки немедленно прерывается, и отправляется предупреждение bad_certificate.
Количество сертификатов УЦ, которые необходимо пройти при проверке цепочки сертификатов.
Выполнять ли проверку того, что сертификат сервера действителен для имени хоста.
Для работы этого параметра необходимо установить #verify_mode в VERIFY_PEER и указать имя хоста сервера с помощью OpenSSL::SSL::SSLSocket#hostname=.
Режим проверки сессии.
Допустимые режимы — VERIFY_NONE, VERIFY_PEER, VERIFY_CLIENT_ONCE, VERIFY_FAIL_IF_NO_PEER_CERT, и определены в OpenSSL::SSL.
По умолчанию режим — VERIFY_NONE, при котором проверка вообще не выполняется.
См. SSL_CTX_set_verify(3) для получения подробностей.
Методы публичного класса
# File ext/openssl/lib/openssl/ssl.rb, line 119 def initialize(version = nil) self.options |= OpenSSL::SSL::OP_ALL self.ssl_version = version if version end
Создает новый контекст SSL.
Если аргумент предоставлен, вызывается ssl_version= со значением. Обратите внимание, что этот вариант устарел. Новые приложения должны использовать min_version= и max_version= по мере необходимости.
Общедоступные методы экземпляров
static VALUE
ossl_sslctx_add_certificate(int argc, VALUE *argv, VALUE self)
{
VALUE cert, key, extra_chain_ary;
SSL_CTX *ctx;
X509 *x509;
STACK_OF(X509) *extra_chain = NULL;
EVP_PKEY *pkey, *pub_pkey;
GetSSLCTX(self, ctx);
rb_scan_args(argc, argv, "21", &cert, &key, &extra_chain_ary);
rb_check_frozen(self);
x509 = GetX509CertPtr(cert);
pkey = GetPrivPKeyPtr(key);
/*
* The reference counter is bumped, and decremented immediately.
* X509_get0_pubkey() is only available in OpenSSL >= 1.1.0.
*/
pub_pkey = X509_get_pubkey(x509);
EVP_PKEY_free(pub_pkey);
if (!pub_pkey)
rb_raise(rb_eArgError, "certificate does not contain public key");
if (EVP_PKEY_cmp(pub_pkey, pkey) != 1)
rb_raise(rb_eArgError, "public key mismatch");
if (argc >= 3)
extra_chain = ossl_x509_ary2sk(extra_chain_ary);
if (!SSL_CTX_use_certificate(ctx, x509)) {
sk_X509_pop_free(extra_chain, X509_free);
ossl_raise(eSSLError, "SSL_CTX_use_certificate");
}
if (!SSL_CTX_use_PrivateKey(ctx, pkey)) {
sk_X509_pop_free(extra_chain, X509_free);
ossl_raise(eSSLError, "SSL_CTX_use_PrivateKey");
}
if (extra_chain) {
#if OPENSSL_VERSION_NUMBER >= 0x10002000 && !defined(LIBRESSL_VERSION_NUMBER)
if (!SSL_CTX_set0_chain(ctx, extra_chain)) {
sk_X509_pop_free(extra_chain, X509_free);
ossl_raise(eSSLError, "SSL_CTX_set0_chain");
}
#else
STACK_OF(X509) *orig_extra_chain;
X509 *x509_tmp;
/* First, clear the existing chain */
SSL_CTX_get_extra_chain_certs(ctx, &orig_extra_chain);
if (orig_extra_chain && sk_X509_num(orig_extra_chain)) {
rb_warning("SSL_CTX_set0_chain() is not available; " \
"clearing previously set certificate chain");
SSL_CTX_clear_extra_chain_certs(ctx);
}
while ((x509_tmp = sk_X509_shift(extra_chain))) {
/* Transfers ownership */
if (!SSL_CTX_add_extra_chain_cert(ctx, x509_tmp)) {
X509_free(x509_tmp);
sk_X509_pop_free(extra_chain, X509_free);
ossl_raise(eSSLError, "SSL_CTX_add_extra_chain_cert");
}
}
sk_X509_free(extra_chain);
#endif
}
return self;
} Добавляет сертификат в контекст. pkey должен быть соответствующим закрытым ключом к certificate.
Несколько сертификатов с разными типами открытых ключей могут быть добавлены многократным вызовом этого метода, и OpenSSL выберет наиболее подходящий сертификат во время рукопожатия.
cert=, key= и extra_chain_cert= — это устаревшие методы доступа для установки сертификата и внутренне вызывают этот метод.
Параметры
- certificate
-
Сертификат. Экземпляр OpenSSL::X509::Certificate.
- pkey
-
Закрытый ключ для certificate. Экземпляр OpenSSL::PKey::PKey.
- extra_certs
-
Необязательно. Массив OpenSSL::X509::Certificate. При отправке цепочки сертификатов сертификаты, указанные в этом массиве, отправляются после certificate в указанном порядке.
Пример
rsa_cert = OpenSSL::X509::Certificate.new(...) rsa_pkey = OpenSSL::PKey.read(...) ca_intermediate_cert = OpenSSL::X509::Certificate.new(...) ctx.add_certificate(rsa_cert, rsa_pkey, [ca_intermediate_cert]) ecdsa_cert = ... ecdsa_pkey = ... another_ca_cert = ... ctx.add_certificate(ecdsa_cert, ecdsa_pkey, [another_ca_cert])
Примечание
OpenSSL до версии 1.0.2 мог обрабатывать только одну дополнительную цепочку для всех типов ключей. Вызов этого метода отбрасывает ранее установленную цепочку.
static VALUE
ossl_sslctx_get_ciphers(VALUE self)
{
SSL_CTX *ctx;
STACK_OF(SSL_CIPHER) *ciphers;
const SSL_CIPHER *cipher;
VALUE ary;
int i, num;
GetSSLCTX(self, ctx);
ciphers = SSL_CTX_get_ciphers(ctx);
if (!ciphers)
return rb_ary_new();
num = sk_SSL_CIPHER_num(ciphers);
ary = rb_ary_new2(num);
for(i = 0; i < num; i++){
cipher = sk_SSL_CIPHER_value(ciphers, i);
rb_ary_push(ary, ossl_ssl_cipher_to_ary(cipher));
}
return ary;
} Список наборов шифрования, настроенных для этого контекста.
static VALUE
ossl_sslctx_set_ciphers(VALUE self, VALUE v)
{
SSL_CTX *ctx;
VALUE str, elem;
int i;
rb_check_frozen(self);
if (NIL_P(v))
return v;
else if (RB_TYPE_P(v, T_ARRAY)) {
str = rb_str_new(0, 0);
for (i = 0; i < RARRAY_LEN(v); i++) {
elem = rb_ary_entry(v, i);
if (RB_TYPE_P(elem, T_ARRAY)) elem = rb_ary_entry(elem, 0);
elem = rb_String(elem);
rb_str_append(str, elem);
if (i < RARRAY_LEN(v)-1) rb_str_cat2(str, ":");
}
} else {
str = v;
StringValue(str);
}
GetSSLCTX(self, ctx);
if (!SSL_CTX_set_cipher_list(ctx, StringValueCStr(str))) {
ossl_raise(eSSLError, "SSL_CTX_set_cipher_list");
}
return v;
} Устанавливает список доступных наборов шифрования для этого контекста. Обратите внимание, что в контексте сервера некоторые наборы шифрования требуют соответствующих сертификатов. Например, набор шифрования RSA может быть выбран только при наличии сертификата RSA.
static VALUE
ossl_sslctx_set_ecdh_curves(VALUE self, VALUE arg)
{
SSL_CTX *ctx;
rb_check_frozen(self);
GetSSLCTX(self, ctx);
StringValueCStr(arg);
#if defined(HAVE_SSL_CTX_SET1_CURVES_LIST)
if (!SSL_CTX_set1_curves_list(ctx, RSTRING_PTR(arg)))
ossl_raise(eSSLError, NULL);
#else
/* OpenSSL does not have SSL_CTX_set1_curves_list()... Fallback to
* SSL_CTX_set_tmp_ecdh(). So only the first curve is used. */
{
VALUE curve, splitted;
EC_KEY *ec;
int nid;
splitted = rb_str_split(arg, ":");
if (!RARRAY_LEN(splitted))
ossl_raise(eSSLError, "invalid input format");
curve = RARRAY_AREF(splitted, 0);
StringValueCStr(curve);
/* SSL_CTX_set1_curves_list() accepts NIST names */
nid = EC_curve_nist2nid(RSTRING_PTR(curve));
if (nid == NID_undef)
nid = OBJ_txt2nid(RSTRING_PTR(curve));
if (nid == NID_undef)
ossl_raise(eSSLError, "unknown curve name");
ec = EC_KEY_new_by_curve_name(nid);
if (!ec)
ossl_raise(eSSLError, NULL);
EC_KEY_set_asn1_flag(ec, OPENSSL_EC_NAMED_CURVE);
if (!SSL_CTX_set_tmp_ecdh(ctx, ec)) {
EC_KEY_free(ec);
ossl_raise(eSSLError, "SSL_CTX_set_tmp_ecdh");
}
EC_KEY_free(ec);
# if defined(HAVE_SSL_CTX_SET_ECDH_AUTO)
/* tmp_ecdh and ecdh_auto conflict. tmp_ecdh is ignored when ecdh_auto
* is enabled. So disable ecdh_auto. */
if (!SSL_CTX_set_ecdh_auto(ctx, 0))
ossl_raise(eSSLError, "SSL_CTX_set_ecdh_auto");
# endif
}
#endif
return arg;
} Устанавливает список «поддерживаемых эллиптических кривых» для этого контекста.
Для клиента TLS список используется непосредственно в расширении Supported Elliptic Curves. Для сервера список используется OpenSSL для определения набора общих кривых. OpenSSL выберет наиболее подходящую из них.
Обратите внимание, что это работает по-разному со старой версией OpenSSL (<= 1.0.1). Может быть установлена только одна кривая, и это не влияет на клиентов TLS.
Пример
ctx1 = OpenSSL::SSL::SSLContext.new
ctx1.ecdh_curves = "X25519:P-256:P-224"
svr = OpenSSL::SSL::SSLServer.new(tcp_svr, ctx1)
Thread.new { svr.accept }
ctx2 = OpenSSL::SSL::SSLContext.new
ctx2.ecdh_curves = "P-256"
cli = OpenSSL::SSL::SSLSocket.new(tcp_sock, ctx2)
cli.connect
p cli.tmp_key.group.curve_name
# => "prime256v1" (is an alias for NIST P-256)
static VALUE
ossl_sslctx_enable_fallback_scsv(VALUE self)
{
SSL_CTX *ctx;
GetSSLCTX(self, ctx);
SSL_CTX_set_mode(ctx, SSL_MODE_SEND_FALLBACK_SCSV);
return Qnil;
} Активирует TLS_FALLBACK_SCSV для данного контекста. См. RFC 7507.
static VALUE
ossl_sslctx_flush_sessions(int argc, VALUE *argv, VALUE self)
{
VALUE arg1;
SSL_CTX *ctx;
time_t tm = 0;
rb_scan_args(argc, argv, "01", &arg1);
GetSSLCTX(self, ctx);
if (NIL_P(arg1)) {
tm = time(0);
} else if (rb_obj_is_instance_of(arg1, rb_cTime)) {
tm = NUM2LONG(rb_funcall(arg1, rb_intern("to_i"), 0));
} else {
ossl_raise(rb_eArgError, "arg must be Time or nil");
}
SSL_CTX_flush_sessions(ctx, (long)tm);
return self;
} Удаляет сессии в кэше, которые истекли к time.
# File ext/openssl/lib/openssl/ssl.rb, line 180 def max_version=(version) set_minmax_proto_version(@min_proto_version ||= nil, version) @max_proto_version = version end
Устанавливает верхнюю границу поддерживаемой версии протокола SSL/TLS. См. min_version= для возможных значений.
# File ext/openssl/lib/openssl/ssl.rb, line 168 def min_version=(version) set_minmax_proto_version(version, @max_proto_version ||= nil) @min_proto_version = version end
Устанавливает нижнюю границу поддерживаемой версии протокола SSL/TLS. Версия может быть указана целочисленной константой с именем OpenSSL::SSL::*_VERSION, символом или nil, что означает «любая версия».
Будьте осторожны, чтобы не перезаписывать опции OpenSSL::SSL::OP_NO_{SSL,TLS}v* с помощью options= после вызова min_version= или max_version=.
Пример
ctx = OpenSSL::SSL::SSLContext.new ctx.min_version = OpenSSL::SSL::TLS1_1_VERSION ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION sock = OpenSSL::SSL::SSLSocket.new(tcp_sock, ctx) sock.connect # Initiates a connection using either TLS 1.1 or TLS 1.2
static VALUE
ossl_sslctx_get_options(VALUE self)
{
SSL_CTX *ctx;
GetSSLCTX(self, ctx);
/*
* Do explicit cast because SSL_CTX_get_options() returned (signed) long in
* OpenSSL before 1.1.0.
*/
return ULONG2NUM((unsigned long)SSL_CTX_get_options(ctx));
} Получает различные параметры OpenSSL.
static VALUE
ossl_sslctx_set_options(VALUE self, VALUE options)
{
SSL_CTX *ctx;
rb_check_frozen(self);
GetSSLCTX(self, ctx);
SSL_CTX_clear_options(ctx, SSL_CTX_get_options(ctx));
if (NIL_P(options)) {
SSL_CTX_set_options(ctx, SSL_OP_ALL);
} else {
SSL_CTX_set_options(ctx, NUM2ULONG(options));
}
return self;
} Устанавливает различные параметры OpenSSL.
static VALUE
ossl_sslctx_get_security_level(VALUE self)
{
SSL_CTX *ctx;
GetSSLCTX(self, ctx);
#if defined(HAVE_SSL_CTX_GET_SECURITY_LEVEL)
return INT2NUM(SSL_CTX_get_security_level(ctx));
#else
(void)ctx;
return INT2FIX(0);
#endif
} Возвращает уровень безопасности для контекста.
См. также #security_level=.
static VALUE
ossl_sslctx_set_security_level(VALUE self, VALUE value)
{
SSL_CTX *ctx;
rb_check_frozen(self);
GetSSLCTX(self, ctx);
#if defined(HAVE_SSL_CTX_GET_SECURITY_LEVEL)
SSL_CTX_set_security_level(ctx, NUM2INT(value));
#else
(void)ctx;
if (NUM2INT(value) != 0)
ossl_raise(rb_eNotImpError, "setting security level to other than 0 is "
"not supported in this version of OpenSSL");
#endif
return value;
} Устанавливает уровень безопасности для контекста. OpenSSL ограничивает параметры в соответствии с уровнем. «Параметры» включают: наборы шифрования, кривые, размеры ключей, алгоритмы подписи сертификатов, версии протокола и так далее. Например, уровень 1 отклоняет параметры, предлагающие менее 80 бит безопасности, такие как наборы шифрования, использующие MD5 для MAC или ключи RSA короче 1024 бит.
Обратите внимание, что попытки установить такие параметры с недостаточной безопасностью также блокируются. Сначала необходимо снизить уровень.
Эта функция не поддерживается в OpenSSL < 1.1.0, и установка уровня, отличного от 0, вызовет NotImplementedError. Уровень 0 означает, что всё разрешено, такой же результат, как и в предыдущих версиях OpenSSL.
См. руководство manpage для SSL_CTX_set_security_level(3) для получения подробностей.
static VALUE
ossl_sslctx_session_add(VALUE self, VALUE arg)
{
SSL_CTX *ctx;
SSL_SESSION *sess;
GetSSLCTX(self, ctx);
GetSSLSession(arg, sess);
return SSL_CTX_add_session(ctx, sess) == 1 ? Qtrue : Qfalse;
} Добавляет session в кэш сессий.
static VALUE
ossl_sslctx_get_session_cache_mode(VALUE self)
{
SSL_CTX *ctx;
GetSSLCTX(self, ctx);
return LONG2NUM(SSL_CTX_get_session_cache_mode(ctx));
} Текущий режим кэша сессий.
static VALUE
ossl_sslctx_set_session_cache_mode(VALUE self, VALUE arg)
{
SSL_CTX *ctx;
GetSSLCTX(self, ctx);
SSL_CTX_set_session_cache_mode(ctx, NUM2LONG(arg));
return arg;
} Устанавливает режим кэширования сессий SSL. Объедините биты SESSION_CACHE_* констант для установки. См. SSL_CTX_set_session_cache_mode(3) для получения подробностей.
static VALUE
ossl_sslctx_get_session_cache_size(VALUE self)
{
SSL_CTX *ctx;
GetSSLCTX(self, ctx);
return LONG2NUM(SSL_CTX_sess_get_cache_size(ctx));
} Возвращает текущий размер кэша сессий. Ноль используется для представления неограниченного размера кэша.
static VALUE
ossl_sslctx_set_session_cache_size(VALUE self, VALUE arg)
{
SSL_CTX *ctx;
GetSSLCTX(self, ctx);
SSL_CTX_sess_set_cache_size(ctx, NUM2LONG(arg));
return arg;
} Устанавливает размер кэша сеансов. Возвращает предыдущий допустимый размер кэша сеансов. Ноль используется для обозначения неограниченного размера кэша сеансов.
static VALUE
ossl_sslctx_get_session_cache_stats(VALUE self)
{
SSL_CTX *ctx;
VALUE hash;
GetSSLCTX(self, ctx);
hash = rb_hash_new();
rb_hash_aset(hash, ID2SYM(rb_intern("cache_num")), LONG2NUM(SSL_CTX_sess_number(ctx)));
rb_hash_aset(hash, ID2SYM(rb_intern("connect")), LONG2NUM(SSL_CTX_sess_connect(ctx)));
rb_hash_aset(hash, ID2SYM(rb_intern("connect_good")), LONG2NUM(SSL_CTX_sess_connect_good(ctx)));
rb_hash_aset(hash, ID2SYM(rb_intern("connect_renegotiate")), LONG2NUM(SSL_CTX_sess_connect_renegotiate(ctx)));
rb_hash_aset(hash, ID2SYM(rb_intern("accept")), LONG2NUM(SSL_CTX_sess_accept(ctx)));
rb_hash_aset(hash, ID2SYM(rb_intern("accept_good")), LONG2NUM(SSL_CTX_sess_accept_good(ctx)));
rb_hash_aset(hash, ID2SYM(rb_intern("accept_renegotiate")), LONG2NUM(SSL_CTX_sess_accept_renegotiate(ctx)));
rb_hash_aset(hash, ID2SYM(rb_intern("cache_hits")), LONG2NUM(SSL_CTX_sess_hits(ctx)));
rb_hash_aset(hash, ID2SYM(rb_intern("cb_hits")), LONG2NUM(SSL_CTX_sess_cb_hits(ctx)));
rb_hash_aset(hash, ID2SYM(rb_intern("cache_misses")), LONG2NUM(SSL_CTX_sess_misses(ctx)));
rb_hash_aset(hash, ID2SYM(rb_intern("cache_full")), LONG2NUM(SSL_CTX_sess_cache_full(ctx)));
rb_hash_aset(hash, ID2SYM(rb_intern("timeouts")), LONG2NUM(SSL_CTX_sess_timeouts(ctx)));
return hash;
} Возвращает Hash, содержащий следующие ключи:
- :accept
-
Количество начатых рукопожатий SSL/TLS в серверном режиме
- :accept_good
-
Количество установленных SSL/TLS сеансов в серверном режиме
- :accept_renegotiate
-
Количество начатых повторных согласований в серверном режиме
- :cache_full
-
Количество сеансов, которые были удалены из-за переполнения кэша
- :cache_hits
-
Количество успешно повторно использованных соединений
- :cache_misses
-
Количество сеансов, предложенных клиентами, которые не были найдены в кэше
- :cache_num
-
Количество сеансов во внутреннем кэше сеансов
- :cb_hits
-
Количество сеансов, извлечённых из внешнего кэша в серверном режиме
- :connect
-
Количество начатых рукопожатий SSL/TLS в клиентском режиме
- :connect_good
-
Количество установленных SSL/TLS сеансов в клиентском режиме
- :connect_renegotiate
-
Количество начатых повторных согласований в клиентском режиме
- :timeouts
-
Количество сеансов, предложенных клиентами, которые были найдены в кэше, но истекли из-за таймаутов
static VALUE
ossl_sslctx_session_remove(VALUE self, VALUE arg)
{
SSL_CTX *ctx;
SSL_SESSION *sess;
GetSSLCTX(self, ctx);
GetSSLSession(arg, sess);
return SSL_CTX_remove_session(ctx, sess) == 1 ? Qtrue : Qfalse;
} Удаляет session из кэша сеансов.
# File ext/openssl/lib/openssl/ssl.rb, line 136
def set_params(params={})
params = DEFAULT_PARAMS.merge(params)
self.options = params.delete(:options) # set before min_version/max_version
params.each{|name, value| self.__send__("#{name}=", value) }
if self.verify_mode != OpenSSL::SSL::VERIFY_NONE
unless self.ca_file or self.ca_path or self.cert_store
self.cert_store = DEFAULT_CERT_STORE
end
end
return params
end Устанавливает более разумные значения по умолчанию, оптимизированные для использования с протоколами типа HTTP.
Если задан Hash params, параметры перезаписываются им. Ключи в params должны быть методами присваивания в SSLContext.
Если #verify_mode не равен VERIFY_NONE, и #ca_file, #ca_path и #cert_store не заданы, используется системный сертификат по умолчанию.
static VALUE
ossl_sslctx_setup(VALUE self)
{
SSL_CTX *ctx;
X509 *cert = NULL, *client_ca = NULL;
EVP_PKEY *key = NULL;
char *ca_path = NULL, *ca_file = NULL;
int verify_mode;
long i;
VALUE val;
if(OBJ_FROZEN(self)) return Qnil;
GetSSLCTX(self, ctx);
#if !defined(OPENSSL_NO_DH)
SSL_CTX_set_tmp_dh_callback(ctx, ossl_tmp_dh_callback);
#endif
#if !defined(OPENSSL_NO_EC)
/* We added SSLContext#tmp_ecdh_callback= in Ruby 2.3.0,
* but SSL_CTX_set_tmp_ecdh_callback() was removed in OpenSSL 1.1.0. */
if (RTEST(rb_attr_get(self, id_i_tmp_ecdh_callback))) {
# if defined(HAVE_SSL_CTX_SET_TMP_ECDH_CALLBACK)
rb_warn("#tmp_ecdh_callback= is deprecated; use #ecdh_curves= instead");
SSL_CTX_set_tmp_ecdh_callback(ctx, ossl_tmp_ecdh_callback);
# if defined(HAVE_SSL_CTX_SET_ECDH_AUTO)
/* tmp_ecdh_callback and ecdh_auto conflict; OpenSSL ignores
* tmp_ecdh_callback. So disable ecdh_auto. */
if (!SSL_CTX_set_ecdh_auto(ctx, 0))
ossl_raise(eSSLError, "SSL_CTX_set_ecdh_auto");
# endif
# else
ossl_raise(eSSLError, "OpenSSL does not support tmp_ecdh_callback; "
"use #ecdh_curves= instead");
# endif
}
#endif /* OPENSSL_NO_EC */
val = rb_attr_get(self, id_i_cert_store);
if (!NIL_P(val)) {
X509_STORE *store = GetX509StorePtr(val); /* NO NEED TO DUP */
SSL_CTX_set_cert_store(ctx, store);
#if !defined(HAVE_X509_STORE_UP_REF)
/*
* WORKAROUND:
* X509_STORE can count references, but
* X509_STORE_free() doesn't care it.
* So we won't increment it but mark it by ex_data.
*/
SSL_CTX_set_ex_data(ctx, ossl_sslctx_ex_store_p, ctx);
#else /* Fixed in OpenSSL 1.0.2; bff9ce4db38b (master), 5b4b9ce976fc (1.0.2) */
X509_STORE_up_ref(store);
#endif
}
val = rb_attr_get(self, id_i_extra_chain_cert);
if(!NIL_P(val)){
rb_block_call(val, rb_intern("each"), 0, 0, ossl_sslctx_add_extra_chain_cert_i, self);
}
/* private key may be bundled in certificate file. */
val = rb_attr_get(self, id_i_cert);
cert = NIL_P(val) ? NULL : GetX509CertPtr(val); /* NO DUP NEEDED */
val = rb_attr_get(self, id_i_key);
key = NIL_P(val) ? NULL : GetPrivPKeyPtr(val); /* NO DUP NEEDED */
if (cert && key) {
if (!SSL_CTX_use_certificate(ctx, cert)) {
/* Adds a ref => Safe to FREE */
ossl_raise(eSSLError, "SSL_CTX_use_certificate");
}
if (!SSL_CTX_use_PrivateKey(ctx, key)) {
/* Adds a ref => Safe to FREE */
ossl_raise(eSSLError, "SSL_CTX_use_PrivateKey");
}
if (!SSL_CTX_check_private_key(ctx)) {
ossl_raise(eSSLError, "SSL_CTX_check_private_key");
}
}
val = rb_attr_get(self, id_i_client_ca);
if(!NIL_P(val)){
if (RB_TYPE_P(val, T_ARRAY)) {
for(i = 0; i < RARRAY_LEN(val); i++){
client_ca = GetX509CertPtr(RARRAY_AREF(val, i));
if (!SSL_CTX_add_client_CA(ctx, client_ca)){
/* Copies X509_NAME => FREE it. */
ossl_raise(eSSLError, "SSL_CTX_add_client_CA");
}
}
}
else{
client_ca = GetX509CertPtr(val); /* NO DUP NEEDED. */
if (!SSL_CTX_add_client_CA(ctx, client_ca)){
/* Copies X509_NAME => FREE it. */
ossl_raise(eSSLError, "SSL_CTX_add_client_CA");
}
}
}
val = rb_attr_get(self, id_i_ca_file);
ca_file = NIL_P(val) ? NULL : StringValueCStr(val);
val = rb_attr_get(self, id_i_ca_path);
ca_path = NIL_P(val) ? NULL : StringValueCStr(val);
if(ca_file || ca_path){
if (!SSL_CTX_load_verify_locations(ctx, ca_file, ca_path))
rb_warning("can't set verify locations");
}
val = rb_attr_get(self, id_i_verify_mode);
verify_mode = NIL_P(val) ? SSL_VERIFY_NONE : NUM2INT(val);
SSL_CTX_set_verify(ctx, verify_mode, ossl_ssl_verify_callback);
if (RTEST(rb_attr_get(self, id_i_client_cert_cb)))
SSL_CTX_set_client_cert_cb(ctx, ossl_client_cert_cb);
val = rb_attr_get(self, id_i_timeout);
if(!NIL_P(val)) SSL_CTX_set_timeout(ctx, NUM2LONG(val));
val = rb_attr_get(self, id_i_verify_depth);
if(!NIL_P(val)) SSL_CTX_set_verify_depth(ctx, NUM2INT(val));
#ifndef OPENSSL_NO_NEXTPROTONEG
val = rb_attr_get(self, id_i_npn_protocols);
if (!NIL_P(val)) {
VALUE encoded = ssl_encode_npn_protocols(val);
rb_ivar_set(self, id_npn_protocols_encoded, encoded);
SSL_CTX_set_next_protos_advertised_cb(ctx, ssl_npn_advertise_cb, (void *)encoded);
OSSL_Debug("SSL NPN advertise callback added");
}
if (RTEST(rb_attr_get(self, id_i_npn_select_cb))) {
SSL_CTX_set_next_proto_select_cb(ctx, ssl_npn_select_cb, (void *) self);
OSSL_Debug("SSL NPN select callback added");
}
#endif
#ifdef HAVE_SSL_CTX_SET_ALPN_SELECT_CB
val = rb_attr_get(self, id_i_alpn_protocols);
if (!NIL_P(val)) {
VALUE rprotos = ssl_encode_npn_protocols(val);
/* returns 0 on success */
if (SSL_CTX_set_alpn_protos(ctx, (unsigned char *)RSTRING_PTR(rprotos),
RSTRING_LENINT(rprotos)))
ossl_raise(eSSLError, "SSL_CTX_set_alpn_protos");
OSSL_Debug("SSL ALPN values added");
}
if (RTEST(rb_attr_get(self, id_i_alpn_select_cb))) {
SSL_CTX_set_alpn_select_cb(ctx, ssl_alpn_select_cb, (void *) self);
OSSL_Debug("SSL ALPN select callback added");
}
#endif
rb_obj_freeze(self);
val = rb_attr_get(self, id_i_session_id_context);
if (!NIL_P(val)){
StringValue(val);
if (!SSL_CTX_set_session_id_context(ctx, (unsigned char *)RSTRING_PTR(val),
RSTRING_LENINT(val))){
ossl_raise(eSSLError, "SSL_CTX_set_session_id_context");
}
}
if (RTEST(rb_attr_get(self, id_i_session_get_cb))) {
SSL_CTX_sess_set_get_cb(ctx, ossl_sslctx_session_get_cb);
OSSL_Debug("SSL SESSION get callback added");
}
if (RTEST(rb_attr_get(self, id_i_session_new_cb))) {
SSL_CTX_sess_set_new_cb(ctx, ossl_sslctx_session_new_cb);
OSSL_Debug("SSL SESSION new callback added");
}
if (RTEST(rb_attr_get(self, id_i_session_remove_cb))) {
SSL_CTX_sess_set_remove_cb(ctx, ossl_sslctx_session_remove_cb);
OSSL_Debug("SSL SESSION remove callback added");
}
val = rb_attr_get(self, id_i_servername_cb);
if (!NIL_P(val)) {
SSL_CTX_set_tlsext_servername_callback(ctx, ssl_servername_cb);
OSSL_Debug("SSL TLSEXT servername callback added");
}
return Qtrue;
} Этот метод вызывается автоматически при создании нового SSLSocket. Однако он не потокобезопасен и должен вызываться перед созданием объектов SSLSocket в многопоточной программе.
# File ext/openssl/lib/openssl/ssl.rb, line 199
def ssl_version=(meth)
meth = meth.to_s if meth.is_a?(Symbol)
if /(?<type>_client|_server)\z/ =~ meth
meth = $`
if $VERBOSE
warn "#{caller(1, 1)[0]}: method type #{type.inspect} is ignored"
end
end
version = METHODS_MAP[meth.intern] or
raise ArgumentError, "unknown SSL method `%s'" % meth
set_minmax_proto_version(version, version)
@min_proto_version = @max_proto_version = version
end Устанавливает версию протокола SSL/TLS для контекста. Это принудительно заставляет соединения использовать только указанную версию протокола. Это устаревший метод и предоставлен только для обратной совместимости. Используйте min_version= и max_version= вместо него.
История
Как подсказывает название, ранее это вызывало функцию SSL_CTX_set_ssl_version(), которая устанавливала метод SSL, используемый для соединений, созданных из контекста. Начиная с Ruby/OpenSSL 2.1, этот метод-аксессор реализован для вызова min_version= и max_version= вместо этого.
Закрытые методы экземпляра
static VALUE
ossl_sslctx_set_minmax_proto_version(VALUE self, VALUE min_v, VALUE max_v)
{
SSL_CTX *ctx;
int min, max;
GetSSLCTX(self, ctx);
min = parse_proto_version(min_v);
max = parse_proto_version(max_v);
#ifdef HAVE_SSL_CTX_SET_MIN_PROTO_VERSION
if (!SSL_CTX_set_min_proto_version(ctx, min))
ossl_raise(eSSLError, "SSL_CTX_set_min_proto_version");
if (!SSL_CTX_set_max_proto_version(ctx, max))
ossl_raise(eSSLError, "SSL_CTX_set_max_proto_version");
#else
{
unsigned long sum = 0, opts = 0;
int i;
static const struct {
int ver;
unsigned long opts;
} options_map[] = {
{ SSL2_VERSION, SSL_OP_NO_SSLv2 },
{ SSL3_VERSION, SSL_OP_NO_SSLv3 },
{ TLS1_VERSION, SSL_OP_NO_TLSv1 },
{ TLS1_1_VERSION, SSL_OP_NO_TLSv1_1 },
{ TLS1_2_VERSION, SSL_OP_NO_TLSv1_2 },
# if defined(TLS1_3_VERSION)
{ TLS1_3_VERSION, SSL_OP_NO_TLSv1_3 },
# endif
};
for (i = 0; i < numberof(options_map); i++) {
sum |= options_map[i].opts;
if (min && min > options_map[i].ver || max && max < options_map[i].ver)
opts |= options_map[i].opts;
}
SSL_CTX_clear_options(ctx, sum);
SSL_CTX_set_options(ctx, opts);
}
#endif
return Qnil;
} Устанавливает минимальную и максимальную поддерживаемые версии протоколов. См. min_version= и max_version=.
Ruby Core © 1993–2017 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.