Add git-shell.
authorLinus Torvalds <torvalds@osdl.org>
Sun, 23 Oct 2005 21:30:45 +0000 (14:30 -0700)
committerJunio C Hamano <junkio@cox.net>
Mon, 24 Oct 2005 22:12:41 +0000 (15:12 -0700)
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: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
Makefile
quote.c
quote.h
shell.c [new file with mode: 0644]

index 5bdf3cc..5b0306d 100644 (file)
--- 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 (file)
--- a/quote.c
+++ b/quote.c
 #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, const char *src)
 
        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 (file)
--- a/quote.h
+++ b/quote.h
 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 (file)
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);
+}