From: Junio C Hamano Date: Mon, 24 Oct 2005 00:21:55 GMT Subject: [PATCH] Add git-shell. Message-ID: <7vhdb7vk64.fsf_-_@assigned-by-dhcp.cox.net> In-Reply-To: This adds a very git specific restricted shell, that can be added to /etc/shells and set to the pw_shell in the /etc/passwd file, to give users ability to push into repositories over ssh without giving them full interactive shell acount. [jc: I updated Linus' patch to match what the current sq_quote() does.] Signed-off-by: Junio C Hamano --- This one has the '!' dequoting I mentioned earlier, and actually has been tested twice. The system administrator should set things up so that the system default PATH environment variable lets users run the supported commands. We currently rely on the user to have a full shell access for repository administrative actions (e.g. git-init-db to create a repository, git-repack, and hooks management). Probably some of them may need to become accessible from git-shell, but I do not know which ones offhand: - Creating a repository. Probably wherever the user has write privilege, or maybe only under the home directory -- the policy would be up to the system administrator, with usual filesystem quota applied. - I think setting up hooks should be forbidden -- the user can execute arbitrary commands if we allowed it. Instead, the administrator can set things up for people, and would probably make creative use of hooks/update to implement access control (e.g. forbidding non-fast-forward pushes). - Similarly, packing can be left to hooks/post-update or cron job, either of which is under administrator control. Makefile | 2 +- quote.c | 41 ++++++++++++++++++++++++++++++++++++++++- quote.h | 6 ++++++ shell.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 106 insertions(+), 2 deletions(-) create mode 100644 shell.c applies-to: c043f0993afe5c057409ef748ce313c89c20c7dc f176536e2cd5949fa134d8b24dc07dd1f0744b6f diff --git a/Makefile b/Makefile index 5bdf3cc..5b0306d 100644 --- a/Makefile +++ b/Makefile @@ -116,7 +116,7 @@ PROGRAMS = \ git-merge-index$X git-mktag$X git-pack-objects$X git-patch-id$X \ git-peek-remote$X git-prune-packed$X git-read-tree$X \ git-receive-pack$X git-rev-list$X git-rev-parse$X \ - git-send-pack$X git-show-branch$X \ + git-send-pack$X git-show-branch$X git-shell$X \ git-show-index$X git-ssh-fetch$X \ git-ssh-upload$X git-tar-tree$X git-unpack-file$X \ git-unpack-objects$X git-update-index$X git-update-server-info$X \ diff --git a/quote.c b/quote.c index 009e694..e662a7d 100644 --- a/quote.c +++ b/quote.c @@ -15,6 +15,11 @@ #undef EMIT #define EMIT(x) ( (++len < n) && (*bp++ = (x)) ) +static inline int need_bs_quote(char c) +{ + return (c == '\'' || c == '!'); +} + size_t sq_quote_buf(char *dst, size_t n, const char *src) { char c; @@ -23,7 +28,7 @@ size_t sq_quote_buf(char *dst, size_t n, EMIT('\''); while ((c = *src++)) { - if (c == '\'' || c == '!') { + if (need_bs_quote(c)) { EMIT('\''); EMIT('\\'); EMIT(c); @@ -52,6 +57,40 @@ char *sq_quote(const char *src) return buf; } +char *sq_dequote(char *arg) +{ + char *dst = arg; + char *src = arg; + char c; + + if (*src != '\'') + return NULL; + for (;;) { + c = *++src; + if (!c) + return NULL; + if (c != '\'') { + *dst++ = c; + continue; + } + /* We stepped out of sq */ + switch (*++src) { + case '\0': + *dst = 0; + return arg; + case '\\': + c = *++src; + if (need_bs_quote(c) && *++src == '\'') { + *dst++ = c; + continue; + } + /* Fallthrough */ + default: + return NULL; + } + } +} + /* * C-style name quoting. * diff --git a/quote.h b/quote.h index 2fdde3b..2486e6e 100644 --- a/quote.h +++ b/quote.h @@ -31,6 +31,12 @@ extern char *sq_quote(const char *src); extern size_t sq_quote_buf(char *dst, size_t n, const char *src); +/* This unwraps what sq_quote() produces in place, but returns + * NULL if the input does not look like what sq_quote would have + * produced. + */ +extern char *sq_dequote(char *); + extern int quote_c_style(const char *name, char *outbuf, FILE *outfp, int nodq); extern char *unquote_c_style(const char *quoted, const char **endp); diff --git a/shell.c b/shell.c new file mode 100644 index 0000000..2c4789e --- /dev/null +++ b/shell.c @@ -0,0 +1,59 @@ +#include "cache.h" +#include "quote.h" + +static int do_generic_cmd(const char *me, char *arg) +{ + const char *my_argv[4]; + + arg = sq_dequote(arg); + if (!arg) + die("bad argument"); + + my_argv[0] = me; + my_argv[1] = arg; + my_argv[2] = NULL; + + return execvp(me, (char**) my_argv); +} + +static struct commands { + const char *name; + int (*exec)(const char *me, char *arg); +} cmd_list[] = { + { "git-receive-pack", do_generic_cmd }, + { "git-upload-pack", do_generic_cmd }, + { NULL }, +}; + +int main(int argc, char **argv) +{ + char *prog; + struct commands *cmd; + + /* We want to see "-c cmd args", and nothing else */ + if (argc != 3 || strcmp(argv[1], "-c")) + die("What do you think I am? A shell?"); + + prog = argv[2]; + argv += 2; + argc -= 2; + for (cmd = cmd_list ; cmd->name ; cmd++) { + int len = strlen(cmd->name); + char *arg; + if (strncmp(cmd->name, prog, len)) + continue; + arg = NULL; + switch (prog[len]) { + case '\0': + arg = NULL; + break; + case ' ': + arg = prog + len + 1; + break; + default: + continue; + } + exit(cmd->exec(cmd->name, arg)); + } + die("unrecognized command '%s'", prog); +} --- 0.99.8.GIT