X-Git-Url: https://oss.titaniummirror.com/gitweb/?a=blobdiff_plain;f=repo_shell.c;h=7fb31d4c9f6340efff9bfde16c2979deb0a25b49;hb=4a9eda2a8f7eaabce4010c2b53cd86e4cde72b09;hp=35edbe7c790f9cf1ff864a2a7250699464f22c47;hpb=bd8087cf1e4569fdbd4f09fa2bbb01c4c072e007;p=repo_shell.git diff --git a/repo_shell.c b/repo_shell.c index 35edbe7..7fb31d4 100644 --- a/repo_shell.c +++ b/repo_shell.c @@ -8,8 +8,11 @@ #include #include #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); @@ -146,17 +117,41 @@ 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) +{ + int rw = 1; /* 0=no access, 1=read only, 2=read/write */ + + /* What access is required per the incoming command? */ + if (!strcmp(cmd, "git-upload-pack") || + !strcmp(cmd, "git-upload-archive")) + rw = 2; + + /* 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,70 +190,6 @@ 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"); @@ -280,7 +211,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 +247,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 +296,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); }