Re: Backport patch for realpath(3) usage for File.realpath to Ruby 2.4-2.6 to work with unveil(2)

2019-08-28 Thread Jeremy Evans
On 07/01 12:34, Jeremy Evans wrote:
> Ruby previously had an emulated approach for File.realpath, which did
> not work correctly when using unveil(2).  This backports a patch to
> use realpath(3) for File.realpath that I recently committed upstream.
> 
> I have tested this works as expected with unveil(2) on -current, and
> have been running it on some personal apps for about a week to serve
> Ruby web applications using unveil(2) instead of chroot(2) to limit file
> system access.  unveil(2) is a lot less fragile than chroot(2) for
> limiting file system access in Ruby web applications, because many Ruby
> libraries have an unfortunate tendency to load Ruby code at runtime from
> locations under /usr/local/lib/ruby due to a misfeature called autoload.
> 
> Regen patches while here.
> 
> I plan to commit this in a couple days unless I hear objections.

Looks like I forgot to commit this in July.  I've been running it since
then with no problems.

Today, new versions of Ruby 2.4, 2.5, and 2.6 were released to fix a
minor issue in RDoc due to an embedded copy of JQuery.

Release announcements at:

https://www.ruby-lang.org/en/news/2019/08/28/ruby-2-6-4-released/
https://www.ruby-lang.org/en/news/2019/08/28/ruby-2-5-6-released/
https://www.ruby-lang.org/en/news/2019/08/28/ruby-2-4-7-released/

I'm going to include the File.realpath patch with this version update.

Ports-wise, this drops the PATCHFILES usage in ruby 2.6, as the patch
is included in 2.6.4.  It also regens patches.

Tested on amd64.  I plan to commit this in a couple days unless I hear
objections.  After that, I'll update 6.5-stable to get the security
fix, but I will not be including the File.realpath patch in the
-stable update.

Thanks,
Jeremy

Index: 2.4/Makefile
===
RCS file: /cvs/ports/lang/ruby/2.4/Makefile,v
retrieving revision 1.17
diff -u -p -r1.17 Makefile
--- 2.4/Makefile25 Jun 2019 20:25:21 -  1.17
+++ 2.4/Makefile28 Aug 2019 18:00:33 -
@@ -1,7 +1,6 @@
 # $OpenBSD: Makefile,v 1.17 2019/06/25 20:25:21 sthen Exp $
 
-VERSION =  2.4.6
-REVISION-main =0
+VERSION =  2.4.7
 SHARED_LIBS =  ruby24  2.0
 NEXTVER =  2.5
 
Index: 2.4/distinfo
===
RCS file: /cvs/ports/lang/ruby/2.4/distinfo,v
retrieving revision 1.10
diff -u -p -r1.10 distinfo
--- 2.4/distinfo3 Apr 2019 17:25:25 -   1.10
+++ 2.4/distinfo28 Aug 2019 18:00:33 -
@@ -1,2 +1,2 @@
-SHA256 (ruby-2.4.6.tar.gz) = 3g3ICXAjcWCZ98im/8dRURuQ3n9WlPQBtZ8tBx25EL4=
-SIZE (ruby-2.4.6.tar.gz) = 15880585
+SHA256 (ruby-2.4.7.tar.gz) = zW78cgympiJ0XiusefRebNY6sPWlOtfriBVF9Y/zi4k=
+SIZE (ruby-2.4.7.tar.gz) = 16036496
Index: 2.4/patches/patch-file_c
===
RCS file: 2.4/patches/patch-file_c
diff -N 2.4/patches/patch-file_c
--- /dev/null   1 Jan 1970 00:00:00 -
+++ 2.4/patches/patch-file_c28 Aug 2019 18:00:33 -
@@ -0,0 +1,102 @@
+$OpenBSD$
+
+Backport use of realpath(3) for File.realpath to allow unveil(2) to work.
+
+Index: file.c
+--- file.c.orig
 file.c
