]> oss.titaniummirror.com Git - repo_shell.git/blobdiff - repo_shell.c
Misc cleanups
[repo_shell.git] / repo_shell.c
index 35edbe7c790f9cf1ff864a2a7250699464f22c47..ebcb246dbb315422655ab6c60cfac035e20358ff 100644 (file)
@@ -8,8 +8,11 @@
 #include <pwd.h>
 #include <string.h>
 #include "ini.h"
+#include "version.h"
 
 #define CFG_FILE "/etc/repo_shell.cfg"
+#define GIT_ACL_FILE "git_acl.cfg"
+#define SHELL "/bin/bash"
 
 typedef struct {
        char *svn_root;
@@ -28,27 +31,7 @@ static cfg_t cfg {
 static cfg_t cfg;
 #endif
 
-#define alloc_nr(x) (((x)+16)*3/2)
-
-/*
- * Realloc the buffer pointed at by variable 'x' so that it can hold
- * at least 'nr' entries; the number of entries currently allocated
- * is 'alloc', using the standard growing factor alloc_nr() macro.
- *
- * DO NOT USE any expression with side-effect for 'x', 'nr', or 'alloc'.
- */
-#define ALLOC_GROW(x, nr, alloc) \
-       do { \
-               if ((nr) > alloc) { \
-                       if (alloc_nr(alloc) < (nr)) \
-                               alloc = (nr); \
-                       else \
-                               alloc = alloc_nr(alloc); \
-                       x = xrealloc((x), alloc * sizeof(*(x))); \
-               } \
-       } while (0)
-
-static inline void die(const char *fmt, ...)
+static void die(const char *fmt, ...)
 {
        va_list ap;
 
@@ -80,18 +63,6 @@ void *xmalloc(size_t size)
        return ret;
 }
 
-void *xrealloc(void *ptr, size_t size)
-{
-       void *ret;
-
-       ret = realloc(ptr, size);
-       if (!ret && !size)
-               ret = realloc(ptr, 1);
-       if (!ret)
-               die("Out of memory, realloc failed");
-       return ret;
-}
-
 static uid_t user_uid(char *user)
 {
        struct passwd *pw = getpwnam(user);
@@ -124,17 +95,16 @@ static char *dequote(char *arg)
 
 static char *add_prefix(char *prefix, char* arg)
 {
-       int size;
-
-       if (arg && prefix && strlen(prefix)) {
-               char *n = xmalloc(sizeof(char *) *
-                       (strlen(prefix) + strlen(arg) + 2));
-               strcpy(n, prefix);
-               strcat(n, "/");
-               strcat(n, arg);
-               arg = n;
+       char *narg = arg;
+        int i;
+
+       if (arg && prefix && (i = strlen(prefix))) {
+               narg = xmalloc(sizeof(char *) * (i + strlen(arg) + 2));
+               strcpy(narg, prefix);
+               strcpy(narg + i++, "/");
+               strcpy(narg + i, arg);
        }
-       return arg;
+       return narg;
 }
 
 static int check_ssh_interactive(uid_t uid)
@@ -146,17 +116,40 @@ static int check_ssh_interactive(uid_t uid)
        return 1; /* for now */
 }
 
-static int git_check_access(const char *cmd, const char *arg, const char *user)
+static int git_acl(const char *user, const char *repo)
 {
-       /* TODO: Read some configuration file which maps users and access
-        * to a boolean true/false value.
-        *
-        * The git command can support read and write.
-        * git-receive-pack is ok for readers and writers
-        * git-upload-pack is ok only for writers
-        * git-upload-archive is ok only for writers
+       /* TODO: Read GIT_ACL_FILE from cfg.owner's home directory.  Look for
+        * the access level afforded user for repo.  A return of 0 means no
+        * access, a return of 1 means read only, and a return of 2 means
+        * read/write.
         */
-       return 1; /* assume OK for now */
+#if 0
+       struct passwd *pw;
+       char *file;
+       int len = strlen(cfg.owner) + strlen(GIT_ACL_FILE) + 8;
+
+       pw = getpwnam(cfg.owner);
+       if (!pw)
+               die("owner %s has no passwd entry?", cfg.owner);
+       len = strlen(pw->pw_dir) + strlen(GIT_ACL_FILE) + 2;
+       file = xmalloc(sizeof(char) * len);
+       sprintf(file, "%s/%s", pw->pw_dir, GIT_ACL_FILE);
+       fprintf(stderr, "[someday check %s for git ACLs]\n", file);
+       free(file);
+#endif
+       return 2; /* assume read/write for now */
+}
+
+static int git_check_access(const char *cmd, const char *repo, const char *user)
+{
+       /* What access is required per the incoming command?
+        * 0=none, 1=read-only, 2=read-write
+        */
+       int rw = (!strcmp(cmd, "git-upload-pack") ||
+                       !strcmp(cmd, "git-upload-archive")) ? 2 : 1;
+
+       /* Return true (1) if the user permissions >= those required */
+       return (git_acl(user, repo) >= rw) ? 1 : 0;
 }
 
 static int do_git_cmd(const char *cmd, char *arg, char *user)
@@ -195,75 +188,11 @@ static int do_svnserve_cmd(const char *cmd, char *arg, char *user)
        return execvp(svnserve_argv[0], (char *const *) svnserve_argv);
 }
 
-#define SPLIT_CMDLINE_BAD_ENDING 1
-#define SPLIT_CMDLINE_UNCLOSED_QUOTE 2
-static const char *split_cmdline_errors[] = {
-       "cmdline ends with \\",
-       "unclosed quote"
-};
-
-int split_cmdline(char *cmdline, const char ***argv)
-{
-       int src, dst, count = 0, size = 16;
-       char quoted = 0;
-
-       *argv = xmalloc(sizeof(char *) * size);
-
-       /* split alias_string */
-       (*argv)[count++] = cmdline;
-       for (src = dst = 0; cmdline[src];) {
-               char c = cmdline[src];
-               if (!quoted && isspace(c)) {
-                       cmdline[dst++] = 0;
-                       while (cmdline[++src]
-                                       && isspace(cmdline[src]))
-                               ; /* skip */
-                       ALLOC_GROW(*argv, count+1, size);
-                       (*argv)[count++] = cmdline + dst;
-               } else if (!quoted && (c == '\'' || c == '"')) {
-                       quoted = c;
-                       src++;
-               } else if (c == quoted) {
-                       quoted = 0;
-                       src++;
-               } else {
-                       if (c == '\\' && quoted != '\'') {
-                               src++;
-                               c = cmdline[src];
-                               if (!c) {
-                                       free(*argv);
-                                       *argv = NULL;
-                                       return -SPLIT_CMDLINE_BAD_ENDING;
-                               }
-                       }
-                       cmdline[dst++] = c;
-                       src++;
-               }
-       }
-
-       cmdline[dst] = 0;
-
-       if (quoted) {
-               free(*argv);
-               *argv = NULL;
-               return -SPLIT_CMDLINE_UNCLOSED_QUOTE;
-       }
-
-       ALLOC_GROW(*argv, count+1, size);
-       (*argv)[count] = NULL;
-
-       return count;
-}
-
-const char *split_cmdline_strerror(int split_cmdline_errno) {
-       return split_cmdline_errors[-split_cmdline_errno-1];
-}
-
 static void cd_to_homedir(void)
 {
        const char *home = getenv("HOME");
        if (!home)
-               die("could not determine user's home directory; HOME is unset");
+               die("user variable HOME is unset");
        if (chdir(home) == -1)
                die("could not chdir to user's home directory");
 }
@@ -280,7 +209,7 @@ static struct commands {
 };
 
 static int handler(void* user, const char* section, const char* name,
-                   const char* value)
+               const char* value)
 {
        cfg_t* pconfig = (cfg_t*)user;
 
@@ -316,8 +245,17 @@ int main(int argc, char **argv)
                die("opening /dev/null failed");
        close (devnull_fd);
 
-       if (argc < 3)
-               die("invalid arguments");
+       if (argc == 2 && (!strcmp(argv[1], "-v") ||
+                       !strcmp(argv[1], "--version"))) {
+               fprintf(stderr, "%s\n", version);
+               return 0;
+       }
+
+       if (argc == 1 && check_ssh_interactive(getuid())) {
+               setuid(getuid());
+               argv[0] = SHELL;
+               execvp(argv[0], (char *const *) argv);
+       }
 
 #ifdef USE_DEFAULTS
        ini_parse("repo_shell.cfg", handler, &cfg);
@@ -356,15 +294,8 @@ int main(int argc, char **argv)
        if (!check_ssh_interactive(getuid()))
                die("only repository access is allowed");
 
+       setuid(getuid());
        cd_to_homedir();
-       count = split_cmdline(prog, &user_argv);
-       if (count >= 0) {
-               execvp(user_argv[0], (char *const *) user_argv);
-               free(user_argv);
-               die("unrecognized command '%s'", argv[2]);
-       } else {
-               free(prog);
-               die("invalid command format '%s': %s", argv[2],
-                   split_cmdline_strerror(count));
-       }
+       argv[0] = SHELL;
+       execvp(argv[0], (char *const *) argv);
 }