diff options
Diffstat (limited to 'ext/openssl/ossl_config.c')
| -rw-r--r-- | ext/openssl/ossl_config.c | 624 |
1 files changed, 333 insertions, 291 deletions
diff --git a/ext/openssl/ossl_config.c b/ext/openssl/ossl_config.c index 7e6f696d33..274875a978 100644 --- a/ext/openssl/ossl_config.c +++ b/ext/openssl/ossl_config.c @@ -1,414 +1,456 @@ /* - * $Id$ * 'OpenSSL for Ruby' project * Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz> * All rights reserved. */ /* - * This program is licenced under the same licence as Ruby. - * (See the file 'LICENCE'.) + * This program is licensed under the same licence as Ruby. + * (See the file 'COPYING'.) */ #include "ossl.h" -#define WrapConfig(klass, obj, conf) do { \ - if (!conf) { \ - ossl_raise(rb_eRuntimeError, "Config wasn't intitialized!"); \ - } \ - obj = Data_Wrap_Struct(klass, 0, NCONF_free, conf); \ -} while (0) -#define GetConfig(obj, conf) do { \ - Data_Get_Struct(obj, CONF, conf); \ - if (!conf) { \ - ossl_raise(rb_eRuntimeError, "Config wasn't intitialized!"); \ - } \ -} while (0) -#define SafeGetConfig(obj, conf) do { \ - OSSL_Check_Kind(obj, cConfig); \ - GetConfig(obj, conf); \ -} while(0); +static VALUE cConfig, eConfigError; -/* - * Classes - */ -VALUE cConfig; -VALUE eConfigError; - -/* - * Public - */ +static void +nconf_free(void *conf) +{ + NCONF_free(conf); +} -static CONF *parse_config(VALUE, CONF*); +static const rb_data_type_t ossl_config_type = { + "OpenSSL/CONF", + { + 0, nconf_free, + }, + 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FROZEN_SHAREABLE, +}; CONF * -GetConfigPtr(VALUE obj) +GetConfig(VALUE obj) { CONF *conf; - SafeGetConfig(obj, conf); - + TypedData_Get_Struct(obj, CONF, &ossl_config_type, conf); + if (!conf) + rb_raise(rb_eRuntimeError, "CONF is not initialized"); return conf; } -CONF * -DupConfigPtr(VALUE obj) +static VALUE +config_s_alloc(VALUE klass) { - VALUE str; - - OSSL_Check_Kind(obj, cConfig); - str = rb_funcall(obj, rb_intern("to_s"), 0); + VALUE obj; + CONF *conf; - return parse_config(str, NULL); + 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; } -/* - * Private - */ -static CONF * -parse_config(VALUE str, CONF *dst) +static void +config_load_bio(CONF *conf, BIO *bio) { - CONF *conf; - BIO *bio; long eline = -1; - bio = ossl_obj2bio(str); - conf = dst ? dst : NCONF_new(NULL); - if(!conf){ - BIO_free(bio); - ossl_raise(eConfigError, NULL); - } - if(!NCONF_load_bio(conf, bio, &eline)){ - BIO_free(bio); - if(!dst) NCONF_free(conf); - if (eline <= 0) ossl_raise(eConfigError, "wrong config format"); - else ossl_raise(eConfigError, "error in line %d", eline); - ossl_raise(eConfigError, NULL); + 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 %ld", eline); } BIO_free(bio); - return conf; -} - -static VALUE -ossl_config_s_parse(VALUE klass, VALUE str) -{ - CONF *conf; - VALUE obj; - - conf = parse_config(str, NULL); - WrapConfig(klass, obj, conf); - - return obj; + /* + * 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(); } +/* + * call-seq: + * Config.parse(string) -> OpenSSL::Config + * + * Parses a given _string_ as a blob that contains configuration for OpenSSL. + */ static VALUE -ossl_config_s_alloc(VALUE klass) +config_s_parse(VALUE klass, VALUE str) { - CONF *conf; - VALUE obj; - - if(!(conf = NCONF_new(NULL))) - ossl_raise(eConfigError, NULL); - WrapConfig(klass, obj, conf); + VALUE obj = config_s_alloc(klass); + CONF *conf = GetConfig(obj); + BIO *bio; + bio = ossl_obj2bio(&str); + config_load_bio(conf, bio); /* Consumes BIO */ + rb_obj_freeze(obj); return obj; } -static VALUE -ossl_config_copy(VALUE self, VALUE other) -{ - VALUE str; - CONF *conf; - - GetConfig(other, conf); - str = rb_funcall(self, rb_intern("to_s"), 0); - parse_config(str, conf); - - return self; -} +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 -ossl_config_initialize(int argc, VALUE *argv, VALUE self) +config_s_parse_config(VALUE klass, VALUE io) { - CONF *conf; - long eline = -1; - char *filename; - VALUE path; - - GetConfig(self, conf); - rb_scan_args(argc, argv, "01", &path); - if(!NIL_P(path)){ - SafeStringValue(path); - filename = StringValuePtr(path); - if (!NCONF_load(conf, filename, &eline)){ - if (eline <= 0) - ossl_raise(eConfigError, "wrong config file %s", filename); - else - ossl_raise(eConfigError, "error in %s:%d", filename, eline); - } + 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)); } -#ifdef OSSL_NO_CONF_API - else rb_raise(rb_eArgError, "wrong number of arguments(0 for 1)"); -#else - else _CONF_new_data(conf); -#endif - - return self; + 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 -ossl_config_add_value(VALUE self, VALUE section, VALUE name, VALUE value) +config_initialize(int argc, VALUE *argv, VALUE self) { -#ifdef OSSL_NO_CONF_API - rb_notimplement(); -#else - CONF *conf; - CONF_VALUE *sv, *cv; - - GetConfig(self, conf); - StringValue(section); - StringValue(name); - StringValue(value); - if(!(sv = _CONF_get_section(conf, RSTRING(section)->ptr))){ - if(!(sv = _CONF_new_section(conf, RSTRING(section)->ptr))){ - ossl_raise(eConfigError, NULL); - } - } - if(!(cv = OPENSSL_malloc(sizeof(CONF_VALUE)))){ - ossl_raise(eConfigError, NULL); + 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 */ } - cv->name = BUF_strdup(RSTRING(name)->ptr); - cv->value = BUF_strdup(RSTRING(value)->ptr); - if(!cv->name || !cv->value || !_CONF_add_string(conf, sv, cv)){ - OPENSSL_free(cv->name); - OPENSSL_free(cv->value); - OPENSSL_free(cv); - ossl_raise(eConfigError, "_CONF_add_string failure"); - } - - return value; -#endif + rb_obj_freeze(self); + return self; } static VALUE -ossl_config_get_value(VALUE self, VALUE section, VALUE name) +config_initialize_copy(VALUE self, VALUE other) { - CONF *conf; - char *str; - - GetConfig(self, conf); - StringValue(section); - StringValue(name); - str = NCONF_get_string(conf, RSTRING(section)->ptr, RSTRING(name)->ptr); - if(!str){ - ERR_clear_error(); - return Qnil; - } + CONF *conf = GetConfig(self); + VALUE str; + BIO *bio; - return rb_str_new2(str); + str = rb_funcall(other, rb_intern("to_s"), 0); + rb_check_frozen(self); + bio = ossl_obj2bio(&str); + config_load_bio(conf, bio); /* Consumes BIO */ + rb_obj_freeze(self); + 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 -ossl_config_get_value_old(int argc, VALUE *argv, VALUE self) +config_get_value(VALUE self, VALUE section, VALUE key) { - VALUE section, name; - - rb_scan_args(argc, argv, "11", §ion, &name); - - /* support conf.value(nil, "HOME") -> conf.get_value("", "HOME") */ - if (NIL_P(section)) section = rb_str_new2(""); - /* support conf.value("HOME") -> conf.get_value("", "HOME") */ - if (NIL_P(name)) { - name = section; - section = rb_str_new2(""); + 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; } - /* NOTE: Don't care about conf.get_value(nil, nil) */ - rb_warn("Config#value is deprecated; use Config#get_value"); - return ossl_config_get_value(self, section, name); -} - -static VALUE -set_conf_section_i(VALUE i, VALUE *arg) -{ - VALUE name, value; - - Check_Type(i, T_ARRAY); - name = rb_ary_entry(i, 0); - value = rb_ary_entry(i, 1); - ossl_config_add_value(arg[0], arg[1], name, value); - - return Qnil; -} - -static VALUE -ossl_config_set_section(VALUE self, VALUE section, VALUE hash) -{ - VALUE arg[2] = { self, section }; - rb_iterate(rb_each, hash, set_conf_section_i, (VALUE)arg); - return hash; + return rb_str_new_cstr(str); } /* - * Get all numbers as strings - use str.to_i to convert - * long number = CONF_get_number(confp->config, sect, StringValuePtr(item)); + * 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 -ossl_config_get_section(VALUE self, VALUE section) +config_get_section(VALUE self, VALUE section) { - CONF *conf; + CONF *conf = GetConfig(self); STACK_OF(CONF_VALUE) *sk; - CONF_VALUE *entry; int i, entries; VALUE hash; hash = rb_hash_new(); - GetConfig(self, conf); - if (!(sk = NCONF_get_section(conf, StringValuePtr(section)))) { - ERR_clear_error(); - return hash; + StringValueCStr(section); + if (!(sk = NCONF_get_section(conf, RSTRING_PTR(section)))) { + ossl_clear_error(); + return hash; } - if ((entries = sk_CONF_VALUE_num(sk)) < 0) { - OSSL_Debug("# of items in section is < 0?!?"); - 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)); } - for (i=0; i<entries; i++) { - entry = sk_CONF_VALUE_value(sk, i); - rb_hash_aset(hash, rb_str_new2(entry->name), rb_str_new2(entry->value)); - } - return hash; } -static VALUE -ossl_config_get_section_old(VALUE self, VALUE section) -{ - rb_warn("Config#section is deprecated; use Config#[]"); - return ossl_config_get_section(self, section); -} - -#ifdef IMPLEMENT_LHASH_DOALL_ARG_FN static void -get_conf_section(CONF_VALUE *cv, VALUE ary) +get_conf_section_doall_arg(CONF_VALUE *cv, VALUE *aryp) { - if(cv->name) return; - rb_ary_push(ary, rb_str_new2(cv->section)); + if (cv->name) + return; + rb_ary_push(*aryp, rb_str_new_cstr(cv->section)); } -static IMPLEMENT_LHASH_DOALL_ARG_FN(get_conf_section, CONF_VALUE*, VALUE); +/* 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 -ossl_config_get_sections(VALUE self) +config_get_sections(VALUE self) { - CONF *conf; + CONF *conf = GetConfig(self); VALUE ary; - GetConfig(self, conf); ary = rb_ary_new(); - lh_doall_arg(conf->data, LHASH_DOALL_ARG_FN(get_conf_section), (void*)ary); - + lh_doall_arg((_LHASH *)conf->data, LHASH_DOALL_ARG_FN(get_conf_section), + &ary); return ary; } -#else -static VALUE -ossl_config_get_sections(VALUE self) -{ - rb_warn("Config::sections don't work with %s", OPENSSL_VERSION_TEXT); - return rb_ary_new(); -} -#endif -#ifdef IMPLEMENT_LHASH_DOALL_ARG_FN static void -dump_conf_value(CONF_VALUE *cv, VALUE str) +dump_conf_value_doall_arg(CONF_VALUE *cv, VALUE *strp) { + VALUE str = *strp; STACK_OF(CONF_VALUE) *sk; - CONF_VALUE *v; int i, num; - if (cv->name) return; - sk = (STACK_OF(CONF_VALUE)*)cv->value; + if (cv->name) + return; + sk = (STACK_OF(CONF_VALUE) *)cv->value; num = sk_CONF_VALUE_num(sk); - rb_str_cat2(str, "[ "); - rb_str_cat2(str, cv->section); - rb_str_cat2(str, " ]\n"); - for(i = 0; i < num; i++){ - v = sk_CONF_VALUE_value(sk, i); - rb_str_cat2(str, v->name ? v->name : "None"); - rb_str_cat2(str, "="); - rb_str_cat2(str, v->value ? v->value : "None"); - rb_str_cat2(str, "\n"); + 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_cat2(str, "\n"); + rb_str_cat_cstr(str, "\n"); } -static IMPLEMENT_LHASH_DOALL_ARG_FN(dump_conf_value, CONF_VALUE*, VALUE); +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 file being loaded: + * + * config = OpenSSL::Config.load('baz.cnf') + * #=> #<OpenSSL::Config sections=["default"]> + * puts config.to_s + * #=> [ default ] + * # foo=bar + * # baz=buz + * + * You can 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 -dump_conf(CONF *conf) +config_to_s(VALUE self) { + CONF *conf = GetConfig(self); VALUE str; - str = rb_str_new(0, 0); - lh_doall_arg(conf->data, LHASH_DOALL_ARG_FN(dump_conf_value), (void*)str); - + str = rb_str_new(NULL, 0); + lh_doall_arg((_LHASH *)conf->data, LHASH_DOALL_ARG_FN(dump_conf_value), + &str); return str; } -static VALUE -ossl_config_to_s(VALUE self) +static void +each_conf_value_doall_arg(CONF_VALUE *cv, void *unused) { - CONF *conf; - - GetConfig(self, conf); + STACK_OF(CONF_VALUE) *sk; + VALUE section; + int i, num; - return dump_conf(conf); + 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)); + } } -#else + +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 -ossl_config_to_s(VALUE self) +config_each(VALUE self) { - rb_warn("Config::to_s don't work with %s", OPENSSL_VERSION_TEXT); - return rb_str_new(0, 0); + 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; } -#endif +/* + * call-seq: + * config.inspect -> string + * + * String representation of this configuration object, including the class + * name and its sections. + */ static VALUE -ossl_config_inspect(VALUE self) +config_inspect(VALUE self) { - VALUE str, ary = ossl_config_get_sections(self); - char *cname = rb_class2name(rb_obj_class(self)); + VALUE str, ary = config_get_sections(self); + const char *cname = rb_class2name(rb_obj_class(self)); - str = rb_str_new2("#<"); - rb_str_cat2(str, cname); - rb_str_cat2(str, " sections="); + 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_cat2(str, ">"); + rb_str_cat_cstr(str, ">"); return str; } -/* - * INIT - */ void -Init_ossl_config() +Init_ossl_config(void) { - eConfigError = rb_define_class_under(mOSSL, "ConfigError", eOSSLError); + char *path; + VALUE path_str; + + /* 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 https://docs.openssl.org/master/man5/config/ + */ cConfig = rb_define_class_under(mOSSL, "Config", rb_cObject); - rb_define_const(cConfig, "DEFAULT_CONFIG_FILE", - rb_str_new2(CONF_get1_default_config_file())); - rb_define_singleton_method(cConfig, "parse", ossl_config_s_parse, 1); + /* 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, ossl_config_s_alloc); - rb_define_copy_func(cConfig, ossl_config_copy); - rb_define_method(cConfig, "initialize", ossl_config_initialize, -1); - rb_define_method(cConfig, "get_value", ossl_config_get_value, 2); - rb_define_method(cConfig, "value", ossl_config_get_value_old, -1); - rb_define_method(cConfig, "add_value", ossl_config_add_value, 3); - rb_define_method(cConfig, "[]", ossl_config_get_section, 1); - rb_define_method(cConfig, "section", ossl_config_get_section_old, 1); - rb_define_method(cConfig, "[]=", ossl_config_set_section, 2); - rb_define_method(cConfig, "sections", ossl_config_get_sections, 0); - rb_define_method(cConfig, "to_s", ossl_config_to_s, 0); - rb_define_method(cConfig, "inspect", ossl_config_inspect, 0); + 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 = rb_obj_freeze(ossl_buf2str(path, rb_long2int(strlen(path)))); + rb_define_const(cConfig, "DEFAULT_CONFIG_FILE", path_str); } |
