From: Stefan Sperling Subject: Re: dial fix for git-shell To: Omar Polo , gameoftrees@openbsd.org Date: Wed, 8 Mar 2023 15:21:42 +0100 On Sun, Feb 19, 2023 at 10:18:40AM +0100, Stefan Sperling wrote: > On Sat, Feb 18, 2023 at 05:15:38PM +0100, Omar Polo wrote: > > since we're apparently in the quoting business I guess we may need > > something more robust that handles paths with ' in them. > > > > Maybe this is the reason git-shell is so picky. > > > > could be even done as a separate step, fwiw ok op@ if you want to just > > commit this. > > I will think some more about this. I really don't like having to deal > with quoting issues at this layer. I suspect we really cannot avoid doing some escaping. This patch uses the same escaping rules as used by OpenBSD's locale(1). ok? diff cee3836880940ba0d1203e0682a43a680132bc27 351ffb4d09aa704fe94d74437802d0b5c0812f8f commit - cee3836880940ba0d1203e0682a43a680132bc27 commit + 351ffb4d09aa704fe94d74437802d0b5c0812f8f blob - 3325c8994f55721d8588155cdc72b95d11fd2248 blob + e0687bfb44ce75c1677a5a42f7457e8dd31caaed --- lib/dial.c +++ lib/dial.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include "got_error.h" @@ -200,6 +201,55 @@ const struct got_error * return err; } +/* + * Escape a given path for the shell which will be started by sshd. + * In particular, git-shell is known to require single-quote characters + * around its repository path argument and will refuse to run otherwise. + */ +static const struct got_error * +escape_path(char *buf, size_t bufsize, const char *path) +{ + size_t i = 0; + const char *p; + + /* We will need at least this much space: */ + if (bufsize < strlen(path) + 3) + goto toolong; + + memset(buf, 0, bufsize); + buf[i++] = '\''; /* git-shell compat */ + + p = path; + while (*p != '\0') { + switch (*p) { + case ' ': case '\t': case '\n': case '\'': + case '(': case ')': case '<': case '>': + case '&': case ';': case '|': case '~': + case '"': case '\\': case '$': case '`': + if (i >= bufsize - 1) + goto toolong; + buf[i++] = '\\'; + /* FALLTHROUGH */ + default: + if (i >= bufsize - 1) + goto toolong; + buf[i++] = *p; + break; + } + p++; + } + + if (i >= bufsize - 1) + goto toolong; + buf[i++] = '\''; + if (i >= bufsize) + goto toolong; + buf[i] = '\0'; + return NULL; +toolong: + return got_error_fmt(GOT_ERR_NO_SPACE, "overlong path: %s", path); +} + const struct got_error * got_dial_ssh(pid_t *newpid, int *newfd, const char *host, const char *port, const char *path, const char *direction, int verbosity) @@ -209,10 +259,15 @@ got_dial_ssh(pid_t *newpid, int *newfd, const char *ho char cmd[64]; const char *argv[11]; int i = 0, j; + char escaped_path[PATH_MAX]; *newpid = -1; *newfd = -1; + error = escape_path(escaped_path, sizeof(escaped_path), path); + if (error) + return error; + argv[i++] = GOT_DIAL_PATH_SSH; if (port != NULL) { argv[i++] = "-p"; @@ -228,7 +283,7 @@ got_dial_ssh(pid_t *newpid, int *newfd, const char *ho argv[i++] = "--"; argv[i++] = (char *)host; argv[i++] = (char *)cmd; - argv[i++] = (char *)path; + argv[i++] = (char *)escaped_path; argv[i++] = NULL; assert(i <= nitems(argv));