[ruby-dev:21695] Re: access ENV on $SAFE==4
From:
Hidetoshi NAGAI <nagai@...>
Date:
2003-10-23 09:12:38 UTC
List:
ruby-dev #21695
永井@知能.九工大です.
とりあえず,
From: Hidetoshi NAGAI <nagai@ai.kyutech.ac.jp>
Subject: [ruby-dev:21656] Re: access ENV on $SAFE==4
Date: Tue, 21 Oct 2003 06:14:56 +0900
Message-ID: <20031021.061455.74732176.nagai@ai.kyutech.ac.jp>
> (2) $SAFE < 4 において指定された情報にのみアクセスが許可される.
> 最も妥当な線か? ただしライブラリ側の対応が必要となるため,マイ
> ナーバージョンアップのレベルで導入するには大きすぎる変更かも.
でのパッチを作ってみました.
ENV
RUBY_PLATFORM (PLATFORM)
$LOAD_PATH ($:, $-I)
のすべてにおいて,$SAFE >= 4 でのアクセスに制約がかかります.
RUBY_PLATFORM (PLATFORM) は $SAFE >= 4 では nil が返ります.
$LOAD_PATH ($:, $-I) の場合は空の配列が返ります.
ENV についてはメソッドでのアクセスとなりますが,
$SAFE < 4 で許可したもの以外に $SAFE >= 4 では読めなくなります
(書き込みは従来通り一切不可).
アクセス許可のため,次の新しいメソッドを追加しています
(メソッド名はまだ検討の余地ありですが).
ENV.allow?(name)
: 環境変数 name (文字列指定) への $SAFE >=4 でのアクセスが
: 許可されていれば true を,許可されていなければ false を返す.
ENV.allows
: 現在許可されている環境変数のリストを得る.
: ただし,$SAFE >= 4 では例外を発生する.
ENV.allow(name)
: 環境変数 name への $SAFE >=4 でのアクセスを許可する.
: ただし,$SAFE >= 4 では例外を発生する.
ENV.deny(name)
: 環境変数 name への $SAFE >=4 でのアクセスを禁止する.
: ただし,$SAFE >= 4 では例外を発生する.
デフォルトでは,すべての環境変数へのアクセスが
$SAFE >= 4 において禁止されています.
よかったら試してみてください.
Index: eval.c
===================================================================
RCS file: /src/ruby/eval.c,v
retrieving revision 1.573
diff -u -r1.573 eval.c
--- eval.c 23 Oct 2003 02:19:00 -0000 1.573
+++ eval.c 23 Oct 2003 09:05:38 -0000
@@ -6420,6 +6420,16 @@
}
static VALUE
+loadpath_getter(id)
+ ID id;
+{
+ if (rb_safe_level() >= 4) {
+ return rb_ary_new();
+ }
+ return rb_load_path;
+}
+
+static VALUE
rb_f_local_variables()
{
ID *tbl;
@@ -6719,9 +6729,9 @@
Init_load()
{
rb_load_path = rb_ary_new();
- rb_define_readonly_variable("$:", &rb_load_path);
- rb_define_readonly_variable("$-I", &rb_load_path);
- rb_define_readonly_variable("$LOAD_PATH", &rb_load_path);
+ rb_define_hooked_variable("$:", &rb_load_path, loadpath_getter, 0);
+ rb_define_hooked_variable("$-I", &rb_load_path, loadpath_getter, 0);
+ rb_define_hooked_variable("$LOAD_PATH", &rb_load_path, loadpath_getter, 0);
rb_features = rb_ary_new();
rb_define_readonly_variable("$\"", &rb_features);
Index: hash.c
===================================================================
RCS file: /src/ruby/hash.c,v
retrieving revision 1.116
diff -u -r1.116 hash.c
--- hash.c 1 Aug 2003 20:16:48 -0000 1.116
+++ hash.c 23 Oct 2003 09:05:38 -0000
@@ -44,7 +44,7 @@
VALUE rb_cHash;
-static VALUE envtbl;
+static VALUE envtbl, allowed_envkeys;
static ID id_hash, id_call, id_default;
VALUE
@@ -1000,6 +1000,84 @@
return env_str_new(ptr, strlen(ptr));
}
+static ID id_case_conv;
+
+static int
+env_allow_i(name)
+ VALUE name;
+{
+ VALUE key;
+
+ StringValue(name);
+#ifdef ENV_IGNORECASE
+ key = rb_funcall(name, id_case_conv, 0);
+#else
+ key = name;
+#endif
+ if (RTEST(rb_ary_includes(allowed_envkeys, key))) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+static VALUE
+env_allow_p(obj, name)
+ VALUE obj, name;
+{
+ if (env_allow_i(name)) {
+ return Qtrue;
+ } else {
+ return Qfalse;
+ }
+}
+
+static VALUE
+env_allow_list(obj)
+ VALUE obj;
+{
+ rb_secure(4);
+ return rb_ary_dup(allowed_envkeys);
+}
+
+static VALUE
+env_allow(obj, name)
+ VALUE obj, name;
+{
+ VALUE key;
+
+ rb_secure(4);
+ if (!env_allow_i(name)) {
+ StringValue(name);
+#ifdef ENV_IGNORECASE
+ key = rb_funcall(name, id_case_conv, 0);
+#else
+ key = name;
+#endif
+ rb_ary_push(allowed_envkeys, name);
+ }
+ return key;
+}
+
+static VALUE
+env_deny(obj, name)
+ VALUE obj, name;
+{
+ VALUE key;
+
+ rb_secure(4);
+ StringValue(name);
+#ifdef ENV_IGNORECASE
+ key = rb_funcall(name, id_case_conv, 0);
+#else
+ key = name;
+#endif
+ if (RTEST(rb_ary_delete(allowed_envkeys, key))) {
+ return key;
+ }
+ return Qnil;
+}
+
static VALUE
env_delete(obj, name)
VALUE obj, name;
@@ -1052,6 +1130,13 @@
if (strlen(nam) != RSTRING(name)->len) {
rb_raise(rb_eArgError, "bad environment variable name");
}
+ if (rb_safe_level() >= 4) {
+ /* allowed key? */
+ if (!env_allow_i(name)) {
+ /* not allowed */
+ return Qnil;
+ }
+ }
env = getenv(nam);
if (env) {
#ifdef ENV_IGNORECASE
@@ -1084,7 +1169,12 @@
if (strlen(nam) != RSTRING(key)->len) {
rb_raise(rb_eArgError, "bad environment variable name");
}
- env = getenv(nam);
+ if (rb_safe_level() >= 4 && !env_allow_i(key)) {
+ /* not allowed */
+ env = (char*)NULL;
+ } else {
+ env = getenv(nam);
+ }
if (!env) {
if (rb_block_given_p()) {
if (argc > 1) {
@@ -1287,7 +1377,10 @@
while (*env) {
char *s = strchr(*env, '=');
if (s) {
- rb_ary_push(ary, env_str_new(*env, s-*env));
+ VALUE k = env_str_new(*env, s-*env);
+ if (rb_safe_level() < 4 || env_allow_i(k)) {
+ rb_ary_push(ary, k);
+ }
}
env++;
}
@@ -1318,7 +1411,9 @@
while (*env) {
char *s = strchr(*env, '=');
if (s) {
- rb_ary_push(ary, env_str_new2(s+1));
+ if (rb_safe_level() < 4 || env_allow_i(env_str_new(*env,s-*env))) {
+ rb_ary_push(ary, env_str_new2(s+1));
+ }
}
env++;
}
@@ -1351,8 +1446,11 @@
while (*env) {
char *s = strchr(*env, '=');
if (s) {
- rb_ary_push(ary, env_str_new(*env, s-*env));
- rb_ary_push(ary, env_str_new2(s+1));
+ VALUE k = env_str_new(*env, s-*env);
+ if (rb_safe_level() < 4 || env_allow_i(k)) {
+ rb_ary_push(ary, k);
+ rb_ary_push(ary, env_str_new2(s+1));
+ }
}
env++;
}
@@ -1431,8 +1529,10 @@
if (s) {
VALUE k = env_str_new(*env, s-*env);
VALUE v = env_str_new2(s+1);
- if (RTEST(rb_yield_values(2, k, v))) {
- rb_ary_push(result, rb_assoc_new(k, v));
+ if (rb_safe_level() < 4 || env_allow_i(k)) {
+ if (RTEST(rb_yield_values(2, k, v))) {
+ rb_ary_push(result, rb_assoc_new(k, v));
+ }
}
}
env++;
@@ -1477,15 +1577,17 @@
while (*env) {
char *s = strchr(*env, '=');
- if (env != environ) {
- rb_str_buf_cat2(str, ", ");
- }
if (s) {
- rb_str_buf_cat2(str, "\"");
- rb_str_buf_cat(str, *env, s-*env);
- rb_str_buf_cat2(str, "\"=>");
- i = rb_inspect(rb_str_new2(s+1));
- rb_str_buf_append(str, i);
+ if (rb_safe_level() < 4 || env_allow_i(env_str_new(*env,s-*env))) {
+ if (env != environ) {
+ rb_str_buf_cat2(str, ", ");
+ }
+ rb_str_buf_cat2(str, "\"");
+ rb_str_buf_cat(str, *env, s-*env);
+ rb_str_buf_cat2(str, "\"=>");
+ i = rb_inspect(rb_str_new2(s+1));
+ rb_str_buf_append(str, i);
+ }
}
env++;
}
@@ -1506,8 +1608,10 @@
while (*env) {
char *s = strchr(*env, '=');
if (s) {
- rb_ary_push(ary, rb_assoc_new(env_str_new(*env, s-*env),
- env_str_new2(s+1)));
+ VALUE k = env_str_new(*env, s-*env);
+ if (rb_safe_level() < 4 || env_allow_i(k)) {
+ rb_ary_push(ary, rb_assoc_new(k, env_str_new2(s+1)));
+ }
}
env++;
}
@@ -1524,14 +1628,21 @@
static VALUE
env_size()
{
- int i;
+ int size = 0;
char **env;
env = GET_ENVIRON(environ);
- for(i=0; env[i]; i++)
- ;
+ while (*env) {
+ char *s = strchr(*env, '=');
+ if (s) {
+ if (rb_safe_level() < 4 || env_allow_i(env_str_new(*env,s-*env))) {
+ size++;
+ }
+ }
+ env++;
+ }
FREE_ENVIRON(environ);
- return INT2FIX(i);
+ return INT2FIX(size);
}
static VALUE
@@ -1539,6 +1650,13 @@
{
char **env;
+ if (rb_safe_level() >= 4) {
+ if (FIX2INT(env_size()) > 0) {
+ return Qtrue;
+ } else {
+ return Qfalse;
+ }
+ }
env = GET_ENVIRON(environ);
if (env[0] == 0) {
FREE_ENVIRON(environ);
@@ -1554,6 +1672,9 @@
{
char *s;
+ if (rb_safe_level() >= 4 && !env_allow_i(key)) {
+ return Qfalse;
+ }
s = StringValuePtr(key);
if (strlen(s) != RSTRING(key)->len)
rb_raise(rb_eArgError, "bad environment variable name");
@@ -1572,13 +1693,16 @@
while (*env) {
char *s = strchr(*env, '=');
if (s++) {
+ VALUE k = env_str_new(*env, s-*env);
+ if (rb_safe_level() < 4 || env_allow_i(k)) {
#ifdef ENV_IGNORECASE
- if (strncasecmp(s, RSTRING(value)->ptr, strlen(s)) == 0) {
+ if (strncasecmp(s, RSTRING(value)->ptr, strlen(s)) == 0) {
#else
- if (strncmp(s, RSTRING(value)->ptr, strlen(s)) == 0) {
+ if (strncmp(s, RSTRING(value)->ptr, strlen(s)) == 0) {
#endif
- FREE_ENVIRON(environ);
- return Qtrue;
+ FREE_ENVIRON(environ);
+ return Qtrue;
+ }
}
}
env++;
@@ -1592,21 +1716,22 @@
VALUE dmy, value;
{
char **env;
- VALUE str;
StringValue(value);
env = GET_ENVIRON(environ);
while (*env) {
char *s = strchr(*env, '=');
if (s++) {
+ VALUE k = env_str_new(*env, s-*env);
+ if (rb_safe_level() < 4 || env_allow_i(k)) {
#ifdef ENV_IGNORECASE
- if (strncasecmp(s, RSTRING(value)->ptr, strlen(s)) == 0) {
+ if (strncasecmp(s, RSTRING(value)->ptr, strlen(s)) == 0) {
#else
- if (strncmp(s, RSTRING(value)->ptr, strlen(s)) == 0) {
+ if (strncmp(s, RSTRING(value)->ptr, strlen(s)) == 0) {
#endif
- str = env_str_new(*env, s-*env-1);
- FREE_ENVIRON(environ);
- return str;
+ FREE_ENVIRON(environ);
+ return k;
+ }
}
}
env++;
@@ -1631,7 +1756,12 @@
RARRAY(indexes)->ptr[i] = Qnil;
}
else {
- RARRAY(indexes)->ptr[i] = env_str_new2(getenv(RSTRING(tmp)->ptr));
+ if (rb_safe_level() >= 4 && !env_allow_i(tmp)) {
+ RARRAY(indexes)->ptr[i] = Qnil;
+ }
+ else {
+ RARRAY(indexes)->ptr[i] = env_str_new2(getenv(RSTRING(tmp)->ptr));
+ }
}
RARRAY(indexes)->len = i+1;
}
@@ -1649,8 +1779,10 @@
while (*env) {
char *s = strchr(*env, '=');
if (s) {
- rb_hash_aset(hash, env_str_new(*env, s-*env),
- env_str_new2(s+1));
+ VALUE key = env_str_new(*env, s-*env);
+ if (rb_safe_level() < 4 || env_allow_i(key)) {
+ rb_hash_aset(hash, key, env_str_new2(s+1));
+ }
}
env++;
}
@@ -1809,11 +1941,23 @@
rb_define_method(rb_cHash,"key?", rb_hash_has_key, 1);
rb_define_method(rb_cHash,"value?", rb_hash_has_value, 1);
+#if defined(__human68k__)
+ id_case_conv = rb_intern("downcase");
+#else
+ id_case_conv = rb_intern("upcase");
+#endif
+
#ifndef __MACOS__ /* environment variables nothing on MacOS. */
origenviron = environ;
envtbl = rb_obj_alloc(rb_cObject);
rb_extend_object(envtbl, rb_mEnumerable);
+ allowed_envkeys = rb_ary_new();
+ rb_global_variable(&allowed_envkeys);
+ rb_define_singleton_method(envtbl,"allow?", env_allow_p, 1);
+ rb_define_singleton_method(envtbl,"allows", env_allow_list, 0);
+ rb_define_singleton_method(envtbl,"allow", env_allow, 1);
+ rb_define_singleton_method(envtbl,"deny", env_deny, 1);
rb_define_singleton_method(envtbl,"[]", rb_f_getenv, 1);
rb_define_singleton_method(envtbl,"fetch", env_fetch, -1);
rb_define_singleton_method(envtbl,"[]=", env_aset, 2);
Index: inits.c
===================================================================
RCS file: /src/ruby/inits.c,v
retrieving revision 1.8
diff -u -r1.8 inits.c
--- inits.c 15 Aug 2003 03:01:52 -0000 1.8
+++ inits.c 23 Oct 2003 09:05:38 -0000
@@ -43,6 +43,7 @@
void Init_Struct _((void));
void Init_Time _((void));
void Init_var_tables _((void));
+void Init_restricted_global_consts _((void));
void Init_version _((void));
void
@@ -78,5 +79,6 @@
Init_Math();
Init_GC();
Init_marshal();
+ Init_restricted_global_consts();
Init_version();
}
Index: variable.c
===================================================================
RCS file: /src/ruby/variable.c,v
retrieving revision 1.107
diff -u -r1.107 variable.c
--- variable.c 13 Oct 2003 14:57:36 -0000 1.107
+++ variable.c 23 Oct 2003 09:05:38 -0000
@@ -21,6 +21,77 @@
static st_table *rb_global_tbl;
st_table *rb_class_tbl;
static ID autoload, classpath, tmp_classpath;
+static VALUE restricted_global_consts;
+
+static int
+restricted_global_const_exist(id)
+ ID id;
+{
+ return st_lookup(RHASH(restricted_global_consts)->tbl, ID2SYM(id), 0);
+}
+
+static VALUE
+restricted_global_const_set(id, val)
+ ID id;
+ VALUE val;
+{
+ return rb_hash_aset(restricted_global_consts, ID2SYM(id), val);
+}
+
+static VALUE
+restricted_global_const_get(id)
+ ID id;
+{
+ VALUE val;
+
+ if (!st_lookup(RHASH(restricted_global_consts)->tbl, ID2SYM(id), &val)) {
+ return Qnil;
+ }
+ if (val == Qundef) {
+ return Qnil;
+ }
+ if (rb_safe_level() >= 4) {
+ return Qnil;
+ }
+ return val;
+}
+
+static VALUE
+restricted_global_const_remove(id)
+ ID id;
+{
+ VALUE val;
+
+ if (!restricted_global_const_exist(id)) {
+ return Qnil;
+ }
+ if ((val = restricted_global_const_get(id)) == Qundef) {
+ return Qnil;
+ }
+ rb_hash_aset(restricted_global_consts, ID2SYM(id), Qundef);
+ return val;
+}
+
+void
+create_restricted_global_consts_table()
+{
+ /* create table */
+ /* :: The table is a hash { const_sym=>val, ... }. */
+ /* :: If val == Qundef, the constant is not defined. */
+ rb_global_variable(&restricted_global_consts);
+ restricted_global_consts = rb_hash_new();
+}
+
+void
+Init_restricted_global_consts()
+{
+ VALUE val;
+
+ /* RUBY_PLATFORM (PLATFORM) */
+ val = rb_obj_freeze(rb_str_new2(RUBY_PLATFORM));
+ restricted_global_const_set(rb_intern("RUBY_PLATFORM"), val);
+ restricted_global_const_set(rb_intern("PLATFORM"), val);
+}
void
Init_var_tables()
@@ -30,6 +101,7 @@
autoload = rb_intern("__autoload__");
classpath = rb_intern("__classpath__");
tmp_classpath = rb_intern("__tmp_classpath__");
+ create_restricted_global_consts_table();
}
struct fc_result {
@@ -1157,6 +1229,9 @@
rb_raise(rb_eArgError, "empty file name");
}
+ if (mod == rb_cObject && restricted_global_const_exist(id))
+ return;
+
if ((tbl = RCLASS(mod)->iv_tbl) && st_lookup(tbl, id, &av) && av != Qundef)
return;
@@ -1278,6 +1353,9 @@
tmp = klass;
retry:
while (tmp) {
+ if (tmp == rb_cObject && restricted_global_const_exist(id)) {
+ return restricted_global_const_get(id);
+ }
while (RCLASS(tmp)->iv_tbl && st_lookup(RCLASS(tmp)->iv_tbl,id,&value)) {
if (value == Qundef) {
rb_autoload_load(tmp, id);
@@ -1339,6 +1417,9 @@
rb_raise(rb_eSecurityError, "Insecure: can't remove constant");
if (OBJ_FROZEN(mod)) rb_error_frozen("class/module");
+ if (restricted_global_const_exist(id)) {
+ return restricted_global_const_remove(id);
+ }
if (RCLASS(mod)->iv_tbl && st_delete(ROBJECT(mod)->iv_tbl, (st_data_t*)&id, &val)) {
if (val == Qundef) {
autoload_delete(mod, id);
@@ -1369,6 +1450,21 @@
return ST_CONTINUE;
}
+static int
+s_rgc_i(key, value, tbl)
+ VALUE key;
+ VALUE value;
+ st_table *tbl;
+{
+ ID id = SYM2ID(key);
+ if (rb_is_const_id(id) && value != Qundef) {
+ if (!st_lookup(tbl, id, 0)) {
+ st_insert(tbl, id, id);
+ }
+ }
+ return ST_CONTINUE;
+}
+
void*
rb_mod_const_at(mod, data)
VALUE mod;
@@ -1381,6 +1477,10 @@
if (RCLASS(mod)->iv_tbl) {
st_foreach(RCLASS(mod)->iv_tbl, sv_i, (st_data_t)tbl);
}
+ if (mod == rb_cObject) {
+ st_foreach(RHASH(restricted_global_consts)->tbl,
+ s_rgc_i, (st_data_t)tbl);
+ }
return tbl;
}
@@ -1442,6 +1542,9 @@
tmp = klass;
retry:
while (tmp) {
+ if (tmp == rb_cObject && restricted_global_const_exist(id)) {
+ return Qtrue;
+ }
if (RCLASS(tmp)->iv_tbl && st_lookup(RCLASS(tmp)->iv_tbl, id, &value)) {
if (value == Qundef && NIL_P(autoload_file(klass, id)))
return Qfalse;
@@ -1517,6 +1620,11 @@
ID id;
VALUE val;
{
+ if (klass == rb_cObject && restricted_global_const_exist(id)) {
+ rb_secure(4);
+ restricted_global_const_set(id, val);
+ return;
+ }
mod_av_set(klass, id, val, Qtrue);
}
Index: version.c
===================================================================
RCS file: /src/ruby/version.c,v
retrieving revision 1.8
diff -u -r1.8 version.c
--- version.c 16 Jan 2003 07:34:03 -0000 1.8
+++ version.c 23 Oct 2003 09:05:38 -0000
@@ -19,16 +19,16 @@
{
VALUE v = rb_obj_freeze(rb_str_new2(RUBY_VERSION));
VALUE d = rb_obj_freeze(rb_str_new2(RUBY_RELEASE_DATE));
- VALUE p = rb_obj_freeze(rb_str_new2(RUBY_PLATFORM));
+ /* VALUE p = rb_obj_freeze(rb_str_new2(RUBY_PLATFORM)); */
rb_define_global_const("RUBY_VERSION", v);
rb_define_global_const("RUBY_RELEASE_DATE", d);
- rb_define_global_const("RUBY_PLATFORM", p);
+ /* rb_define_global_const("RUBY_PLATFORM", p); */
/* obsolete constants */
rb_define_global_const("VERSION", v);
rb_define_global_const("RELEASE_DATE", d);
- rb_define_global_const("PLATFORM", p);
+ /* rb_define_global_const("PLATFORM", p); */
}
void
--
永井 秀利 (九工大 知能情報)
nagai@ai.kyutech.ac.jp