From 38a195c6e62dfebed3c0c642ec9e3fcc89516097 Mon Sep 17 00:00:00 2001 From: tjpcc Date: Tue, 10 Oct 2023 14:49:08 -0600 Subject: "cmd" modifier to override CGI executable fixes #6 --- example.conf | 9 ++++++++- finger.go | 7 ++++++- gemini.go | 4 ++-- go.mod | 2 +- go.sum | 4 ++-- gopher.go | 4 ++-- parse.go | 19 +++++++++++++++++++ types.go | 1 + 8 files changed, 41 insertions(+), 9 deletions(-) diff --git a/example.conf b/example.conf index 6181fcf..18f58f7 100644 --- a/example.conf +++ b/example.conf @@ -86,7 +86,14 @@ gemini 0.0.0.0:1965 { # "static" and "cgi" directives work much like in gopher servers. # There is no "extendedgophermap" modifier in gemini, however. static /var/gemini/docs at / with dirdefault index.gmi, dirlist, exec - cgi /var/gemini/cgi at /cgi-bin + + # The "cmd" directive modifier can apply to any "cgi" or "static ... with exec" directives. + # It specifies a filesystem path to an executable file, which will be executed in place of the CGI script. + # Even with "cmd" in use, it will only be invoked if the request path points to a world-executable file + # and with the working directory and environment variables of the requested script. + # So generally "cmd" scripts can end with something like "exec $SCRIPT_PATH", perhaps after imposing + # resource limits or some other security sandbox around it first. + cgi /var/gemini/cgi at /cgi-bin with cmd /var/runcgi # The "autoatom" modifier is allowed on directives in a gemini server. # It causes any text/gemini responses to be available as atom at .atom. diff --git a/finger.go b/finger.go index 941e705..75d8160 100644 --- a/finger.go +++ b/finger.go @@ -61,7 +61,12 @@ func fingerHandler(route RouteDirective) sr.Handler { } if st.Mode()&5 == 5 && (route.Modifiers.Exec || route.Type == "cgi") { - buf, code, err := cgi.RunCGI(ctx, request, fpath, "/", nil) + workdir := filepath.Dir(fpath) + if route.Modifiers.ExecCmd != "" { + fpath = route.Modifiers.ExecCmd + } + + buf, code, err := cgi.RunCGI(ctx, request, fpath, "/", workdir, nil) if err != nil { return finger.Error("execution error") } diff --git a/gemini.go b/gemini.go index 3fb7a46..87af01f 100644 --- a/gemini.go +++ b/gemini.go @@ -94,7 +94,7 @@ func addGeminiRoute(router *sr.Router, route RouteDirective) { addGeminiStaticRoute(router, route) case "cgi": buildAndAddRoute(router, route, func(route RouteDirective) sr.Handler { - handler := cgi.GeminiCGIDirectory(route.FsPath, route.URLPath) + handler := cgi.GeminiCGIDirectory(route.FsPath, route.URLPath, route.Modifiers.ExecCmd) if route.Modifiers.AutoAtom { handler = atomconv.Auto(handler) } @@ -110,7 +110,7 @@ func addGeminiStaticRoute(router *sr.Router, route RouteDirective) { handlers := []sr.Handler{} if route.Modifiers.Exec { - handlers = append(handlers, cgi.GeminiCGIDirectory(route.FsPath, route.URLPath)) + handlers = append(handlers, cgi.GeminiCGIDirectory(route.FsPath, route.URLPath, route.Modifiers.ExecCmd)) } handlers = append(handlers, fs.GeminiFileHandler(route.FsPath, route.URLPath)) diff --git a/go.mod b/go.mod index 09affeb..6c67c53 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.21.0 require ( github.com/go-kit/log v0.2.1 - tildegit.org/tjp/sliderule v1.4.1 + tildegit.org/tjp/sliderule v1.4.2-0.20231010204754-04449ed66e42 tildegit.org/tjp/syw v0.9.2 ) diff --git a/go.sum b/go.sum index 7c0c074..d0231f4 100644 --- a/go.sum +++ b/go.sum @@ -10,7 +10,7 @@ github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKs github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -tildegit.org/tjp/sliderule v1.4.1 h1:L1evSiVqhiHSVgWBCFgDXhT30OZdWUHRq8CahCDkd+I= -tildegit.org/tjp/sliderule v1.4.1/go.mod h1:opdo8E25iS9X9pNismM8U7pCH8XO0PdRIIhdADn8Uik= +tildegit.org/tjp/sliderule v1.4.2-0.20231010204754-04449ed66e42 h1:KkbiQO/kIxwABGqhocShHvehchDuGyQzvhV61SKfhO8= +tildegit.org/tjp/sliderule v1.4.2-0.20231010204754-04449ed66e42/go.mod h1:opdo8E25iS9X9pNismM8U7pCH8XO0PdRIIhdADn8Uik= tildegit.org/tjp/syw v0.9.2 h1:bwLRXJqC5RHes2dfntgePHYnh6iIQC9FZpFBTReemQ0= tildegit.org/tjp/syw v0.9.2/go.mod h1:Oo05KA7QibiXxoPh5jzpKUq/RG4U3nz7qs6QVitZw8I= diff --git a/gopher.go b/gopher.go index 3030bac..1c13886 100644 --- a/gopher.go +++ b/gopher.go @@ -57,7 +57,7 @@ func addGopherStaticRoute(router *sr.Router, route RouteDirective) { handlers := []sr.Handler{} if route.Modifiers.Exec { - handlers = append(handlers, cgi.ExecGopherMaps(route.FsPath, route.URLPath, settings)) + handlers = append(handlers, cgi.ExecGopherMaps(route.FsPath, route.URLPath, route.Modifiers.ExecCmd, settings)) } handlers = append(handlers, fs.GopherFileHandler(route.FsPath, route.URLPath, settings)) @@ -90,7 +90,7 @@ func addGopherCGIRoute(router *sr.Router, route RouteDirective) { } buildAndAddRoute(router, route, func(route RouteDirective) sr.Handler { - return cgi.GopherCGIDirectory(route.FsPath, route.URLPath, settings) + return cgi.GopherCGIDirectory(route.FsPath, route.URLPath, route.Modifiers.ExecCmd, settings) }) } diff --git a/parse.go b/parse.go index 0fe3f30..d087e95 100644 --- a/parse.go +++ b/parse.go @@ -293,6 +293,10 @@ func validateRoute(serverType string, dir *RouteDirective) error { return fmt.Errorf("titan modifier only allowed on gemini{static}") } + if dir.Modifiers.ExecCmd != "" && !(dir.Type == "cgi" || (dir.Type == "static" && dir.Modifiers.Exec)) { + return fmt.Errorf("'cmd' modifier only valid on 'cgi' and 'static...with exec' directives") + } + return nil } @@ -410,6 +414,21 @@ func parseModifiers(text string) (Modifiers, string, error) { mod.DirList = true case "exec": mod.Exec = true + case "cmd": + if sep != " " { + return mod, "", errors.New("invalid 'cmd' clause") + } + text = strings.TrimLeft(text, " \t") + idx = strings.IndexAny(text, " \t,") + if idx == 0 { + return mod, "", errors.New("invalid 'cmd' clause") + } else if idx < 0 { + mod.ExecCmd = text + text = "" + } else { + mod.ExecCmd = text[0:idx] + text = text[idx+1:] + } case "extendedgophermap": mod.ExtendedGophermap = true case "autoatom": diff --git a/types.go b/types.go index 8d010b2..3dd3935 100644 --- a/types.go +++ b/types.go @@ -14,6 +14,7 @@ type Modifiers struct { DirDefault string DirList bool Exec bool + ExecCmd string ExtendedGophermap bool AutoAtom bool Titan *Auth -- cgit v1.2.3