[ruby/openssl] config: revert to C implementation of OpenSSL::Config
Revert OpenSSL::Config to using the OpenSSL API and remove our own parser implementation for the config file syntax. OpenSSL::Config now wraps a CONF object. Accessor methods deal with the object directly rather than Ruby-level internal state. This work is based on the old C code we used before 2010. https://github.com/ruby/openssl/commit/c891e0ea89
This commit is contained in:
parent
7c13d2b3cc
commit
22aeb6373e
Notes:
git
2021-03-16 20:38:56 +09:00
@ -15,7 +15,6 @@ require 'openssl.so'
|
||||
require_relative 'openssl/bn'
|
||||
require_relative 'openssl/pkey'
|
||||
require_relative 'openssl/cipher'
|
||||
require_relative 'openssl/config'
|
||||
require_relative 'openssl/digest'
|
||||
require_relative 'openssl/hmac'
|
||||
require_relative 'openssl/x509'
|
||||
|
@ -1,411 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
=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.set_section(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 => error
|
||||
raise ConfigError, "error in line #{io.lineno}: " + error.message
|
||||
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 => {}}
|
||||
io_stack = [io]
|
||||
while definition = get_definition(io_stack)
|
||||
definition = clear_comments(definition)
|
||||
next if definition.empty?
|
||||
case definition
|
||||
when /\A\[/
|
||||
if /\[([^\]]*)\]/ =~ definition
|
||||
section = $1.strip
|
||||
data[section] ||= {}
|
||||
else
|
||||
raise ConfigError, "missing close square bracket"
|
||||
end
|
||||
when /\A\.include (\s*=\s*)?(.+)\z/
|
||||
path = $2
|
||||
if File.directory?(path)
|
||||
files = Dir.glob(File.join(path, "*.{cnf,conf}"), File::FNM_EXTGLOB)
|
||||
else
|
||||
files = [path]
|
||||
end
|
||||
|
||||
files.each do |filename|
|
||||
begin
|
||||
io_stack << StringIO.new(File.read(filename))
|
||||
rescue
|
||||
raise ConfigError, "could not include file '%s'" % filename
|
||||
end
|
||||
end
|
||||
when /\A([^:\s]*)(?:::([^:\s]*))?\s*=(.*)\z/
|
||||
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
|
||||
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_stack)
|
||||
if line = get_line(io_stack)
|
||||
while /[^\\]\\\z/ =~ line
|
||||
if extra = get_line(io_stack)
|
||||
line += extra
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
return line.strip
|
||||
end
|
||||
end
|
||||
|
||||
def get_line(io_stack)
|
||||
while io = io_stack.last
|
||||
if line = io.gets
|
||||
return line.gsub(/[\r\n]*/, '')
|
||||
end
|
||||
io_stack.pop
|
||||
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|
|
||||
set_section(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
|
||||
|
||||
##
|
||||
# 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
|
||||
|
||||
def set_section(section, pairs) # :nodoc:
|
||||
hash = @data[section] ||= {}
|
||||
pairs.each do |key, value|
|
||||
hash[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 get_key_string(section, key)
|
||||
Config.get_key_string(@data, section, key)
|
||||
end
|
||||
end
|
||||
end
|
@ -9,21 +9,46 @@
|
||||
*/
|
||||
#include "ossl.h"
|
||||
|
||||
static VALUE cConfig, eConfigError;
|
||||
|
||||
/*
|
||||
* Classes
|
||||
*/
|
||||
VALUE cConfig;
|
||||
/* Document-class: OpenSSL::ConfigError
|
||||
*
|
||||
* General error for openssl library configuration files. Including formatting,
|
||||
* parsing errors, etc.
|
||||
*/
|
||||
VALUE eConfigError;
|
||||
static void
|
||||
nconf_free(void *conf)
|
||||
{
|
||||
NCONF_free(conf);
|
||||
}
|
||||
|
||||
/*
|
||||
* Public
|
||||
*/
|
||||
static const rb_data_type_t ossl_config_type = {
|
||||
"OpenSSL/CONF",
|
||||
{
|
||||
0, nconf_free,
|
||||
},
|
||||
0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
|
||||
};
|
||||
|
||||
static CONF *
|
||||
GetConfig(VALUE obj)
|
||||
{
|
||||
CONF *conf;
|
||||
|
||||
TypedData_Get_Struct(obj, CONF, &ossl_config_type, conf);
|
||||
if (!conf)
|
||||
rb_raise(rb_eRuntimeError, "CONF is not initialized");
|
||||
return conf;
|
||||
}
|
||||
|
||||
static VALUE
|
||||
config_s_alloc(VALUE klass)
|
||||
{
|
||||
VALUE obj;
|
||||
CONF *conf;
|
||||
|
||||
obj = TypedData_Wrap_Struct(klass, &ossl_config_type, 0);
|
||||
conf = NCONF_new(NULL);
|
||||
if (!conf)
|
||||
ossl_raise(eConfigError, "NCONF_new");
|
||||
RTYPEDDATA_DATA(obj) = conf;
|
||||
return obj;
|
||||
}
|
||||
|
||||
/*
|
||||
* DupConfigPtr is a public C-level function for getting OpenSSL CONF struct
|
||||
@ -60,30 +85,412 @@ DupConfigPtr(VALUE obj)
|
||||
return conf;
|
||||
}
|
||||
|
||||
/* Document-const: DEFAULT_CONFIG_FILE
|
||||
*
|
||||
* The default system configuration file for openssl
|
||||
*/
|
||||
|
||||
static void
|
||||
config_load_bio(CONF *conf, BIO *bio)
|
||||
{
|
||||
long eline = -1;
|
||||
|
||||
if (!NCONF_load_bio(conf, bio, &eline)) {
|
||||
BIO_free(bio);
|
||||
if (eline <= 0)
|
||||
ossl_raise(eConfigError, "wrong config format");
|
||||
else
|
||||
ossl_raise(eConfigError, "error in line %d", eline);
|
||||
}
|
||||
BIO_free(bio);
|
||||
|
||||
/*
|
||||
* Clear the error queue even if it is parsed successfully.
|
||||
* Particularly, when the .include directive refers to a non-existent file,
|
||||
* it is only reported in the error queue.
|
||||
*/
|
||||
ossl_clear_error();
|
||||
}
|
||||
|
||||
/*
|
||||
* INIT
|
||||
* call-seq:
|
||||
* Config.parse(string) -> OpenSSL::Config
|
||||
*
|
||||
* Parses a given _string_ as a blob that contains configuration for OpenSSL.
|
||||
*/
|
||||
static VALUE
|
||||
config_s_parse(VALUE klass, VALUE str)
|
||||
{
|
||||
VALUE obj = config_s_alloc(klass);
|
||||
CONF *conf = GetConfig(obj);
|
||||
BIO *bio;
|
||||
|
||||
bio = ossl_obj2bio(&str);
|
||||
config_load_bio(conf, bio); /* Consumes BIO */
|
||||
return obj;
|
||||
}
|
||||
|
||||
static VALUE config_get_sections(VALUE self);
|
||||
static VALUE config_get_section(VALUE self, VALUE section);
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* Config.parse_config(io) -> hash
|
||||
*
|
||||
* Parses the configuration data read from _io_ and returns the whole content
|
||||
* as a Hash.
|
||||
*/
|
||||
static VALUE
|
||||
config_s_parse_config(VALUE klass, VALUE io)
|
||||
{
|
||||
VALUE obj, sections, ret;
|
||||
long i;
|
||||
|
||||
obj = config_s_parse(klass, io);
|
||||
sections = config_get_sections(obj);
|
||||
ret = rb_hash_new();
|
||||
for (i = 0; i < RARRAY_LEN(sections); i++) {
|
||||
VALUE section = rb_ary_entry(sections, i);
|
||||
rb_hash_aset(ret, section, config_get_section(obj, section));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* Config.new(filename) -> OpenSSL::Config
|
||||
*
|
||||
* Creates an instance of OpenSSL::Config from the content of the file
|
||||
* specified by _filename_.
|
||||
*
|
||||
* This can be used in contexts like OpenSSL::X509::ExtensionFactory.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.
|
||||
*/
|
||||
static VALUE
|
||||
config_initialize(int argc, VALUE *argv, VALUE self)
|
||||
{
|
||||
CONF *conf = GetConfig(self);
|
||||
VALUE filename;
|
||||
|
||||
/* 0-arguments call has no use-case, but is kept for compatibility */
|
||||
rb_scan_args(argc, argv, "01", &filename);
|
||||
rb_check_frozen(self);
|
||||
if (!NIL_P(filename)) {
|
||||
BIO *bio = BIO_new_file(StringValueCStr(filename), "rb");
|
||||
if (!bio)
|
||||
ossl_raise(eConfigError, "BIO_new_file");
|
||||
config_load_bio(conf, bio); /* Consumes BIO */
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
static VALUE
|
||||
config_initialize_copy(VALUE self, VALUE other)
|
||||
{
|
||||
CONF *conf = GetConfig(self);
|
||||
VALUE str;
|
||||
BIO *bio;
|
||||
|
||||
str = rb_funcall(other, rb_intern("to_s"), 0);
|
||||
rb_check_frozen(self);
|
||||
bio = ossl_obj2bio(&str);
|
||||
config_load_bio(conf, bio); /* Consumes BIO */
|
||||
return self;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* config.get_value(section, key) -> string
|
||||
*
|
||||
* 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"
|
||||
*/
|
||||
static VALUE
|
||||
config_get_value(VALUE self, VALUE section, VALUE key)
|
||||
{
|
||||
CONF *conf = GetConfig(self);
|
||||
const char *str, *sectionp;
|
||||
|
||||
StringValueCStr(section);
|
||||
StringValueCStr(key);
|
||||
/* For compatibility; NULL means "default". */
|
||||
sectionp = RSTRING_LEN(section) ? RSTRING_PTR(section) : NULL;
|
||||
str = NCONF_get_string(conf, sectionp, RSTRING_PTR(key));
|
||||
if (!str) {
|
||||
ossl_clear_error();
|
||||
return Qnil;
|
||||
}
|
||||
return rb_str_new_cstr(str);
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* config[section] -> hash
|
||||
*
|
||||
* Gets all key-value pairs in 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"}
|
||||
*
|
||||
*/
|
||||
static VALUE
|
||||
config_get_section(VALUE self, VALUE section)
|
||||
{
|
||||
CONF *conf = GetConfig(self);
|
||||
STACK_OF(CONF_VALUE) *sk;
|
||||
int i, entries;
|
||||
VALUE hash;
|
||||
|
||||
hash = rb_hash_new();
|
||||
StringValueCStr(section);
|
||||
if (!(sk = NCONF_get_section(conf, RSTRING_PTR(section)))) {
|
||||
ossl_clear_error();
|
||||
return hash;
|
||||
}
|
||||
entries = sk_CONF_VALUE_num(sk);
|
||||
for (i = 0; i < entries; i++) {
|
||||
CONF_VALUE *entry = sk_CONF_VALUE_value(sk, i);
|
||||
rb_hash_aset(hash, rb_str_new_cstr(entry->name),
|
||||
rb_str_new_cstr(entry->value));
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
static void
|
||||
get_conf_section_doall_arg(CONF_VALUE *cv, VALUE *aryp)
|
||||
{
|
||||
if (cv->name)
|
||||
return;
|
||||
rb_ary_push(*aryp, rb_str_new_cstr(cv->section));
|
||||
}
|
||||
|
||||
/* IMPLEMENT_LHASH_DOALL_ARG_CONST() requires >= OpenSSL 1.1.0 */
|
||||
static IMPLEMENT_LHASH_DOALL_ARG_FN(get_conf_section, CONF_VALUE, VALUE)
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* config.sections -> array of string
|
||||
*
|
||||
* Get the names of all sections in the current configuration.
|
||||
*/
|
||||
static VALUE
|
||||
config_get_sections(VALUE self)
|
||||
{
|
||||
CONF *conf = GetConfig(self);
|
||||
VALUE ary;
|
||||
|
||||
ary = rb_ary_new();
|
||||
lh_doall_arg((_LHASH *)conf->data, LHASH_DOALL_ARG_FN(get_conf_section),
|
||||
&ary);
|
||||
return ary;
|
||||
}
|
||||
|
||||
static void
|
||||
dump_conf_value_doall_arg(CONF_VALUE *cv, VALUE *strp)
|
||||
{
|
||||
VALUE str = *strp;
|
||||
STACK_OF(CONF_VALUE) *sk;
|
||||
int i, num;
|
||||
|
||||
if (cv->name)
|
||||
return;
|
||||
sk = (STACK_OF(CONF_VALUE) *)cv->value;
|
||||
num = sk_CONF_VALUE_num(sk);
|
||||
rb_str_cat_cstr(str, "[ ");
|
||||
rb_str_cat_cstr(str, cv->section);
|
||||
rb_str_cat_cstr(str, " ]\n");
|
||||
for (i = 0; i < num; i++){
|
||||
CONF_VALUE *v = sk_CONF_VALUE_value(sk, i);
|
||||
rb_str_cat_cstr(str, v->name ? v->name : "None");
|
||||
rb_str_cat_cstr(str, "=");
|
||||
rb_str_cat_cstr(str, v->value ? v->value : "None");
|
||||
rb_str_cat_cstr(str, "\n");
|
||||
}
|
||||
rb_str_cat_cstr(str, "\n");
|
||||
}
|
||||
|
||||
static IMPLEMENT_LHASH_DOALL_ARG_FN(dump_conf_value, CONF_VALUE, VALUE)
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* config.to_s -> string
|
||||
*
|
||||
*
|
||||
* Gets 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
|
||||
*/
|
||||
static VALUE
|
||||
config_to_s(VALUE self)
|
||||
{
|
||||
CONF *conf = GetConfig(self);
|
||||
VALUE str;
|
||||
|
||||
str = rb_str_new(NULL, 0);
|
||||
lh_doall_arg((_LHASH *)conf->data, LHASH_DOALL_ARG_FN(dump_conf_value),
|
||||
&str);
|
||||
return str;
|
||||
}
|
||||
|
||||
static void
|
||||
each_conf_value_doall_arg(CONF_VALUE *cv, void *unused)
|
||||
{
|
||||
STACK_OF(CONF_VALUE) *sk;
|
||||
VALUE section;
|
||||
int i, num;
|
||||
|
||||
if (cv->name)
|
||||
return;
|
||||
sk = (STACK_OF(CONF_VALUE) *)cv->value;
|
||||
num = sk_CONF_VALUE_num(sk);
|
||||
section = rb_str_new_cstr(cv->section);
|
||||
for (i = 0; i < num; i++){
|
||||
CONF_VALUE *v = sk_CONF_VALUE_value(sk, i);
|
||||
VALUE name = v->name ? rb_str_new_cstr(v->name) : Qnil;
|
||||
VALUE value = v->value ? rb_str_new_cstr(v->value) : Qnil;
|
||||
rb_yield(rb_ary_new3(3, section, name, value));
|
||||
}
|
||||
}
|
||||
|
||||
static IMPLEMENT_LHASH_DOALL_ARG_FN(each_conf_value, CONF_VALUE, void)
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* config.each { |section, key, value| }
|
||||
*
|
||||
* Retrieves the section and its pairs for the current configuration.
|
||||
*
|
||||
* config.each do |section, key, value|
|
||||
* # ...
|
||||
* end
|
||||
*/
|
||||
static VALUE
|
||||
config_each(VALUE self)
|
||||
{
|
||||
CONF *conf = GetConfig(self);
|
||||
|
||||
RETURN_ENUMERATOR(self, 0, 0);
|
||||
|
||||
lh_doall_arg((_LHASH *)conf->data, LHASH_DOALL_ARG_FN(each_conf_value),
|
||||
NULL);
|
||||
return self;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* config.inspect -> string
|
||||
*
|
||||
* String representation of this configuration object, including the class
|
||||
* name and its sections.
|
||||
*/
|
||||
static VALUE
|
||||
config_inspect(VALUE self)
|
||||
{
|
||||
VALUE str, ary = config_get_sections(self);
|
||||
const char *cname = rb_class2name(rb_obj_class(self));
|
||||
|
||||
str = rb_str_new_cstr("#<");
|
||||
rb_str_cat_cstr(str, cname);
|
||||
rb_str_cat_cstr(str, " sections=");
|
||||
rb_str_append(str, rb_inspect(ary));
|
||||
rb_str_cat_cstr(str, ">");
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
void
|
||||
Init_ossl_config(void)
|
||||
{
|
||||
char *default_config_file;
|
||||
char *path;
|
||||
VALUE path_str;
|
||||
|
||||
#if 0
|
||||
mOSSL = rb_define_module("OpenSSL");
|
||||
eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError);
|
||||
#endif
|
||||
|
||||
eConfigError = rb_define_class_under(mOSSL, "ConfigError", eOSSLError);
|
||||
/* Document-class: 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
|
||||
*/
|
||||
cConfig = rb_define_class_under(mOSSL, "Config", rb_cObject);
|
||||
|
||||
default_config_file = CONF_get1_default_config_file();
|
||||
rb_define_const(cConfig, "DEFAULT_CONFIG_FILE",
|
||||
rb_str_new2(default_config_file));
|
||||
OPENSSL_free(default_config_file);
|
||||
/* methods are defined by openssl/config.rb */
|
||||
/* Document-class: OpenSSL::ConfigError
|
||||
*
|
||||
* General error for openssl library configuration files. Including formatting,
|
||||
* parsing errors, etc.
|
||||
*/
|
||||
eConfigError = rb_define_class_under(mOSSL, "ConfigError", eOSSLError);
|
||||
|
||||
rb_include_module(cConfig, rb_mEnumerable);
|
||||
rb_define_singleton_method(cConfig, "parse", config_s_parse, 1);
|
||||
rb_define_singleton_method(cConfig, "parse_config", config_s_parse_config, 1);
|
||||
rb_define_alias(CLASS_OF(cConfig), "load", "new");
|
||||
rb_define_alloc_func(cConfig, config_s_alloc);
|
||||
rb_define_method(cConfig, "initialize", config_initialize, -1);
|
||||
rb_define_method(cConfig, "initialize_copy", config_initialize_copy, 1);
|
||||
rb_define_method(cConfig, "get_value", config_get_value, 2);
|
||||
rb_define_method(cConfig, "[]", config_get_section, 1);
|
||||
rb_define_method(cConfig, "sections", config_get_sections, 0);
|
||||
rb_define_method(cConfig, "to_s", config_to_s, 0);
|
||||
rb_define_method(cConfig, "each", config_each, 0);
|
||||
rb_define_method(cConfig, "inspect", config_inspect, 0);
|
||||
|
||||
/* Document-const: DEFAULT_CONFIG_FILE
|
||||
*
|
||||
* The default system configuration file for OpenSSL.
|
||||
*/
|
||||
path = CONF_get1_default_config_file();
|
||||
path_str = ossl_buf2str(path, rb_long2int(strlen(path)));
|
||||
rb_define_const(cConfig, "DEFAULT_CONFIG_FILE", path_str);
|
||||
}
|
||||
|
@ -7,13 +7,10 @@
|
||||
* This program is licensed under the same licence as Ruby.
|
||||
* (See the file 'LICENCE'.)
|
||||
*/
|
||||
#if !defined(_OSSL_CONFIG_H_)
|
||||
#define _OSSL_CONFIG_H_
|
||||
#ifndef OSSL_CONFIG_H
|
||||
#define OSSL_CONFIG_H
|
||||
|
||||
extern VALUE cConfig;
|
||||
extern VALUE eConfigError;
|
||||
|
||||
CONF* DupConfigPtr(VALUE obj);
|
||||
CONF *DupConfigPtr(VALUE obj);
|
||||
void Init_ossl_config(void);
|
||||
|
||||
#endif /* _OSSL_CONFIG_H_ */
|
||||
#endif /* OSSL_CONFIG_H */
|
||||
|
@ -150,6 +150,10 @@ __EOC__
|
||||
|
||||
# Include a file by relative path
|
||||
c1 = OpenSSL::Config.parse(include_file)
|
||||
if c1["sec-main"][".include"]
|
||||
# OpenSSL < 1.1.1 parses '.include =' as a normal assignment
|
||||
pend ".include directive is not supported"
|
||||
end
|
||||
assert_equal(["default", "sec-a", "sec-b", "sec-main"], c1.sections.sort)
|
||||
assert_equal(["file-a", "file-b", "file-main"], c1["default"].keys.sort)
|
||||
assert_equal({"a" => "123"}, c1["sec-a"])
|
||||
@ -157,9 +161,9 @@ __EOC__
|
||||
assert_equal({"main" => "123", "key_outside_section" => "value_a"}, c1["sec-main"])
|
||||
|
||||
# Relative paths are from the working directory
|
||||
assert_raise(OpenSSL::ConfigError) do
|
||||
Dir.chdir("child") { OpenSSL::Config.parse(include_file) }
|
||||
end
|
||||
# Inclusion fails, but the error is ignored silently
|
||||
c2 = Dir.chdir("child") { OpenSSL::Config.parse(include_file) }
|
||||
assert_equal(["default", "sec-main"], c2.sections.sort)
|
||||
end
|
||||
end
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user