mirror of
https://github.com/ruby/ruby.git
synced 2025-08-28 07:26:00 +02:00

Import Ruby/OpenSSL 2.1.0.beta1. The full commit log since v2.0.5 (imported by r59567) can be found at: https://github.com/ruby/openssl/compare/v2.0.5...v2.1.0.beta1 ---------------------------------------------------------------- Antonio Terceiro (1): test/test_ssl: explicitly accept TLS 1.1 in corresponding test Colby Swandale (1): document using secure protocol to fetch git master in Bundler Colton Jenkins (1): Add fips_mode_get to return fips_mode Kazuki Yamaguchi (85): Start preparing for 2.1.0 Remove support for OpenSSL 0.9.8 and 1.0.0 bn: refine tests bn: implement unary {plus,minus} operators for OpenSSL::BN bn: implement OpenSSL::BN#negative? Don't define main() when built with --enable-debug test: let OpenSSL::TestCase include OpenSSL::TestUtils test: prepare test PKey instances on demand Add OpenSSL.print_mem_leaks Enable OSSL_MDEBUG on CI builds ssl: move default DH parameters from OpenSSL::PKey::DH Make exceptions with the same format regardless of OpenSSL.debug ssl: show reason of 'certificate verify error' in exception message ssl: remove OpenSSL::ExtConfig::TLS_DH_anon_WITH_AES_256_GCM_SHA384 ssl: do not confuse different ex_data index registries ssl: assume SSL/SSL_CTX always have a valid reference to the Ruby object Fix RDoc markup ssl: suppress compiler warning ext/openssl/deprecation.rb: remove broken-apple-openssl extconf.rb: print informative message if OpenSSL can't be found Rakefile: compile the extension before test kdf: introduce OpenSSL::KDF module ossl.h: add NUM2UINT64T() macro kdf: add scrypt Expand rb_define_copy_func() macro Expand FPTR_TO_FD() macro Remove SafeGet*() macros cipher: rename GetCipherPtr() to ossl_evp_get_cipherbyname() digest: rename GetDigestPtr() to ossl_evp_get_digestbyname() Add ossl_str_new(), an exception-safe rb_str_new() bio: simplify ossl_membio2str() using ossl_str_new() Remove unused functions and macros Drop support for LibreSSL 2.3 ocsp: add OpenSSL::OCSP::Request#signed? asn1: infinite length -> indefinite length asn1: rearrange tests ssl: remove a needless NULL check in SSL::SSLContext#ciphers ssl: return nil in SSL::SSLSocket#cipher if session is not started asn1: remove an unnecessary function prototype asn1: require tag information when instantiating generic type asn1: initialize 'unused_bits' attribute of BitString with 0 asn1: check for illegal 'unused_bits' value of BitString asn1: disallow NULL to be passed to asn1time_to_time() asn1: avoid truncating OID in OpenSSL::ASN1::ObjectId#oid asn1: allow constructed encoding with definite length form asn1: prohibit indefinite length form for primitive encoding asn1: allow tag number to be >= 32 for universal tag class asn1: use ossl_asn1_tag() asn1: clean up OpenSSL::ASN1::Constructive#to_der asn1: harmonize OpenSSL::ASN1::*#to_der asn1: prevent EOC octets from being in the middle of the content asn1: do not treat EOC octets as part of content octets x509name: add 'loc' and 'set' kwargs to OpenSSL::X509::Name#add_entry ssl: do not call session_remove_cb during GC Backport "Merge branch 'topic/test-memory-leak'" to maint cipher: update the documentation for Cipher#auth_tag= Rakefile: let sync:to_ruby know about test/openssl/fixtures test: fix formatting test/utils: remove OpenSSL::TestUtils.silent test/utils: add SSLTestCase#tls12_supported? test/utils: have start_server yield only the port number test/utils: do not set ecdh_curves in start_server test/utils: let server_loop close socket test/utils: improve error handling in start_server test/utils: add OpenSSL::TestUtils.openssl? and .libressl? test/utils: do not use DSA certificates in SSL tests test/test_ssl: remove test_invalid_shutdown_by_gc test/test_ssl: move test_multibyte_read_write to test_pair test/test_ssl_session: rearrange tests test/test_pair, test/test_ssl: fix for TLS 1.3 ssl: remove useless call to rb_thread_wait_fd() ssl: fix NPN support ssl: mark OpenSSL::SSL::SSLContext::DEFAULT_{1024,2048} as private ssl: use 2048-bit group in the default tmp_dh_cb ssl: ensure that SSL option flags are non-negative ssl: update OpenSSL::SSL::OP_* flags ssl: prefer TLS_method() over SSLv23_method() ssl: add SSLContext#min_version= and #max_version= ssl: rework SSLContext#ssl_version= test/test_x509name: change script encoding to ASCII-8BIT x509name: refactor OpenSSL::X509::Name#to_s x509name: add OpenSSL::X509::Name#to_utf8 x509name: add OpenSSL::X509::Name#inspect x509name: update regexp in OpenSSL::X509::Name.parse Ruby/OpenSSL 2.1.0.beta1 Marcus Stollsteimer (1): Fix rdoc for core Integer class nobu (4): [DOC] {read,write}_nonblock with exception: false [DOC] keyword argument _exception_ [DOC] mark up literals Revert r57690 except for read_nonblock git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@59734 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
474 lines
12 KiB
Ruby
474 lines
12 KiB
Ruby
# frozen_string_literal: false
|
|
=begin
|
|
= Ruby-space definitions that completes C-space funcs for Config
|
|
|
|
= Info
|
|
Copyright (C) 2010 Hiroshi Nakamura <nahi@ruby-lang.org>
|
|
|
|
= Licence
|
|
This program is licensed under the same licence as Ruby.
|
|
(See the file 'LICENCE'.)
|
|
|
|
=end
|
|
|
|
require 'stringio'
|
|
|
|
module OpenSSL
|
|
##
|
|
# = OpenSSL::Config
|
|
#
|
|
# Configuration for the openssl library.
|
|
#
|
|
# Many system's installation of openssl library will depend on your system
|
|
# configuration. See the value of OpenSSL::Config::DEFAULT_CONFIG_FILE for
|
|
# the location of the file for your host.
|
|
#
|
|
# See also http://www.openssl.org/docs/apps/config.html
|
|
class Config
|
|
include Enumerable
|
|
|
|
class << self
|
|
|
|
##
|
|
# Parses a given _string_ as a blob that contains configuration for
|
|
# OpenSSL.
|
|
#
|
|
# If the source of the IO is a file, then consider using #parse_config.
|
|
def parse(string)
|
|
c = new()
|
|
parse_config(StringIO.new(string)).each do |section, hash|
|
|
c[section] = hash
|
|
end
|
|
c
|
|
end
|
|
|
|
##
|
|
# load is an alias to ::new
|
|
alias load new
|
|
|
|
##
|
|
# Parses the configuration data read from _io_, see also #parse.
|
|
#
|
|
# Raises a ConfigError on invalid configuration data.
|
|
def parse_config(io)
|
|
begin
|
|
parse_config_lines(io)
|
|
rescue ConfigError => e
|
|
e.message.replace("error in line #{io.lineno}: " + e.message)
|
|
raise
|
|
end
|
|
end
|
|
|
|
def get_key_string(data, section, key) # :nodoc:
|
|
if v = data[section] && data[section][key]
|
|
return v
|
|
elsif section == 'ENV'
|
|
if v = ENV[key]
|
|
return v
|
|
end
|
|
end
|
|
if v = data['default'] && data['default'][key]
|
|
return v
|
|
end
|
|
end
|
|
|
|
private
|
|
|
|
def parse_config_lines(io)
|
|
section = 'default'
|
|
data = {section => {}}
|
|
while definition = get_definition(io)
|
|
definition = clear_comments(definition)
|
|
next if definition.empty?
|
|
if definition[0] == ?[
|
|
if /\[([^\]]*)\]/ =~ definition
|
|
section = $1.strip
|
|
data[section] ||= {}
|
|
else
|
|
raise ConfigError, "missing close square bracket"
|
|
end
|
|
else
|
|
if /\A([^:\s]*)(?:::([^:\s]*))?\s*=(.*)\z/ =~ definition
|
|
if $2
|
|
section = $1
|
|
key = $2
|
|
else
|
|
key = $1
|
|
end
|
|
value = unescape_value(data, section, $3)
|
|
(data[section] ||= {})[key] = value.strip
|
|
else
|
|
raise ConfigError, "missing equal sign"
|
|
end
|
|
end
|
|
end
|
|
data
|
|
end
|
|
|
|
# escape with backslash
|
|
QUOTE_REGEXP_SQ = /\A([^'\\]*(?:\\.[^'\\]*)*)'/
|
|
# escape with backslash and doubled dq
|
|
QUOTE_REGEXP_DQ = /\A([^"\\]*(?:""[^"\\]*|\\.[^"\\]*)*)"/
|
|
# escaped char map
|
|
ESCAPE_MAP = {
|
|
"r" => "\r",
|
|
"n" => "\n",
|
|
"b" => "\b",
|
|
"t" => "\t",
|
|
}
|
|
|
|
def unescape_value(data, section, value)
|
|
scanned = []
|
|
while m = value.match(/['"\\$]/)
|
|
scanned << m.pre_match
|
|
c = m[0]
|
|
value = m.post_match
|
|
case c
|
|
when "'"
|
|
if m = value.match(QUOTE_REGEXP_SQ)
|
|
scanned << m[1].gsub(/\\(.)/, '\\1')
|
|
value = m.post_match
|
|
else
|
|
break
|
|
end
|
|
when '"'
|
|
if m = value.match(QUOTE_REGEXP_DQ)
|
|
scanned << m[1].gsub(/""/, '').gsub(/\\(.)/, '\\1')
|
|
value = m.post_match
|
|
else
|
|
break
|
|
end
|
|
when "\\"
|
|
c = value.slice!(0, 1)
|
|
scanned << (ESCAPE_MAP[c] || c)
|
|
when "$"
|
|
ref, value = extract_reference(value)
|
|
refsec = section
|
|
if ref.index('::')
|
|
refsec, ref = ref.split('::', 2)
|
|
end
|
|
if v = get_key_string(data, refsec, ref)
|
|
scanned << v
|
|
else
|
|
raise ConfigError, "variable has no value"
|
|
end
|
|
else
|
|
raise 'must not reaced'
|
|
end
|
|
end
|
|
scanned << value
|
|
scanned.join
|
|
end
|
|
|
|
def extract_reference(value)
|
|
rest = ''
|
|
if m = value.match(/\(([^)]*)\)|\{([^}]*)\}/)
|
|
value = m[1] || m[2]
|
|
rest = m.post_match
|
|
elsif [?(, ?{].include?(value[0])
|
|
raise ConfigError, "no close brace"
|
|
end
|
|
if m = value.match(/[a-zA-Z0-9_]*(?:::[a-zA-Z0-9_]*)?/)
|
|
return m[0], m.post_match + rest
|
|
else
|
|
raise
|
|
end
|
|
end
|
|
|
|
def clear_comments(line)
|
|
# FCOMMENT
|
|
if m = line.match(/\A([\t\n\f ]*);.*\z/)
|
|
return m[1]
|
|
end
|
|
# COMMENT
|
|
scanned = []
|
|
while m = line.match(/[#'"\\]/)
|
|
scanned << m.pre_match
|
|
c = m[0]
|
|
line = m.post_match
|
|
case c
|
|
when '#'
|
|
line = nil
|
|
break
|
|
when "'", '"'
|
|
regexp = (c == "'") ? QUOTE_REGEXP_SQ : QUOTE_REGEXP_DQ
|
|
scanned << c
|
|
if m = line.match(regexp)
|
|
scanned << m[0]
|
|
line = m.post_match
|
|
else
|
|
scanned << line
|
|
line = nil
|
|
break
|
|
end
|
|
when "\\"
|
|
scanned << c
|
|
scanned << line.slice!(0, 1)
|
|
else
|
|
raise 'must not reaced'
|
|
end
|
|
end
|
|
scanned << line
|
|
scanned.join
|
|
end
|
|
|
|
def get_definition(io)
|
|
if line = get_line(io)
|
|
while /[^\\]\\\z/ =~ line
|
|
if extra = get_line(io)
|
|
line += extra
|
|
else
|
|
break
|
|
end
|
|
end
|
|
return line.strip
|
|
end
|
|
end
|
|
|
|
def get_line(io)
|
|
if line = io.gets
|
|
line.gsub(/[\r\n]*/, '')
|
|
end
|
|
end
|
|
end
|
|
|
|
##
|
|
# Creates an instance of OpenSSL's configuration class.
|
|
#
|
|
# This can be used in contexts like OpenSSL::X509::ExtensionFactory.config=
|
|
#
|
|
# If the optional _filename_ parameter is provided, then it is read in and
|
|
# parsed via #parse_config.
|
|
#
|
|
# This can raise IO exceptions based on the access, or availability of the
|
|
# file. A ConfigError exception may be raised depending on the validity of
|
|
# the data being configured.
|
|
#
|
|
def initialize(filename = nil)
|
|
@data = {}
|
|
if filename
|
|
File.open(filename.to_s) do |file|
|
|
Config.parse_config(file).each do |section, hash|
|
|
self[section] = hash
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
##
|
|
# Gets the value of _key_ from the given _section_
|
|
#
|
|
# Given the following configurating file being loaded:
|
|
#
|
|
# config = OpenSSL::Config.load('foo.cnf')
|
|
# #=> #<OpenSSL::Config sections=["default"]>
|
|
# puts config.to_s
|
|
# #=> [ default ]
|
|
# # foo=bar
|
|
#
|
|
# You can get a specific value from the config if you know the _section_
|
|
# and _key_ like so:
|
|
#
|
|
# config.get_value('default','foo')
|
|
# #=> "bar"
|
|
#
|
|
def get_value(section, key)
|
|
if section.nil?
|
|
raise TypeError.new('nil not allowed')
|
|
end
|
|
section = 'default' if section.empty?
|
|
get_key_string(section, key)
|
|
end
|
|
|
|
##
|
|
#
|
|
# *Deprecated*
|
|
#
|
|
# Use #get_value instead
|
|
def value(arg1, arg2 = nil) # :nodoc:
|
|
warn('Config#value is deprecated; use Config#get_value')
|
|
if arg2.nil?
|
|
section, key = 'default', arg1
|
|
else
|
|
section, key = arg1, arg2
|
|
end
|
|
section ||= 'default'
|
|
section = 'default' if section.empty?
|
|
get_key_string(section, key)
|
|
end
|
|
|
|
##
|
|
# Set the target _key_ with a given _value_ under a specific _section_.
|
|
#
|
|
# Given the following configurating file being loaded:
|
|
#
|
|
# config = OpenSSL::Config.load('foo.cnf')
|
|
# #=> #<OpenSSL::Config sections=["default"]>
|
|
# puts config.to_s
|
|
# #=> [ default ]
|
|
# # foo=bar
|
|
#
|
|
# You can set the value of _foo_ under the _default_ section to a new
|
|
# value:
|
|
#
|
|
# config.add_value('default', 'foo', 'buzz')
|
|
# #=> "buzz"
|
|
# puts config.to_s
|
|
# #=> [ default ]
|
|
# # foo=buzz
|
|
#
|
|
def add_value(section, key, value)
|
|
check_modify
|
|
(@data[section] ||= {})[key] = value
|
|
end
|
|
|
|
##
|
|
# Get a specific _section_ from the current configuration
|
|
#
|
|
# Given the following configurating file being loaded:
|
|
#
|
|
# config = OpenSSL::Config.load('foo.cnf')
|
|
# #=> #<OpenSSL::Config sections=["default"]>
|
|
# puts config.to_s
|
|
# #=> [ default ]
|
|
# # foo=bar
|
|
#
|
|
# You can get a hash of the specific section like so:
|
|
#
|
|
# config['default']
|
|
# #=> {"foo"=>"bar"}
|
|
#
|
|
def [](section)
|
|
@data[section] || {}
|
|
end
|
|
|
|
##
|
|
# Deprecated
|
|
#
|
|
# Use #[] instead
|
|
def section(name) # :nodoc:
|
|
warn('Config#section is deprecated; use Config#[]')
|
|
@data[name] || {}
|
|
end
|
|
|
|
##
|
|
# Sets a specific _section_ name with a Hash _pairs_.
|
|
#
|
|
# Given the following configuration being created:
|
|
#
|
|
# config = OpenSSL::Config.new
|
|
# #=> #<OpenSSL::Config sections=[]>
|
|
# config['default'] = {"foo"=>"bar","baz"=>"buz"}
|
|
# #=> {"foo"=>"bar", "baz"=>"buz"}
|
|
# puts config.to_s
|
|
# #=> [ default ]
|
|
# # foo=bar
|
|
# # baz=buz
|
|
#
|
|
# It's important to note that this will essentially merge any of the keys
|
|
# in _pairs_ with the existing _section_. For example:
|
|
#
|
|
# config['default']
|
|
# #=> {"foo"=>"bar", "baz"=>"buz"}
|
|
# config['default'] = {"foo" => "changed"}
|
|
# #=> {"foo"=>"changed"}
|
|
# config['default']
|
|
# #=> {"foo"=>"changed", "baz"=>"buz"}
|
|
#
|
|
def []=(section, pairs)
|
|
check_modify
|
|
@data[section] ||= {}
|
|
pairs.each do |key, value|
|
|
self.add_value(section, key, value)
|
|
end
|
|
end
|
|
|
|
##
|
|
# Get the names of all sections in the current configuration
|
|
def sections
|
|
@data.keys
|
|
end
|
|
|
|
##
|
|
# Get the parsable form of the current configuration
|
|
#
|
|
# Given the following configuration being created:
|
|
#
|
|
# config = OpenSSL::Config.new
|
|
# #=> #<OpenSSL::Config sections=[]>
|
|
# config['default'] = {"foo"=>"bar","baz"=>"buz"}
|
|
# #=> {"foo"=>"bar", "baz"=>"buz"}
|
|
# puts config.to_s
|
|
# #=> [ default ]
|
|
# # foo=bar
|
|
# # baz=buz
|
|
#
|
|
# You can parse get the serialized configuration using #to_s and then parse
|
|
# it later:
|
|
#
|
|
# serialized_config = config.to_s
|
|
# # much later...
|
|
# new_config = OpenSSL::Config.parse(serialized_config)
|
|
# #=> #<OpenSSL::Config sections=["default"]>
|
|
# puts new_config
|
|
# #=> [ default ]
|
|
# foo=bar
|
|
# baz=buz
|
|
#
|
|
def to_s
|
|
ary = []
|
|
@data.keys.sort.each do |section|
|
|
ary << "[ #{section} ]\n"
|
|
@data[section].keys.each do |key|
|
|
ary << "#{key}=#{@data[section][key]}\n"
|
|
end
|
|
ary << "\n"
|
|
end
|
|
ary.join
|
|
end
|
|
|
|
##
|
|
# For a block.
|
|
#
|
|
# Receive the section and its pairs for the current configuration.
|
|
#
|
|
# config.each do |section, key, value|
|
|
# # ...
|
|
# end
|
|
#
|
|
def each
|
|
@data.each do |section, hash|
|
|
hash.each do |key, value|
|
|
yield [section, key, value]
|
|
end
|
|
end
|
|
end
|
|
|
|
##
|
|
# String representation of this configuration object, including the class
|
|
# name and its sections.
|
|
def inspect
|
|
"#<#{self.class.name} sections=#{sections.inspect}>"
|
|
end
|
|
|
|
protected
|
|
|
|
def data # :nodoc:
|
|
@data
|
|
end
|
|
|
|
private
|
|
|
|
def initialize_copy(other)
|
|
@data = other.data.dup
|
|
end
|
|
|
|
def check_modify
|
|
raise TypeError.new("Insecure: can't modify OpenSSL config") if frozen?
|
|
end
|
|
|
|
def get_key_string(section, key)
|
|
Config.get_key_string(@data, section, key)
|
|
end
|
|
end
|
|
end
|