永井@知能.九工大です. とりあえず, 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