+@@ -126,6 +126,9 @@ int flock(int, int);
+ #define STAT(p, s)stat((p), (s))
+ #endif
+ 
++#include 
++#include 
++
+ VALUE rb_cFile;
+ VALUE rb_mFileTest;
+ VALUE rb_cStat;
+@@ -3898,7 +3901,7 @@ realpath_rec(long *prefixlenp, VALUE *resolvedp, const
+ }
+ 
+ static VALUE
+-rb_check_realpath_internal(VALUE basedir, VALUE path, enum rb_realpath_mode 
mode)
++rb_check_realpath_emulate(VALUE basedir, VALUE path, enum rb_realpath_mode 
mode)
+ {
+ long prefixlen;
+ VALUE resolved;
+@@ -3980,6 +3983,75 @@ rb_check_realpath_internal(VALUE basedir, VALUE path, 
+   rb_enc_associate(resolved, origenc);
+ 
+ OBJ_INFECT(resolved, unresolved_path);
++return resolved;
++}
++
++static VALUE rb_file_join(VALUE ary, VALUE sep);
++
++static VALUE
++rb_check_realpath_internal(VALUE basedir, VALUE path, enum rb_realpath_mode 
mode)
++{
++VALUE unresolved_path;
++rb_encoding *origenc;
++char *resolved_ptr = NULL;
++VALUE resolved;
++
++if (mode == RB_REALPATH_DIR) {
++  return rb_check_realpath_emulate(basedir, path, mode);
++}
++
++unresolved_path = rb_str_dup_frozen(path);
++origenc = rb_enc_get(unresolved_path);
++if (*RSTRING_PTR(unresolved_path) != '/' && !NIL_P(basedir)) {
++  unresolved_path = rb_file_join(rb_ary_new_from_args(2, basedir, 
unresolved_path), rb_str_new2("/"));
++}
++
++if((resolved_ptr = realpath(RSTRING_PTR(unresolved_path), NULL)) == NULL) 
{
++  /* glibc realpath(3) does not allow /path/to/file.rb/../other_file.rb,
++ returning ENOTDIR in that case.
++ glibc realpath(3) can also return ENOENT for paths that exist,
++ such as /dev/fd/5.
++ Fallback to the emulated approach in either of those cases. */
++  if 

Backport patch for realpath(3) usage for File.realpath to Ruby 2.4-2.6 to work with unveil(2)

2019-07-01 Thread Jeremy Evans
Ruby previously had an emulated approach for File.realpath, which did
not work correctly when using unveil(2).  This backports a patch to
use realpath(3) for File.realpath that I recently committed upstream.

I have tested this works as expected with unveil(2) on -current, and
have been running it on some personal apps for about a week to serve
Ruby web applications using unveil(2) instead of chroot(2) to limit file
system access.  unveil(2) is a lot less fragile than chroot(2) for
limiting file system access in Ruby web applications, because many Ruby
libraries have an unfortunate tendency to load Ruby code at runtime from
locations under /usr/local/lib/ruby due to a misfeature called autoload.

Regen patches while here.

I plan to commit this in a couple days unless I hear objections.

Thanks,
Jeremy

Index: 2.4/Makefile
===
RCS file: /cvs/ports/lang/ruby/2.4/Makefile,v
retrieving revision 1.16
diff -u -p -r1.16 Makefile
--- 2.4/Makefile3 Apr 2019 17:25:25 -   1.16
+++ 2.4/Makefile26 Jun 2019 19:45:33 -
@@ -3,6 +3,7 @@
 VERSION =  2.4.6
 SHARED_LIBS =  ruby24  2.0
 NEXTVER =  2.5
+REVISION-main =0
 
 PSEUDO_FLAVORS=no_ri_docs bootstrap
 # Do not build the RI docs on slow arches
Index: 2.4/patches/patch-file_c
===
RCS file: 2.4/patches/patch-file_c
diff -N 2.4/patches/patch-file_c
--- /dev/null   1 Jan 1970 00:00:00 -
+++ 2.4/patches/patch-file_c26 Jun 2019 20:11:48 -
@@ -0,0 +1,102 @@
+$OpenBSD$
+
+Backport use of realpath(3) for File.realpath to allow unveil(2) to work.
+
+Index: file.c
+--- file.c.orig
 file.c
+@@ -126,6 +126,9 @@ int flock(int, int);
+ #define STAT(p, s)stat((p), (s))
+ #endif
+ 
++#include 
++#include 
++
+ VALUE rb_cFile;
+ VALUE rb_mFileTest;
+ VALUE rb_cStat;
+@@ -3898,7 +3901,7 @@ realpath_rec(long *prefixlenp, VALUE *resolvedp, const
+ }
+ 
+ static VALUE
+-rb_check_realpath_internal(VALUE basedir, VALUE path, enum rb_realpath_mode 
mode)
++rb_check_realpath_emulate(VALUE basedir, VALUE path, enum rb_realpath_mode 
mode)
+ {
+ long prefixlen;
+ VALUE resolved;
+@@ -3980,6 +3983,75 @@ rb_check_realpath_internal(VALUE basedir, VALUE path, 
+   rb_enc_associate(resolved, origenc);
+ 
+ OBJ_INFECT(resolved, unresolved_path);
++return resolved;
++}
++
++static VALUE rb_file_join(VALUE ary, VALUE sep);
++
++static VALUE
++rb_check_realpath_internal(VALUE basedir, VALUE path, enum rb_realpath_mode 
mode)
++{
++VALUE unresolved_path;
++rb_encoding *origenc;
++char *resolved_ptr = NULL;
++VALUE resolved;
++
++if (mode == RB_REALPATH_DIR) {
++  return rb_check_realpath_emulate(basedir, path, mode);
++}
++
++unresolved_path = rb_str_dup_frozen(path);
++origenc = rb_enc_get(unresolved_path);
++if (*RSTRING_PTR(unresolved_path) != '/' && !NIL_P(basedir)) {
++  unresolved_path = rb_file_join(rb_ary_new_from_args(2, basedir, 
unresolved_path), rb_str_new2("/"));
++}
++
++if((resolved_ptr = realpath(RSTRING_PTR(unresolved_path), NULL)) == NULL) 
{
++  /* glibc realpath(3) does not allow /path/to/file.rb/../other_file.rb,
++ returning ENOTDIR in that case.
++ glibc realpath(3) can also return ENOENT for paths that exist,
++ such as /dev/fd/5.
++ Fallback to the emulated approach in either of those cases. */
++  if (errno == ENOTDIR ||
++  (errno == ENOENT && rb_file_exist_p(0, unresolved_path))) {
++  return rb_check_realpath_emulate(basedir, path, mode);
++
++  }
++  if (mode == RB_REALPATH_CHECK) {
++  return Qnil;
++  }
++  rb_sys_fail_path(unresolved_path);
++}
++resolved = ospath_new(resolved_ptr, strlen(resolved_ptr), 
rb_filesystem_encoding());
++free(resolved_ptr);
++
++if (mode == RB_REALPATH_STRICT || mode == RB_REALPATH_CHECK) {
++  struct stat st;
++
++  if (rb_stat(resolved, ) < 0) {
++  if (mode == RB_REALPATH_STRICT) {
++  rb_sys_fail_path(unresolved_path);
++  }
++  return Qnil;
++  }
++}
++
++if (origenc != rb_enc_get(resolved)) {
++  if (!rb_enc_str_asciionly_p(resolved)) {
++  resolved = rb_str_conv_enc(resolved, NULL, origenc);
++  }
++  rb_enc_associate(resolved, origenc);
++}
++
++if(rb_enc_str_coderange(resolved) == ENC_CODERANGE_BROKEN) {
++  rb_enc_associate(resolved, rb_filesystem_encoding());
++  if(rb_enc_str_coderange(resolved) == ENC_CODERANGE_BROKEN) {
++  rb_enc_associate(resolved, rb_ascii8bit_encoding());
++  }
++}
++
++rb_obj_taint(resolved);
++RB_GC_GUARD(unresolved_path);
+ return resolved;
+ }
+ 
Index: 2.5/Makefile
===
RCS file: /cvs/ports/lang/ruby/2.5/Makefile,v
retrieving