X-Git-Url: https://oss.titaniummirror.com/gitweb?a=blobdiff_plain;f=repo-shell.c;fp=repo-shell.c;h=0000000000000000000000000000000000000000;hb=bd8087cf1e4569fdbd4f09fa2bbb01c4c072e007;hp=8399925729f6d1646dfabcfeb27113918205fca3;hpb=9f92ff54a7119d649dfb1e3110ea35f63fa00967;p=repo_shell.git diff --git a/repo-shell.c b/repo-shell.c deleted file mode 100644 index 8399925..0000000 --- a/repo-shell.c +++ /dev/null @@ -1,332 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* TODO: these should come from a config file */ -static char *svn_repo_root = "/var/lib/svn/"; -static char *git_repo_root = "/var/lib/svn/"; -//static char *repo_user = "repo"; -static char *repo_user = "smckown"; - -#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, ...) -{ - va_list ap; - - va_start(ap, fmt); - fprintf(stderr, "error: "); - vfprintf(stderr, fmt, ap); - fprintf(stderr, "\n" ); - va_end(ap); - exit(1); -} - -char *xstrdup(const char *str) -{ - char *ret = strdup(str); - if (!ret) - die("out of memory"); - return ret; -} - -void *xmalloc(size_t size) -{ - void *ret; - - ret = malloc(size); - if (!ret && !size) - ret = malloc(1); - if (!ret) - die("out of memory"); - 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); - - if (!pw) - die("invalid user %s", user); - return pw->pw_uid; -} - -static void change_user(char *user) -{ - /* This is the function for which setuid is required, as root */ - setuid(user_uid(user)); -} - -static char *dequote(char *arg) -{ - char* narg = NULL; - - if (arg && *arg == '\'') { - char* end = arg + strlen(arg) - 1; - - if (end != arg && *end == '\'') { - narg = arg + 1; - *end = '\0'; - } - } - return narg; -} - -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; - } - return arg; -} - -static int check_ssh_interactive(uid_t uid) -{ - /* TODO: Check the config file for the user owning uid to see if that - * user should be able to execute any commands other than those required - * to support repository access. Return a boolean true/false. - */ - return 1; /* for now */ -} - -static int git_check_access(const char *cmd, const char *arg, const char *user) -{ - /* 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 - */ - return 1; /* assume OK for now */ -} - -static int do_git_cmd(const char *cmd, char *arg, char *user) -{ - const char *nargv[4]; - char* narg; - int ret; - - if (!(arg = dequote(arg))) - die("bad argument"); - if (strncmp(cmd, "git-", 4)) - die("bad command"); - - change_user(repo_user); - if (!git_check_access(cmd, arg, user)) - die("permission denied"); - - nargv[0] = cmd; - nargv[1] = add_prefix(git_repo_root, arg); - nargv[2] = NULL; - - ret = execvp(nargv[0], (char *const *) nargv); - /* Code unreached if execv successful */ - free(narg); - return ret; -} - -static int do_svnserve_cmd(const char *cmd, char *arg, char *user) -{ - const char *svnserve_argv[7] = { - cmd, "-t", "--root", svn_repo_root, "--tunnel-user", user, NULL - }; - int ret; - - change_user(repo_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"); - if (chdir(home) == -1) - die("could not chdir to user's home directory"); -} - -static struct commands { - const char *name; - int (*exec)(const char *cmd, char *arg, char *user); -} cmd_list[] = { - { "git-receive-pack", do_git_cmd }, - { "git-upload-pack", do_git_cmd }, - { "git-upload-archive", do_git_cmd }, - { "svnserve", do_svnserve_cmd }, - { NULL }, -}; - -int main(int argc, char **argv) -{ - char *prog; - const char **user_argv; - struct commands *cmd; - int devnull_fd; - int count; - - /* - * Always open file descriptors 0/1/2 to avoid clobbering files - * in die(). It also avoids not messing up when the pipes are - * dup'ed onto stdin/stdout/stderr in the child processes we spawn. - */ - devnull_fd = open("/dev/null", O_RDWR); - while (devnull_fd >= 0 && devnull_fd <= 2) - devnull_fd = dup(devnull_fd); - if (devnull_fd == -1) - die("opening /dev/null failed"); - close (devnull_fd); - - if (argc < 3) - die("invalid arguments"); - fprintf(stderr, "prog |%s|\n", argv[2]); - prog = xstrdup(argv[2]); - if (!strncmp(prog, "git", 3) && isspace(prog[3])) - /* Accept "git foo" as if the caller said "git-foo". */ - prog[3] = '-'; - - for (cmd = cmd_list ; cmd->name ; cmd++) { - int len = strlen(cmd->name); - char *arg; - struct passwd *pw; - 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; - } - - pw = getpwuid(getuid()); - exit(cmd->exec(cmd->name, arg, pw->pw_name)); - } - - if (!check_ssh_interactive(getuid())) - die("only repository access is allowed"); - - 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)); - } -}