omar-polo / gmid

a Gemini server
https://gmid.omarpolo.com
ISC License
98 stars 8 forks source link

cgi with chroot always 42 #10

Closed Toby222 closed 2 years ago

Toby222 commented 2 years ago

Every time I make a request to a cgi file to my gmid server, I get a 42 CGI error. After some debugging, it seems that this execvp call always fails with No such file or directory, despite the file definitely existing and being executable by the gmid user. Console output showing File Not Found error image

The regression tests work fine, so I think it might be an error with chroot being configured I've been trying to debug this with low-to-mediocre C-skill but ran out of ideas.


I'm running gmid on my RasPi with Arch arm for armv7 architecture. (I needed to SC_ALLOW fstatat64, _llseek, and sigreturn to stop errors there)

I'm using this config:

port 1965
user "gmid"

chroot "/var/gemini"

server "home.tobot.dev" {
  cert "/etc/ssl/certs/home.tobot.dev.cert.pem"
  key "/etc/ssl/private/home.tobot.dev.privkey.pem"

  root "/home.tobot.dev"
  auto index on

  cgi "*"
}

server "localhost" {
  cert "/etc/ssl/certs/localhost.pem"
  key "/etc/ssl/private/localhost.key"

  root "/home.tobot.dev"
  auto index on

  cgi "*"
}
omar-polo commented 2 years ago

The "no such file or directory" is kind of misleading. It doesn't refer to the actual program (as you can have proven, it's really there) but to the kernel failing to execute it.

When running in a chroot you need to install in it also the interpreter for your CGI script and all of its dependencies (shared libraries.) The "no such file or directory" is the kernel failing to find the interpreter (sh, perl, python, ...) or ld.so failing to load a shared library in the chroot.

(if your CGI script is a compiled program, for e.g. C, Go, rust, ..., you could try to statically compile and copy it in the chroot, it should "just work" then)

Yep, running CGI scripts in a chroot is kind of a pain. gmid also supports FastCGI and, from 1.8 onwards, a reverse-proxying facility that can help alleviate the pain with plain CGI under chroot.

omar-polo commented 2 years ago

Oh, I almost forgot

(I needed to SC_ALLOW fstatat64, _llseek, and sigreturn to stop errors there)

If you turn that into a PR I'll be happy to merge it!

Toby222 commented 2 years ago

That's what I initially thought, so I first tried copying sh to the chroot, then made a cgi ... "script" that's just a statically linked C executable, so there should be absolutely no dependencies for it, neither of which worked.

omar-polo commented 2 years ago

Just to be sure, what ldd hello (or how the script is named) says? (you could also use file hello on some systems to tell if an executable is statically linked or not.)

I can't reproduce it on OpenBSD but will try with arch later.

thanks :)

P.S.: copying sh may not be enough. You need to copy also the shared libraries it depends on (ldd /bin/sh helps.)

Toby222 commented 2 years ago

commands showing the binary is static source of the binary being served by the server

It's decidedly a statically linked file, sadly

omar-polo commented 2 years ago

gmid is quite strict when it comes to CGI scripts. It inspects the cgi reply for logging purposes but if it cannot read a valid reply it interrupts the cgi script and return 42.

The gemini reply header should be terminate with CRLF (i.e. \r\n) not by a \n alone. Since gmid can't find the \r\n sequence in the first 1024 character of the script output, it discards it and return a 42 CGI error, otherwise it would end up serving an invalid reply.

So, changing your example code to use \r\n should make it work.

(I should probably add some more logging for these cases so it's easier to understand why gmid is discarding the script output.)

Toby222 commented 2 years ago

Welp, that worked. I'm not sure why I got a file not found error from execvp if it ran correctly but the output was discarded by gmid. Unless I missed something when debugging and errno was set somewhere else internally.

(Yes some logging would probably help a long way ^^;)

omar-polo commented 2 years ago

Well, there were two different issues that caused the same return code: the first one being the shell (or some linked library) not being found and the other the malformed header. I'll add some logging for the "read malformed header" case, it affects the reverse proxy case too.

thanks!